Unnamed repository; edit this file 'description' to name the repository.
Merge ref '68ffae46b581' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh. Upstream ref: rust-lang/rust@68ffae46b581747074413e4242ab0c6ecc4db4a9 Filtered ref: rust-lang/compiler-builtins@76029397ce81cf74ceeb79b22f65af86ed35256d Upstream diff: https://github.com/rust-lang/rust/compare/d0442e2800d356ae282ddcdbe0eff8798fe648b6...68ffae46b581747074413e4242ab0c6ecc4db4a9 This merge was created using https://github.com/rust-lang/josh-sync.
The rustc-josh-sync Cronjob Bot 3 months ago
parent b590d30 · parent 532482c · commit 292271d
-rw-r--r--.github/workflows/gen-lints.yml44
-rw-r--r--.github/workflows/release.yaml2
-rw-r--r--.github/workflows/rustc-pull.yml1
-rw-r--r--.github/workflows/rustdoc.yaml3
-rw-r--r--Cargo.lock120
-rw-r--r--Cargo.toml31
-rw-r--r--bench_data/glorious_old_parser1
-rw-r--r--crates/base-db/src/change.rs5
-rw-r--r--crates/base-db/src/editioned_file_id.rs57
-rw-r--r--crates/base-db/src/input.rs43
-rw-r--r--crates/base-db/src/lib.rs108
-rw-r--r--crates/cfg/Cargo.toml3
-rw-r--r--crates/cfg/src/cfg_expr.rs111
-rw-r--r--crates/cfg/src/tests.rs49
-rw-r--r--crates/hir-def/src/attrs.rs865
-rw-r--r--crates/hir-def/src/attrs/docs.rs736
-rw-r--r--crates/hir-def/src/db.rs4
-rw-r--r--crates/hir-def/src/dyn_map.rs2
-rw-r--r--crates/hir-def/src/expr_store.rs27
-rw-r--r--crates/hir-def/src/expr_store/body.rs4
-rw-r--r--crates/hir-def/src/expr_store/lower.rs177
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs61
-rw-r--r--crates/hir-def/src/expr_store/scope.rs7
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs33
-rw-r--r--crates/hir-def/src/expr_store/tests/body/block.rs6
-rw-r--r--crates/hir-def/src/hir.rs36
-rw-r--r--crates/hir-def/src/import_map.rs6
-rw-r--r--crates/hir-def/src/item_scope.rs46
-rw-r--r--crates/hir-def/src/item_tree/attrs.rs108
-rw-r--r--crates/hir-def/src/lang_item.rs10
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs18
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe/regression.rs10
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mod.rs3
-rw-r--r--crates/hir-def/src/macro_expansion_tests/proc_macros.rs4
-rw-r--r--crates/hir-def/src/nameres.rs2
-rw-r--r--crates/hir-def/src/nameres/collector.rs11
-rw-r--r--crates/hir-def/src/nameres/tests.rs1
-rw-r--r--crates/hir-def/src/nameres/tests/incremental.rs60
-rw-r--r--crates/hir-def/src/nameres/tests/macros.rs5
-rw-r--r--crates/hir-def/src/test_db.rs13
-rw-r--r--crates/hir-expand/Cargo.toml1
-rw-r--r--crates/hir-expand/src/attrs.rs400
-rw-r--r--crates/hir-expand/src/builtin/derive_macro.rs81
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs2
-rw-r--r--crates/hir-expand/src/cfg_process.rs59
-rw-r--r--crates/hir-expand/src/db.rs60
-rw-r--r--crates/hir-expand/src/declarative.rs13
-rw-r--r--crates/hir-expand/src/files.rs8
-rw-r--r--crates/hir-expand/src/lib.rs11
-rw-r--r--crates/hir-expand/src/span_map.rs2
-rw-r--r--crates/hir-ty/Cargo.toml2
-rw-r--r--crates/hir-ty/src/builtin_derive.rs37
-rw-r--r--crates/hir-ty/src/consteval.rs251
-rw-r--r--crates/hir-ty/src/consteval/tests.rs53
-rw-r--r--crates/hir-ty/src/db.rs96
-rw-r--r--crates/hir-ty/src/diagnostics/expr.rs10
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/pat_util.rs6
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--crates/hir-ty/src/display.rs221
-rw-r--r--crates/hir-ty/src/drop.rs44
-rw-r--r--crates/hir-ty/src/dyn_compatibility.rs67
-rw-r--r--crates/hir-ty/src/infer.rs490
-rw-r--r--crates/hir-ty/src/infer/autoderef.rs2
-rw-r--r--crates/hir-ty/src/infer/callee.rs543
-rw-r--r--crates/hir-ty/src/infer/closure.rs287
-rw-r--r--crates/hir-ty/src/infer/closure/analysis.rs2648
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs1705
-rw-r--r--crates/hir-ty/src/infer/coerce.rs266
-rw-r--r--crates/hir-ty/src/infer/expr.rs366
-rw-r--r--crates/hir-ty/src/infer/mutability.rs3
-rw-r--r--crates/hir-ty/src/infer/path.rs2
-rw-r--r--crates/hir-ty/src/infer/unify.rs103
-rw-r--r--crates/hir-ty/src/layout.rs29
-rw-r--r--crates/hir-ty/src/layout/target.rs31
-rw-r--r--crates/hir-ty/src/layout/tests/closure.rs10
-rw-r--r--crates/hir-ty/src/lib.rs53
-rw-r--r--crates/hir-ty/src/lower.rs125
-rw-r--r--crates/hir-ty/src/lower/path.rs6
-rw-r--r--crates/hir-ty/src/method_resolution.rs3
-rw-r--r--crates/hir-ty/src/method_resolution/confirm.rs4
-rw-r--r--crates/hir-ty/src/method_resolution/probe.rs2
-rw-r--r--crates/hir-ty/src/mir.rs62
-rw-r--r--crates/hir-ty/src/mir/borrowck.rs141
-rw-r--r--crates/hir-ty/src/mir/eval.rs243
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs37
-rw-r--r--crates/hir-ty/src/mir/lower.rs315
-rw-r--r--crates/hir-ty/src/mir/lower/as_place.rs13
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs17
-rw-r--r--crates/hir-ty/src/mir/pretty.rs3
-rw-r--r--crates/hir-ty/src/next_solver.rs30
-rw-r--r--crates/hir-ty/src/next_solver/allocation.rs73
-rw-r--r--crates/hir-ty/src/next_solver/consts.rs219
-rw-r--r--crates/hir-ty/src/next_solver/consts/valtree.rs712
-rw-r--r--crates/hir-ty/src/next_solver/def_id.rs13
-rw-r--r--crates/hir-ty/src/next_solver/fold.rs26
-rw-r--r--crates/hir-ty/src/next_solver/fulfill.rs2
-rw-r--r--crates/hir-ty/src/next_solver/fulfill/errors.rs1173
-rw-r--r--crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs38
-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.rs18
-rw-r--r--crates/hir-ty/src/next_solver/infer/mod.rs32
-rw-r--r--crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs4
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/generalize.rs2
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs23
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/lattice.rs4
-rw-r--r--crates/hir-ty/src/next_solver/inspect.rs15
-rw-r--r--crates/hir-ty/src/next_solver/interner.rs446
-rw-r--r--crates/hir-ty/src/next_solver/ir_print.rs2
-rw-r--r--crates/hir-ty/src/next_solver/normalize.rs1
-rw-r--r--crates/hir-ty/src/next_solver/region.rs136
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs45
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs128
-rw-r--r--crates/hir-ty/src/next_solver/util.rs21
-rw-r--r--crates/hir-ty/src/opaques.rs16
-rw-r--r--crates/hir-ty/src/specialization.rs2
-rw-r--r--crates/hir-ty/src/test_db.rs10
-rw-r--r--crates/hir-ty/src/tests/closure_captures.rs247
-rw-r--r--crates/hir-ty/src/tests/coercion.rs6
-rw-r--r--crates/hir-ty/src/tests/incremental.rs51
-rw-r--r--crates/hir-ty/src/tests/method_resolution.rs4
-rw-r--r--crates/hir-ty/src/tests/patterns.rs1
-rw-r--r--crates/hir-ty/src/tests/regression.rs72
-rw-r--r--crates/hir-ty/src/tests/regression/new_solver.rs4
-rw-r--r--crates/hir-ty/src/tests/traits.rs114
-rw-r--r--crates/hir-ty/src/upvars.rs129
-rw-r--r--crates/hir-ty/src/utils.rs53
-rw-r--r--crates/hir-ty/src/variance.rs2
-rw-r--r--crates/hir/Cargo.toml1
-rw-r--r--crates/hir/src/attrs.rs19
-rw-r--r--crates/hir/src/diagnostics.rs65
-rw-r--r--crates/hir/src/display.rs4
-rw-r--r--crates/hir/src/has_source.rs2
-rw-r--r--crates/hir/src/lib.rs375
-rw-r--r--crates/hir/src/semantics.rs63
-rw-r--r--crates/hir/src/semantics/child_by_source.rs3
-rw-r--r--crates/hir/src/semantics/source_to_def.rs8
-rw-r--r--crates/hir/src/source_analyzer.rs12
-rw-r--r--crates/ide-assists/src/handlers/add_braces.rs8
-rw-r--r--crates/ide-assists/src/handlers/add_explicit_dot_deref.rs10
-rw-r--r--crates/ide-assists/src/handlers/add_label_to_loop.rs13
-rw-r--r--crates/ide-assists/src/handlers/add_missing_impl_members.rs23
-rw-r--r--crates/ide-assists/src/handlers/add_missing_match_arms.rs134
-rw-r--r--crates/ide-assists/src/handlers/add_turbo_fish.rs12
-rw-r--r--crates/ide-assists/src/handlers/apply_demorgan.rs35
-rw-r--r--crates/ide-assists/src/handlers/auto_import.rs173
-rw-r--r--crates/ide-assists/src/handlers/bind_unused_param.rs4
-rw-r--r--crates/ide-assists/src/handlers/convert_bool_then.rs20
-rw-r--r--crates/ide-assists/src/handlers/convert_closure_to_fn.rs126
-rw-r--r--crates/ide-assists/src/handlers/convert_comment_block.rs15
-rw-r--r--crates/ide-assists/src/handlers/convert_for_to_while_let.rs16
-rw-r--r--crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs9
-rw-r--r--crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs54
-rw-r--r--crates/ide-assists/src/handlers/convert_let_else_to_match.rs242
-rw-r--r--crates/ide-assists/src/handlers/convert_match_to_let_else.rs17
-rw-r--r--crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs74
-rw-r--r--crates/ide-assists/src/handlers/convert_range_for_to_while.rs66
-rw-r--r--crates/ide-assists/src/handlers/convert_to_guarded_return.rs333
-rw-r--r--crates/ide-assists/src/handlers/convert_tuple_return_type_to_struct.rs79
-rw-r--r--crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs55
-rw-r--r--crates/ide-assists/src/handlers/convert_while_to_loop.rs19
-rw-r--r--crates/ide-assists/src/handlers/destructure_struct_binding.rs27
-rw-r--r--crates/ide-assists/src/handlers/destructure_tuple_binding.rs46
-rw-r--r--crates/ide-assists/src/handlers/desugar_try_expr.rs15
-rw-r--r--crates/ide-assists/src/handlers/expand_glob_import.rs14
-rw-r--r--crates/ide-assists/src/handlers/expand_rest_pattern.rs31
-rw-r--r--crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs9
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs187
-rw-r--r--crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs226
-rw-r--r--crates/ide-assists/src/handlers/extract_type_alias.rs66
-rw-r--r--crates/ide-assists/src/handlers/extract_variable.rs318
-rw-r--r--crates/ide-assists/src/handlers/fix_visibility.rs2
-rw-r--r--crates/ide-assists/src/handlers/flip_binexpr.rs23
-rw-r--r--crates/ide-assists/src/handlers/flip_comma.rs13
-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.rs67
-rw-r--r--crates/ide-assists/src/handlers/generate_default_from_new.rs6
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_methods.rs10
-rw-r--r--crates/ide-assists/src/handlers/generate_delegate_trait.rs12
-rw-r--r--crates/ide-assists/src/handlers/generate_deref.rs10
-rw-r--r--crates/ide-assists/src/handlers/generate_derive.rs16
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_is_method.rs78
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_projection_method.rs92
-rw-r--r--crates/ide-assists/src/handlers/generate_enum_variant.rs8
-rw-r--r--crates/ide-assists/src/handlers/generate_fn_type_alias.rs13
-rw-r--r--crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs7
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs8
-rw-r--r--crates/ide-assists/src/handlers/generate_getter_or_setter.rs51
-rw-r--r--crates/ide-assists/src/handlers/generate_impl.rs51
-rw-r--r--crates/ide-assists/src/handlers/generate_mut_trait_impl.rs36
-rw-r--r--crates/ide-assists/src/handlers/generate_new.rs35
-rw-r--r--crates/ide-assists/src/handlers/generate_single_field_struct_from.rs118
-rw-r--r--crates/ide-assists/src/handlers/generate_trait_from_impl.rs54
-rw-r--r--crates/ide-assists/src/handlers/inline_local_variable.rs18
-rw-r--r--crates/ide-assists/src/handlers/inline_type_alias.rs31
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_lifetime.rs58
-rw-r--r--crates/ide-assists/src/handlers/introduce_named_type_parameter.rs7
-rw-r--r--crates/ide-assists/src/handlers/merge_imports.rs14
-rw-r--r--crates/ide-assists/src/handlers/move_bounds.rs21
-rw-r--r--crates/ide-assists/src/handlers/move_const_to_impl.rs1
-rw-r--r--crates/ide-assists/src/handlers/move_guard.rs23
-rw-r--r--crates/ide-assists/src/handlers/promote_local_to_const.rs7
-rw-r--r--crates/ide-assists/src/handlers/pull_assignment_up.rs15
-rw-r--r--crates/ide-assists/src/handlers/qualify_method_call.rs15
-rw-r--r--crates/ide-assists/src/handlers/qualify_path.rs29
-rw-r--r--crates/ide-assists/src/handlers/raw_string.rs33
-rw-r--r--crates/ide-assists/src/handlers/remove_dbg.rs35
-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.rs11
-rw-r--r--crates/ide-assists/src/handlers/remove_unused_param.rs4
-rw-r--r--crates/ide-assists/src/handlers/reorder_fields.rs10
-rw-r--r--crates/ide-assists/src/handlers/reorder_impl_items.rs2
-rw-r--r--crates/ide-assists/src/handlers/replace_arith_op.rs12
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs54
-rw-r--r--crates/ide-assists/src/handlers/replace_if_let_with_match.rs92
-rw-r--r--crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs18
-rw-r--r--crates/ide-assists/src/handlers/replace_let_with_if_let.rs28
-rw-r--r--crates/ide-assists/src/handlers/replace_named_generic_with_impl.rs5
-rw-r--r--crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs11
-rw-r--r--crates/ide-assists/src/handlers/sort_items.rs2
-rw-r--r--crates/ide-assists/src/handlers/toggle_macro_delimiter.rs7
-rw-r--r--crates/ide-assists/src/handlers/unmerge_imports.rs18
-rw-r--r--crates/ide-assists/src/handlers/unmerge_match_arm.rs7
-rw-r--r--crates/ide-assists/src/handlers/unqualify_method_call.rs13
-rw-r--r--crates/ide-assists/src/handlers/unwrap_block.rs28
-rw-r--r--crates/ide-assists/src/handlers/unwrap_return_type.rs7
-rw-r--r--crates/ide-assists/src/handlers/unwrap_type_to_generic_arg.rs2
-rw-r--r--crates/ide-assists/src/handlers/wrap_return_type.rs16
-rw-r--r--crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs212
-rw-r--r--crates/ide-assists/src/tests.rs22
-rw-r--r--crates/ide-assists/src/tests/generated.rs4
-rw-r--r--crates/ide-assists/src/utils.rs247
-rw-r--r--crates/ide-assists/src/utils/gen_trait_fn_body.rs500
-rw-r--r--crates/ide-completion/src/completions.rs160
-rw-r--r--crates/ide-completion/src/completions/attribute.rs4
-rw-r--r--crates/ide-completion/src/completions/env_vars.rs50
-rw-r--r--crates/ide-completion/src/completions/expr.rs17
-rw-r--r--crates/ide-completion/src/completions/flyimport.rs14
-rw-r--r--crates/ide-completion/src/completions/postfix.rs75
-rw-r--r--crates/ide-completion/src/completions/postfix/format_like.rs7
-rw-r--r--crates/ide-completion/src/completions/ra_fixture.rs2
-rw-r--r--crates/ide-completion/src/completions/type.rs50
-rw-r--r--crates/ide-completion/src/config.rs5
-rw-r--r--crates/ide-completion/src/context.rs88
-rw-r--r--crates/ide-completion/src/context/analysis.rs64
-rw-r--r--crates/ide-completion/src/item.rs66
-rw-r--r--crates/ide-completion/src/lib.rs25
-rw-r--r--crates/ide-completion/src/render.rs611
-rw-r--r--crates/ide-completion/src/render/const_.rs2
-rw-r--r--crates/ide-completion/src/render/function.rs2
-rw-r--r--crates/ide-completion/src/render/literal.rs8
-rw-r--r--crates/ide-completion/src/render/macro_.rs2
-rw-r--r--crates/ide-completion/src/render/pattern.rs4
-rw-r--r--crates/ide-completion/src/render/type_alias.rs2
-rw-r--r--crates/ide-completion/src/render/union_literal.rs2
-rw-r--r--crates/ide-completion/src/tests.rs5
-rw-r--r--crates/ide-completion/src/tests/expression.rs110
-rw-r--r--crates/ide-completion/src/tests/flyimport.rs68
-rw-r--r--crates/ide-completion/src/tests/item.rs52
-rw-r--r--crates/ide-completion/src/tests/special.rs9
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs226
-rw-r--r--crates/ide-db/Cargo.toml1
-rw-r--r--crates/ide-db/src/generated/lints.rs6869
-rw-r--r--crates/ide-db/src/imports/import_assets.rs172
-rw-r--r--crates/ide-db/src/imports/insert_use.rs61
-rw-r--r--crates/ide-db/src/imports/merge_imports.rs10
-rw-r--r--crates/ide-db/src/lib.rs40
-rw-r--r--crates/ide-db/src/path_transform.rs79
-rw-r--r--crates/ide-db/src/prime_caches.rs11
-rw-r--r--crates/ide-db/src/ra_fixture.rs19
-rw-r--r--crates/ide-db/src/search.rs110
-rw-r--r--crates/ide-db/src/source_change.rs8
-rw-r--r--crates/ide-db/src/symbol_index.rs28
-rw-r--r--crates/ide-db/src/syntax_helpers/format_string_exprs.rs2
-rw-r--r--crates/ide-db/src/syntax_helpers/node_ext.rs7
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs14
-rw-r--r--crates/ide-db/src/test_data/test_doc_alias.txt30
-rw-r--r--crates/ide-db/src/test_data/test_symbol_index_collection.txt138
-rw-r--r--crates/ide-db/src/test_data/test_symbols_exclude_imports.txt4
-rw-r--r--crates/ide-db/src/test_data/test_symbols_with_imports.txt8
-rw-r--r--crates/ide-diagnostics/src/handlers/inactive_code.rs46
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs57
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/remove_trailing_return.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs112
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs187
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs14
-rw-r--r--crates/ide-diagnostics/src/lib.rs22
-rw-r--r--crates/ide-diagnostics/src/tests.rs5
-rw-r--r--crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs1
-rw-r--r--crates/ide-ssr/src/from_comment.rs4
-rw-r--r--crates/ide-ssr/src/matching.rs4
-rw-r--r--crates/ide/src/annotations.rs20
-rw-r--r--crates/ide/src/call_hierarchy.rs12
-rw-r--r--crates/ide/src/doc_links.rs4
-rw-r--r--crates/ide/src/expand_macro.rs7
-rw-r--r--crates/ide/src/fetch_crates.rs4
-rw-r--r--crates/ide/src/folding_ranges.rs293
-rw-r--r--crates/ide/src/goto_declaration.rs4
-rw-r--r--crates/ide/src/goto_definition.rs10
-rw-r--r--crates/ide/src/highlight_related.rs1
-rw-r--r--crates/ide/src/hover.rs10
-rw-r--r--crates/ide/src/hover/render.rs11
-rw-r--r--crates/ide/src/hover/tests.rs363
-rw-r--r--crates/ide/src/inlay_hints.rs31
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs102
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs107
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs4
-rw-r--r--crates/ide/src/inlay_hints/lifetime.rs55
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs17
-rw-r--r--crates/ide/src/inlay_hints/ra_fixture.rs2
-rw-r--r--crates/ide/src/interpret.rs6
-rw-r--r--crates/ide/src/lib.rs58
-rw-r--r--crates/ide/src/matching_brace.rs48
-rw-r--r--crates/ide/src/navigation_target.rs5
-rw-r--r--crates/ide/src/parent_module.rs4
-rw-r--r--crates/ide/src/references.rs196
-rw-r--r--crates/ide/src/runnables.rs30
-rw-r--r--crates/ide/src/signature_help.rs4
-rw-r--r--crates/ide/src/static_index.rs26
-rw-r--r--crates/ide/src/syntax_highlighting.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs12
-rw-r--r--crates/ide/src/syntax_highlighting/html.rs4
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs6
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html6
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs6
-rw-r--r--crates/ide/src/test_explorer.rs8
-rw-r--r--crates/ide/src/typing.rs13
-rw-r--r--crates/ide/src/typing/on_enter.rs196
-rw-r--r--crates/ide/src/view_crate_graph.rs11
-rw-r--r--crates/ide/src/view_syntax_tree.rs4
-rw-r--r--crates/intern/src/symbol/symbols.rs41
-rw-r--r--crates/load-cargo/src/lib.rs42
-rw-r--r--crates/parser/src/grammar.rs106
-rw-r--r--crates/parser/src/grammar/attributes.rs113
-rw-r--r--crates/parser/src/grammar/items.rs30
-rw-r--r--crates/parser/src/grammar/items/adt.rs37
-rw-r--r--crates/parser/src/grammar/items/consts.rs1
-rw-r--r--crates/parser/src/grammar/types.rs3
-rw-r--r--crates/parser/src/syntax_kind/generated.rs36
-rw-r--r--crates/parser/test_data/generated/runner.rs42
-rw-r--r--crates/parser/test_data/parser/err/0005_attribute_recover.rast4
-rw-r--r--crates/parser/test_data/parser/err/0025_nope.rast2
-rw-r--r--crates/parser/test_data/parser/err/0027_incomplete_where_for.rast2
-rw-r--r--crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast5
-rw-r--r--crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast50
-rw-r--r--crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs2
-rw-r--r--crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast19
-rw-r--r--crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs1
-rw-r--r--crates/parser/test_data/parser/inline/err/meta_recovery.rast18
-rw-r--r--crates/parser/test_data/parser/inline/ok/arg_with_attr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/array_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast10
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_attr.rast63
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_attr.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast33
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast17
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_meta.rast30
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_meta.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast25
-rw-r--r--crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs2
-rw-r--r--crates/parser/test_data/parser/inline/ok/crate_visibility.rast39
-rw-r--r--crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast38
-rw-r--r--crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast4
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rast80
-rw-r--r--crates/parser/test_data/parser/inline/ok/impl_restrictions.rs4
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast6
-rw-r--r--crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast65
-rw-r--r--crates/parser/test_data/parser/inline/ok/metas.rast274
-rw-r--r--crates/parser/test_data/parser/inline/ok/param_outer_arg.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_field_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast35
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast35
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_pat_field.rast16
-rw-r--r--crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast16
-rw-r--r--crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast2
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast37
-rw-r--r--crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs1
-rw-r--r--crates/parser/test_data/parser/inline/ok/type_const.rast22
-rw-r--r--crates/parser/test_data/parser/inline/ok/type_const.rs1
-rw-r--r--crates/parser/test_data/parser/ok/0006_inner_attributes.rast20
-rw-r--r--crates/parser/test_data/parser/ok/0008_mod_item.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0011_outer_attribute.rast17
-rw-r--r--crates/parser/test_data/parser/ok/0012_visibility.rast49
-rw-r--r--crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0035_weird_exprs.rast10
-rw-r--r--crates/parser/test_data/parser/ok/0044_let_attrs.rast13
-rw-r--r--crates/parser/test_data/parser/ok/0045_block_attrs.rast12
-rw-r--r--crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0051_parameter_attrs.rast28
-rw-r--r--crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0062_macro_2.0.rast2
-rw-r--r--crates/parser/test_data/parser/ok/0063_variadic_fun.rast13
-rw-r--r--crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast4
-rw-r--r--crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast6
-rw-r--r--crates/proc-macro-api/src/legacy_protocol/msg/flat.rs4
-rw-r--r--crates/proc-macro-srv/src/dylib/proc_macros.rs2
-rw-r--r--crates/profile/Cargo.toml4
-rw-r--r--crates/profile/src/stop_watch.rs32
-rw-r--r--crates/project-model/src/cargo_workspace.rs5
-rw-r--r--crates/project-model/src/env.rs121
-rw-r--r--crates/project-model/src/workspace.rs5
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs18
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs4
-rw-r--r--crates/rust-analyzer/src/cli/lsif.rs10
-rw-r--r--crates/rust-analyzer/src/cli/run_tests.rs4
-rw-r--r--crates/rust-analyzer/src/cli/scip.rs4
-rw-r--r--crates/rust-analyzer/src/cli/unresolved_references.rs4
-rw-r--r--crates/rust-analyzer/src/config.rs126
-rw-r--r--crates/rust-analyzer/src/flycheck.rs46
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs54
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs10
-rw-r--r--crates/rust-analyzer/src/lsp.rs8
-rw-r--r--crates/rust-analyzer/src/lsp/capabilities.rs14
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs1
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs34
-rw-r--r--crates/rust-analyzer/src/main_loop.rs22
-rw-r--r--crates/rust-analyzer/src/reload.rs22
-rw-r--r--crates/rust-analyzer/src/target_spec.rs38
-rw-r--r--crates/rust-analyzer/tests/slow-tests/flycheck.rs43
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs4
-rw-r--r--crates/rust-analyzer/tests/slow-tests/ratoml.rs42
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs3
-rw-r--r--crates/span/src/hygiene.rs51
-rw-r--r--crates/syntax/Cargo.toml1
-rw-r--r--crates/syntax/rust.ungram50
-rw-r--r--crates/syntax/src/ast.rs6
-rw-r--r--crates/syntax/src/ast/edit.rs70
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs399
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs699
-rw-r--r--crates/syntax/src/ast/make.rs12
-rw-r--r--crates/syntax/src/ast/node_ext.rs153
-rw-r--r--crates/syntax/src/ast/syntax_factory.rs10
-rw-r--r--crates/syntax/src/ast/syntax_factory/constructors.rs164
-rw-r--r--crates/syntax/src/ast/token_ext.rs6
-rw-r--r--crates/syntax/src/ast/traits.rs3
-rw-r--r--crates/syntax/src/syntax_editor.rs249
-rw-r--r--crates/syntax/src/syntax_editor/edit_algo.rs60
-rw-r--r--crates/syntax/src/syntax_editor/edits.rs73
-rw-r--r--crates/syntax/src/syntax_editor/mapping.rs2
-rw-r--r--crates/syntax/src/validation.rs12
-rw-r--r--crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast8
-rw-r--r--crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast26
-rw-r--r--crates/test-fixture/src/lib.rs4
-rw-r--r--crates/test-utils/src/minicore.rs8
-rw-r--r--crates/vfs-notify/src/lib.rs35
-rw-r--r--crates/vfs/src/lib.rs2
-rw-r--r--docs/book/src/configuration_generated.md62
-rw-r--r--docs/book/src/contributing/lsp-extensions.md6
-rw-r--r--docs/book/src/other_editors.md69
-rw-r--r--editors/code/package-lock.json1255
-rw-r--r--editors/code/package.json75
-rw-r--r--editors/code/src/client.ts2
-rw-r--r--editors/code/src/commands.ts3
-rw-r--r--editors/code/src/config.ts2
-rw-r--r--editors/code/src/debug.ts2
-rw-r--r--editors/code/src/dependencies_provider.ts6
-rw-r--r--editors/code/src/diagnostics.ts2
-rw-r--r--editors/code/src/lsp_ext.ts8
-rw-r--r--editors/code/src/snippets.ts2
-rw-r--r--editors/code/src/toolchain.ts2
-rw-r--r--editors/code/tsconfig.json1
-rw-r--r--lib/smol_str/Cargo.toml2
-rw-r--r--rust-version2
-rw-r--r--xtask/src/codegen.rs13
-rw-r--r--xtask/src/codegen/grammar.rs28
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs17
-rw-r--r--xtask/src/codegen/lints.rs15
-rw-r--r--xtask/src/tidy.rs4
482 files changed, 25988 insertions, 12596 deletions
diff --git a/.github/workflows/gen-lints.yml b/.github/workflows/gen-lints.yml
new file mode 100644
index 0000000000..c978e3571c
--- /dev/null
+++ b/.github/workflows/gen-lints.yml
@@ -0,0 +1,44 @@
+name: Generate lints and feature flags
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '50 23 * * 6'
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ lints-gen:
+ name: Generate lints
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6
+
+ - name: Install nightly
+ run: rustup default nightly
+
+ - name: Generate lints/feature flags
+ run: cargo codegen lint-definitions
+
+ - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
+ id: app-token
+ with:
+ app-id: ${{ vars.APP_CLIENT_ID }}
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
+
+ - name: Submit PR
+ uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
+ with:
+ token: ${{ steps.app-token.outputs.token }}
+ commit-message: "internal: update generated lints"
+ branch: "ci/gen-lints"
+ delete-branch: true
+ sign-commits: true
+ title: "Update generated lints"
+ body: "Weekly lint updates for `crates/ide-db/src/generated/lints.rs`."
+ labels: "A-infra"
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index b35614f91b..ef61e397fc 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -264,8 +264,6 @@ jobs:
name: ${{ env.TAG }}
token: ${{ secrets.GITHUB_TOKEN }}
- - run: rm dist/rust-analyzer-no-server.vsix
-
- run: npm ci
working-directory: ./editors/code
diff --git a/.github/workflows/rustc-pull.yml b/.github/workflows/rustc-pull.yml
index 37cf5f3726..be3362b79b 100644
--- a/.github/workflows/rustc-pull.yml
+++ b/.github/workflows/rustc-pull.yml
@@ -12,6 +12,7 @@ jobs:
uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main
with:
github-app-id: ${{ vars.APP_CLIENT_ID }}
+ pr-author: "workflows-rust-analyzer[bot]"
zulip-stream-id: 185405
zulip-bot-email: "[email protected]"
pr-base-branch: master
diff --git a/.github/workflows/rustdoc.yaml b/.github/workflows/rustdoc.yaml
index 03fd083175..c5588a29f6 100644
--- a/.github/workflows/rustdoc.yaml
+++ b/.github/workflows/rustdoc.yaml
@@ -3,6 +3,8 @@ on:
push:
branches:
- master
+ pull_request:
+ merge_group:
env:
CARGO_INCREMENTAL: 0
@@ -28,6 +30,7 @@ jobs:
run: cargo doc --all --no-deps --document-private-items
- name: Deploy Docs
+ if: github.event_name == 'push' && github.repository == 'rust-lang/rust-analyzer' && github.ref == 'refs/heads/master'
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/Cargo.lock b/Cargo.lock
index 5370127ddc..e6575c28c1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -234,7 +234,6 @@ dependencies = [
"intern",
"oorandom",
"rustc-hash 2.1.1",
- "span",
"syntax",
"syntax-bridge",
"tracing",
@@ -459,9 +458,9 @@ dependencies = [
[[package]]
name = "derive-where"
-version = "1.6.0"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
+checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
dependencies = [
"proc-macro2",
"quote",
@@ -787,6 +786,7 @@ dependencies = [
"hir-ty",
"intern",
"itertools 0.14.0",
+ "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"ra-ap-rustc_type_ir",
"rustc-hash 2.1.1",
"serde_json",
@@ -846,7 +846,6 @@ dependencies = [
name = "hir-expand"
version = "0.0.0"
dependencies = [
- "arrayvec",
"base-db",
"cfg",
"cov-mark",
@@ -1106,7 +1105,6 @@ dependencies = [
"nohash-hasher",
"parser",
"profile",
- "query-group-macro",
"rayon",
"rustc-hash 2.1.1",
"salsa",
@@ -1731,9 +1729,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "perf-event"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5396562cd2eaa828445d6d34258ae21ee1eb9d40fe626ca7f51c8dccb4af9d66"
+checksum = "b4d6393d9238342159080d79b78cb59c67399a8e7ecfa5d410bd614169e4e823"
dependencies = [
"libc",
"perf-event-open-sys",
@@ -1741,9 +1739,9 @@ dependencies = [
[[package]]
name = "perf-event-open-sys"
-version = "1.0.1"
+version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a"
+checksum = "7c44fb1c7651a45a3652c4afc6e754e40b3d6e6556f1487e2b230bfc4f33c2a8"
dependencies = [
"libc",
]
@@ -1977,9 +1975,9 @@ dependencies = [
[[package]]
name = "protobuf"
-version = "3.7.1"
+version = "3.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72"
+checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4"
dependencies = [
"once_cell",
"protobuf-support",
@@ -1988,9 +1986,9 @@ dependencies = [
[[package]]
name = "protobuf-support"
-version = "3.7.1"
+version = "3.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252"
+checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6"
dependencies = [
"thiserror 1.0.69",
]
@@ -2050,9 +2048,9 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "ra-ap-rustc_abi"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49dbe5d570793b3c3227972a6ac85fc3e830f09b32c3cb3b68cfceebad3b0a"
+checksum = "4b917ab47d7036977be4c984321af3e0de089229404d68ea9a286f50aa464697"
dependencies = [
"bitflags 2.9.4",
"ra-ap-rustc_hashes",
@@ -2062,33 +2060,33 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_ast_ir"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0956db62c264a899d15667993cbbd2e8f0b02108712217e2579c61ac30b94b"
+checksum = "021d80bea67458b8c90cc25bfdca6f911ea818a41905e370c1f310cced1dd07e"
[[package]]
name = "ra-ap-rustc_hashes"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7df512084c24f4c96c8cc9a59cbd264301efbc8913d3759b065398024af316c9"
+checksum = "8bb89395306ecfc980d252f77a4038d8b8bb578a25c856b545cbeeb3fde8358e"
dependencies = [
"rustc-stable-hash",
]
[[package]]
name = "ra-ap-rustc_index"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca3a49a928d38ba7927605e5909b6abe77d09ff359e4695c070c3f91d69cc8a"
+checksum = "84219d028a1954c4340ddde11adffe93eb83e476e942718fe926f4d99637cbbe"
dependencies = [
"ra-ap-rustc_index_macros",
]
[[package]]
name = "ra-ap-rustc_index_macros"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4463e908a62c64c2a65c1966c2f4995d0e1f8b7dfc85a8b8de2562edf3d89070"
+checksum = "3908fdfa258c663d8ee407e6b4a205b0880e323b533c0df7edceafbd54a02fb6"
dependencies = [
"proc-macro2",
"quote",
@@ -2097,20 +2095,20 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "228e01e1b237adb4bd8793487e1c37019c1e526a8f93716d99602301be267056"
+checksum = "34b50f19d5856b8e2b36150e89b53a6102ab096e8044e1f55fd6fef977b10d85"
dependencies = [
"memchr",
+ "unicode-ident",
"unicode-properties",
- "unicode-xid",
]
[[package]]
name = "ra-ap-rustc_next_trait_solver"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10d6f91143011d474bb844d268b0784c6a4c6db57743558b83f5ad34511627f1"
+checksum = "76f83dcc451bcee8a99e284a583d5b3d82db5a200107a256a40ef132c4988f1b"
dependencies = [
"derive-where",
"ra-ap-rustc_index",
@@ -2121,19 +2119,19 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37fa8effbc436c0ddd9d7b1421aa3cccf8b94566c841c4e4aa3e09063b8f423f"
+checksum = "f31236bdc6cbcae8af42d0b2db2fa8d812a8715b90a2ba5afb1132b37a4d0bbc"
dependencies = [
"ra-ap-rustc_lexer",
- "rustc-literal-escaper 0.0.5",
+ "rustc-literal-escaper 0.0.7",
]
[[package]]
name = "ra-ap-rustc_pattern_analysis"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "883c843fc27847ad03b8e772dd4a2d2728af4333a6d6821a22dfcfe7136dff3e"
+checksum = "3fc4edac740e896fba4b3b4d9c423083e3eac49947732561ddfb2377e1f57829"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash 2.1.1",
@@ -2144,15 +2142,16 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_type_ir"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a86e33c46b2b261a173b23f207461a514812a8b2d2d7935bbc685f733eacce10"
+checksum = "8efa119afc1bcadd821b27aa94332abf79c48ac0a972cb78932f63004ba4cdd9"
dependencies = [
"arrayvec",
"bitflags 2.9.4",
"derive-where",
"ena",
"indexmap",
+ "ra-ap-rustc_abi",
"ra-ap-rustc_ast_ir",
"ra-ap-rustc_index",
"ra-ap-rustc_type_ir_macros",
@@ -2164,9 +2163,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_type_ir_macros"
-version = "0.143.0"
+version = "0.160.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15034c2fcaa5cf302aea6db20eda0f71fffeb0b372d6073cc50f940e974a2a47"
+checksum = "e6b1dc03abfabc7179393c282892c73a3f0e4bbd5f0b6c87ff42c2b142e68f57"
dependencies = [
"proc-macro2",
"quote",
@@ -2176,9 +2175,9 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.9.2"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
+checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
dependencies = [
"rand_chacha",
"rand_core",
@@ -2283,9 +2282,9 @@ checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]]
name = "rowan"
-version = "0.15.17"
+version = "0.15.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b"
+checksum = "62f509095fc8cc0c8c8564016771d458079c11a8d857e65861f045145c0d3208"
dependencies = [
"countme",
"hashbrown 0.14.5",
@@ -2387,9 +2386,9 @@ checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
[[package]]
name = "rustc-literal-escaper"
-version = "0.0.5"
+version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ee29da77c5a54f42697493cd4c9b9f31b74df666a6c04dfc4fde77abe0438b"
+checksum = "8be87abb9e40db7466e0681dc8ecd9dcfd40360cb10b4c8fe24a7c4c3669b198"
[[package]]
name = "rustc-stable-hash"
@@ -2455,9 +2454,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc"
+checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676"
dependencies = [
"boxcar",
"crossbeam-queue",
@@ -2480,15 +2479,15 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3"
+checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5"
[[package]]
name = "salsa-macros"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1"
+checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05"
dependencies = [
"proc-macro2",
"quote",
@@ -2507,9 +2506,9 @@ dependencies = [
[[package]]
name = "scip"
-version = "0.5.2"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb2b449a5e4660ce817676a0871cd1b4e2ff1023e33a1ac046670fa594b543a2"
+checksum = "f4d0e81b39f590b2edbe2369760641511898ed34062aed2e18e6d05eead3d6b7"
dependencies = [
"protobuf",
]
@@ -2733,6 +2732,7 @@ dependencies = [
"rustc-hash 2.1.1",
"rustc-literal-escaper 0.0.4",
"rustc_apfloat",
+ "smallvec",
"smol_str 0.3.2",
"stdx",
"test-utils",
@@ -2824,9 +2824,9 @@ checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233"
[[package]]
name = "thin-vec"
-version = "0.2.14"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d"
+checksum = "259cdf8ed4e4aca6f1e9d011e10bd53f524a2d0637d7b28450f6c64ac298c4c6"
[[package]]
name = "thiserror"
@@ -3140,21 +3140,15 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicode-ident"
-version = "1.0.19"
+version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-properties"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.6"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
[[package]]
name = "url"
diff --git a/Cargo.toml b/Cargo.toml
index 9f31e1903a..b8dedc6c50 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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.143", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.143", default-features = false }
-ra-ap-rustc_index = { version = "0.143", default-features = false }
-ra-ap-rustc_abi = { version = "0.143", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.143", default-features = false }
-ra-ap-rustc_ast_ir = { version = "0.143", default-features = false }
-ra-ap-rustc_type_ir = { version = "0.143", default-features = false }
-ra-ap-rustc_next_trait_solver = { version = "0.143", default-features = false }
+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 }
# local crates that aren't published to crates.io. These should not have versions.
@@ -127,21 +127,21 @@ object = { version = "0.36.7", default-features = false, features = [
"macho",
"pe",
] }
-postcard = {version = "1.1.3", features = ["alloc"]}
+postcard = { version = "1.1.3", features = ["alloc"] }
process-wrap = { version = "8.2.1", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.6", default-features = false }
rayon = "1.10.0"
-rowan = "=0.15.17"
+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.25.2", default-features = false, features = [
+salsa = { version = "0.26", default-features = false, features = [
"rayon",
"salsa_unstable",
"macros",
"inventory",
] }
-salsa-macros = "0.25.2"
+salsa-macros = "0.26"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }
@@ -170,7 +170,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features =
triomphe = { version = "0.1.14", default-features = false, features = ["std"] }
url = "2.5.4"
xshell = "0.2.7"
-thin-vec = "0.2.14"
+thin-vec = "0.2.16"
petgraph = { version = "0.8.2", default-features = false }
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
@@ -186,7 +186,10 @@ hashbrown = { version = "0.14.*", features = [
elided_lifetimes_in_paths = "warn"
explicit_outlives_requirements = "warn"
unsafe_op_in_unsafe_fn = "warn"
-unexpected_cfgs = { level = "warn", check-cfg = ['cfg(bootstrap)', "cfg(no_salsa_async_drops)"] }
+unexpected_cfgs = { level = "warn", check-cfg = [
+ 'cfg(bootstrap)',
+ "cfg(no_salsa_async_drops)",
+] }
unused_extern_crates = "warn"
unused_lifetimes = "warn"
unreachable_pub = "warn"
diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser
index 8136daa832..5022514924 100644
--- a/bench_data/glorious_old_parser
+++ b/bench_data/glorious_old_parser
@@ -1,3 +1,4 @@
+//- minicore: fn
use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy};
use crate::ast::{GenericBound, TraitBoundModifier};
use crate::ast::Unsafety;
diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs
index c728f3e5ca..4d4bf78cbc 100644
--- a/crates/base-db/src/change.rs
+++ b/crates/base-db/src/change.rs
@@ -9,7 +9,8 @@ use triomphe::Arc;
use vfs::FileId;
use crate::{
- CrateGraphBuilder, CratesIdMap, LibraryRoots, LocalRoots, RootQueryDb, SourceRoot, SourceRootId,
+ CrateGraphBuilder, CratesIdMap, LibraryRoots, LocalRoots, SourceDatabase, SourceRoot,
+ SourceRootId,
};
/// Encapsulate a bunch of raw `.set` calls on the database.
@@ -49,7 +50,7 @@ impl FileChange {
self.crate_graph = Some(graph);
}
- pub fn apply(self, db: &mut dyn RootQueryDb) -> Option<CratesIdMap> {
+ pub fn apply(self, db: &mut dyn SourceDatabase) -> Option<CratesIdMap> {
let _p = tracing::info_span!("FileChange::apply").entered();
if let Some(roots) = self.roots {
let mut local_roots = FxHashSet::default();
diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs
index 8721f3a0ff..a77b45f8ae 100644
--- a/crates/base-db/src/editioned_file_id.rs
+++ b/crates/base-db/src/editioned_file_id.rs
@@ -5,14 +5,71 @@ use std::hash::Hash;
use salsa::Database;
use span::Edition;
+use syntax::{SyntaxError, ast};
use vfs::FileId;
+use crate::SourceDatabase;
+
#[salsa::interned(debug, constructor = from_span_file_id, no_lifetime)]
#[derive(PartialOrd, Ord)]
pub struct EditionedFileId {
field: span::EditionedFileId,
}
+// Currently does not work due to a salsa bug
+// #[salsa::tracked]
+// impl EditionedFileId {
+// #[salsa::tracked(lru = 128)]
+// pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> {
+// let _p = tracing::info_span!("parse", ?self).entered();
+// let (file_id, edition) = self.unpack(db);
+// let text = db.file_text(file_id).text(db);
+// ast::SourceFile::parse(text, edition)
+// }
+
+// // firewall query
+// #[salsa::tracked(returns(as_deref))]
+// pub fn parse_errors(self, db: &dyn SourceDatabase) -> Option<Box<[SyntaxError]>> {
+// let errors = self.parse(db).errors();
+// match &*errors {
+// [] => None,
+// [..] => Some(errors.into()),
+// }
+// }
+// }
+
+impl EditionedFileId {
+ pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> {
+ #[salsa::tracked(lru = 128)]
+ pub fn parse(
+ db: &dyn SourceDatabase,
+ file_id: EditionedFileId,
+ ) -> syntax::Parse<ast::SourceFile> {
+ let _p = tracing::info_span!("parse", ?file_id).entered();
+ let (file_id, edition) = file_id.unpack(db);
+ let text = db.file_text(file_id).text(db);
+ ast::SourceFile::parse(text, edition)
+ }
+ parse(db, self)
+ }
+
+ // firewall query
+ pub fn parse_errors(self, db: &dyn SourceDatabase) -> Option<&[SyntaxError]> {
+ #[salsa::tracked(returns(as_deref))]
+ pub fn parse_errors(
+ db: &dyn SourceDatabase,
+ file_id: EditionedFileId,
+ ) -> Option<Box<[SyntaxError]>> {
+ let errors = file_id.parse(db).errors();
+ match &*errors {
+ [] => None,
+ [..] => Some(errors.into()),
+ }
+ }
+ parse_errors(db, self)
+ }
+}
+
impl EditionedFileId {
#[inline]
pub fn new(db: &dyn Database, file_id: FileId, edition: Edition) -> Self {
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 246c57edc2..38f9c5a5a1 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -21,7 +21,10 @@ use span::Edition;
use triomphe::Arc;
use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
-use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb};
+use crate::{
+ CrateWorkspaceData, EditionedFileId, FxIndexSet, SourceDatabase, all_crates,
+ set_all_crates_with_durability,
+};
pub type ProcMacroPaths =
FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), ProcMacroLoadingError>>;
@@ -490,13 +493,13 @@ impl Crate {
/// including the crate itself.
///
/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
- pub fn transitive_rev_deps(self, db: &dyn RootQueryDb) -> Box<[Crate]> {
+ pub fn transitive_rev_deps(self, db: &dyn SourceDatabase) -> Box<[Crate]> {
let mut worklist = vec![self];
let mut rev_deps = FxHashSet::default();
rev_deps.insert(self);
let mut inverted_graph = FxHashMap::<_, Vec<_>>::default();
- db.all_crates().iter().for_each(|&krate| {
+ all_crates(db).iter().for_each(|&krate| {
krate
.data(db)
.dependencies
@@ -586,15 +589,15 @@ impl CrateGraphBuilder {
Ok(())
}
- pub fn set_in_db(self, db: &mut dyn RootQueryDb) -> CratesIdMap {
+ pub fn set_in_db(self, db: &mut dyn SourceDatabase) -> CratesIdMap {
+ let old_all_crates = all_crates(db);
+
// For some reason in some repositories we have duplicate crates, so we use a set and not `Vec`.
// We use an `IndexSet` because the list needs to be topologically sorted.
let mut all_crates = FxIndexSet::with_capacity_and_hasher(self.arena.len(), FxBuildHasher);
let mut visited = FxHashMap::default();
let mut visited_root_files = FxHashSet::default();
- let old_all_crates = db.all_crates();
-
let crates_map = db.crates_map();
// salsa doesn't compare new input to old input to see if they are the same, so here we are doing all the work ourselves.
for krate in self.iter() {
@@ -612,17 +615,14 @@ impl CrateGraphBuilder {
if old_all_crates.len() != all_crates.len()
|| old_all_crates.iter().any(|&krate| !all_crates.contains(&krate))
{
- db.set_all_crates_with_durability(
- Arc::new(Vec::from_iter(all_crates).into_boxed_slice()),
- Durability::MEDIUM,
- );
+ set_all_crates_with_durability(db, all_crates, Durability::MEDIUM);
}
return visited;
fn go(
graph: &CrateGraphBuilder,
- db: &mut dyn RootQueryDb,
+ db: &mut dyn SourceDatabase,
crates_map: &CratesMap,
visited: &mut FxHashMap<CrateBuilderId, Crate>,
visited_root_files: &mut FxHashSet<FileId>,
@@ -929,6 +929,27 @@ impl<'a> IntoIterator for &'a Env {
}
}
+/// The crate graph had a cycle. This is typically a bug, and
+/// rust-analyzer logs a warning when it encounters a cycle. Generally
+/// rust-analyzer will continue working OK in the presence of cycle,
+/// but it's better to have an accurate crate graph.
+///
+/// ## dev-dependencies
+///
+/// Note that it's actually legal for a cargo package (i.e. a thing
+/// with a Cargo.toml) to depend on itself in dev-dependencies. This
+/// can enable additional features, and is typically used when a
+/// project wants features to be enabled in tests. Dev-dependencies
+/// are not propagated, so they aren't visible to package that depend
+/// on this one.
+///
+/// <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#development-dependencies>
+///
+/// However, rust-analyzer constructs its crate graph from Cargo
+/// metadata, so it can end up producing a cyclic crate graph from a
+/// well-formed package graph.
+///
+/// <https://github.com/rust-lang/rust-analyzer/issues/14167>
#[derive(Debug)]
pub struct CyclicDependenciesError {
path: Vec<(CrateBuilderId, Option<CrateDisplayName>)>,
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 5baf4ce6f9..e438505c07 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -36,7 +36,6 @@ pub use query_group;
use rustc_hash::{FxHashSet, FxHasher};
use salsa::{Durability, Setter};
pub use semver::{BuildMetadata, Prerelease, Version, VersionReq};
-use syntax::{Parse, SyntaxError, ast};
use triomphe::Arc;
pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
@@ -236,36 +235,6 @@ pub struct SourceRootInput {
pub source_root: Arc<SourceRoot>,
}
-/// Database which stores all significant input facts: source code and project
-/// model. Everything else in rust-analyzer is derived from these queries.
-#[query_group::query_group]
-pub trait RootQueryDb: SourceDatabase + salsa::Database {
- /// Parses the file into the syntax tree.
- #[salsa::invoke(parse)]
- #[salsa::lru(128)]
- fn parse(&self, file_id: EditionedFileId) -> Parse<ast::SourceFile>;
-
- /// Returns the set of errors obtained from parsing the file including validation errors.
- #[salsa::transparent]
- fn parse_errors(&self, file_id: EditionedFileId) -> Option<&[SyntaxError]>;
-
- #[salsa::transparent]
- fn toolchain_channel(&self, krate: Crate) -> Option<ReleaseChannel>;
-
- /// Crates whose root file is in `id`.
- #[salsa::invoke_interned(source_root_crates)]
- fn source_root_crates(&self, id: SourceRootId) -> Arc<[Crate]>;
-
- #[salsa::transparent]
- fn relevant_crates(&self, file_id: FileId) -> Arc<[Crate]>;
-
- /// Returns the crates in topological order.
- ///
- /// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
- #[salsa::input]
- fn all_crates(&self) -> Arc<Box<[Crate]>>;
-}
-
#[salsa_macros::db]
pub trait SourceDatabase: salsa::Database {
/// Text of the file.
@@ -353,46 +322,67 @@ impl CrateWorkspaceData {
}
}
-fn toolchain_channel(db: &dyn RootQueryDb, krate: Crate) -> Option<ReleaseChannel> {
+pub fn toolchain_channel(db: &dyn salsa::Database, krate: Crate) -> Option<ReleaseChannel> {
krate.workspace_data(db).toolchain.as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre))
}
-fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFile> {
- let _p = tracing::info_span!("parse", ?file_id).entered();
- let (file_id, edition) = file_id.unpack(db.as_dyn_database());
- let text = db.file_text(file_id).text(db);
- ast::SourceFile::parse(text, edition)
+#[salsa::input(singleton, debug)]
+struct AllCrates {
+ crates: std::sync::Arc<[Crate]>,
}
-fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
- #[salsa_macros::tracked(returns(ref))]
- fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<Box<[SyntaxError]>> {
- let errors = db.parse(file_id).errors();
- match &*errors {
- [] => None,
- [..] => Some(errors.into()),
- }
- }
- parse_errors(db, file_id).as_ref().map(|it| &**it)
+pub fn set_all_crates_with_durability(
+ db: &mut dyn salsa::Database,
+ crates: impl IntoIterator<Item = Crate>,
+ durability: Durability,
+) {
+ AllCrates::try_get(db)
+ .unwrap_or_else(|| AllCrates::new(db, std::sync::Arc::default()))
+ .set_crates(db)
+ .with_durability(durability)
+ .to(crates.into_iter().collect());
}
-fn source_root_crates(db: &dyn RootQueryDb, id: SourceRootId) -> Arc<[Crate]> {
- let crates = db.all_crates();
- crates
- .iter()
- .copied()
- .filter(|&krate| {
- let root_file = krate.data(db).root_file_id;
- db.file_source_root(root_file).source_root_id(db) == id
- })
- .collect()
+/// Returns the crates in topological order.
+///
+/// **Warning**: do not use this query in `hir-*` crates! It kills incrementality across crate metadata modifications.
+pub fn all_crates(db: &dyn salsa::Database) -> std::sync::Arc<[Crate]> {
+ AllCrates::try_get(db).map_or(std::sync::Arc::default(), |all_crates| all_crates.crates(db))
+}
+
+// FIXME: VFS rewrite should allow us to get rid of this wrapper
+#[doc(hidden)]
+#[salsa::interned]
+pub struct InternedSourceRootId {
+ pub id: SourceRootId,
+}
+
+/// Crates whose root file is in `id`.
+pub fn source_root_crates(db: &dyn SourceDatabase, id: SourceRootId) -> &[Crate] {
+ #[salsa::tracked(returns(deref))]
+ pub fn source_root_crates<'db>(
+ db: &'db dyn SourceDatabase,
+ id: InternedSourceRootId<'db>,
+ ) -> Box<[Crate]> {
+ let crates = AllCrates::get(db).crates(db);
+ let id = id.id(db);
+ crates
+ .iter()
+ .copied()
+ .filter(|&krate| {
+ let root_file = krate.data(db).root_file_id;
+ db.file_source_root(root_file).source_root_id(db) == id
+ })
+ .collect()
+ }
+ source_root_crates(db, InternedSourceRootId::new(db, id))
}
-fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
+pub fn relevant_crates(db: &dyn SourceDatabase, file_id: FileId) -> &[Crate] {
let _p = tracing::info_span!("relevant_crates").entered();
let source_root = db.file_source_root(file_id);
- db.source_root_crates(source_root.source_root_id(db))
+ source_root_crates(db, source_root.source_root_id(db))
}
#[must_use]
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index cf2a7607b0..15de1f3293 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -19,7 +19,6 @@ tracing.workspace = true
# locals deps
tt = { workspace = true, optional = true }
syntax = { workspace = true, optional = true }
-span = { path = "../span", version = "0.0", optional = true }
intern.workspace = true
[dev-dependencies]
@@ -36,7 +35,7 @@ cfg = { path = ".", default-features = false, features = ["tt"] }
[features]
default = []
-syntax = ["dep:syntax", "dep:span"]
+syntax = ["dep:syntax"]
tt = ["dep:tt"]
in-rust-tree = []
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index d253f6f492..7af3ed5dc9 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -106,10 +106,54 @@ impl CfgExpr {
}
#[cfg(feature = "syntax")]
- pub fn parse_from_ast(
- ast: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
- ) -> CfgExpr {
- next_cfg_expr_from_ast(ast).unwrap_or(CfgExpr::Invalid)
+ pub fn parse_from_ast(ast: syntax::ast::CfgPredicate) -> CfgExpr {
+ use intern::sym;
+ use syntax::ast::{self, AstToken};
+
+ match ast {
+ ast::CfgPredicate::CfgAtom(atom) => {
+ let atom = match atom.key() {
+ Some(ast::CfgAtomKey::True) => CfgAtom::Flag(sym::true_),
+ Some(ast::CfgAtomKey::False) => CfgAtom::Flag(sym::false_),
+ Some(ast::CfgAtomKey::Ident(key)) => {
+ let key = Symbol::intern(key.text());
+ match atom.string_token().and_then(ast::String::cast) {
+ Some(value) => {
+ if let Ok(value) = value.value() {
+ CfgAtom::KeyValue { key, value: Symbol::intern(&value) }
+ } else {
+ return CfgExpr::Invalid;
+ }
+ }
+ None => CfgAtom::Flag(key),
+ }
+ }
+ None => return CfgExpr::Invalid,
+ };
+ CfgExpr::Atom(atom)
+ }
+ ast::CfgPredicate::CfgComposite(composite) => {
+ let Some(keyword) = composite.keyword() else {
+ return CfgExpr::Invalid;
+ };
+ match keyword.text() {
+ "all" => CfgExpr::All(
+ composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
+ ),
+ "any" => CfgExpr::Any(
+ composite.cfg_predicates().map(CfgExpr::parse_from_ast).collect(),
+ ),
+ "not" => {
+ let mut inner = composite.cfg_predicates();
+ let (Some(inner), None) = (inner.next(), inner.next()) else {
+ return CfgExpr::Invalid;
+ };
+ CfgExpr::Not(Box::new(CfgExpr::parse_from_ast(inner)))
+ }
+ _ => CfgExpr::Invalid,
+ }
+ }
+ }
}
/// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
@@ -128,65 +172,6 @@ impl CfgExpr {
}
}
-#[cfg(feature = "syntax")]
-fn next_cfg_expr_from_ast(
- it: &mut std::iter::Peekable<syntax::ast::TokenTreeChildren>,
-) -> Option<CfgExpr> {
- use intern::sym;
- use syntax::{NodeOrToken, SyntaxKind, T, ast};
-
- let name = match it.next() {
- None => return None,
- Some(NodeOrToken::Token(ident)) if ident.kind().is_any_identifier() => {
- Symbol::intern(ident.text())
- }
- Some(_) => return Some(CfgExpr::Invalid),
- };
-
- let ret = match it.peek() {
- Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
- it.next();
- if let Some(NodeOrToken::Token(literal)) = it.peek()
- && matches!(literal.kind(), SyntaxKind::STRING)
- {
- let dummy_span = span::Span {
- range: span::TextRange::empty(span::TextSize::new(0)),
- anchor: span::SpanAnchor {
- file_id: span::EditionedFileId::from_raw(0),
- ast_id: span::FIXUP_ERASED_FILE_AST_ID_MARKER,
- },
- ctx: span::SyntaxContext::root(span::Edition::Edition2015),
- };
- let literal =
- Symbol::intern(tt::token_to_literal(literal.text(), dummy_span).text());
- it.next();
- CfgAtom::KeyValue { key: name, value: literal.clone() }.into()
- } else {
- return Some(CfgExpr::Invalid);
- }
- }
- Some(NodeOrToken::Node(subtree)) => {
- let mut subtree_iter = ast::TokenTreeChildren::new(subtree).peekable();
- it.next();
- let mut subs = std::iter::from_fn(|| next_cfg_expr_from_ast(&mut subtree_iter));
- match name {
- s if s == sym::all => CfgExpr::All(subs.collect()),
- s if s == sym::any => CfgExpr::Any(subs.collect()),
- s if s == sym::not => {
- CfgExpr::Not(Box::new(subs.next().unwrap_or(CfgExpr::Invalid)))
- }
- _ => CfgExpr::Invalid,
- }
- }
- _ => CfgAtom::Flag(name).into(),
- };
-
- // Eat comma separator
- while it.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
-
- Some(ret)
-}
-
#[cfg(feature = "tt")]
fn next_cfg_expr(it: &mut tt::iter::TtIter<'_>) -> Option<CfgExpr> {
use intern::sym;
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs
index 52c581dbbd..bfc9220a05 100644
--- a/crates/cfg/src/tests.rs
+++ b/crates/cfg/src/tests.rs
@@ -1,10 +1,7 @@
use arbitrary::{Arbitrary, Unstructured};
use expect_test::{Expect, expect};
use intern::Symbol;
-use syntax::{
- AstNode, Edition,
- ast::{self, TokenTreeChildren},
-};
+use syntax::{AstNode, Edition, ast};
use syntax_bridge::{
DocCommentDesugarMode,
dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
@@ -14,32 +11,32 @@ use syntax_bridge::{
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
#[track_caller]
-fn parse_ast_cfg(tt: &ast::TokenTree) -> CfgExpr {
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable())
+fn parse_ast_cfg(pred: ast::CfgPredicate) -> CfgExpr {
+ CfgExpr::parse_from_ast(pred)
}
#[track_caller]
fn assert_parse_result(input: &str, expected: CfgExpr) {
- let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let source_file = ast::SourceFile::parse(input, Edition::CURRENT).syntax_node();
+ let pred_ast = source_file.descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
);
let cfg = CfgExpr::parse(&tt);
assert_eq!(cfg, expected);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
assert_eq!(cfg, expected);
}
#[track_caller]
fn check_dnf(input: &str, expect: Expect) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -47,7 +44,7 @@ fn check_dnf(input: &str, expect: Expect) {
let cfg = CfgExpr::parse(&tt);
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let actual = format!("#![cfg({})]", DnfExpr::new(&cfg));
expect.assert_eq(&actual);
}
@@ -55,9 +52,9 @@ fn check_dnf(input: &str, expect: Expect) {
#[track_caller]
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -66,7 +63,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
let dnf = DnfExpr::new(&cfg);
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
expect.assert_eq(&why_inactive);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let dnf = DnfExpr::new(&cfg);
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
expect.assert_eq(&why_inactive);
@@ -75,9 +72,9 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
#[track_caller]
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
let source_file = ast::SourceFile::parse(input, Edition::CURRENT).ok().unwrap();
- let tt_ast = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+ let pred_ast = source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
let tt = syntax_node_to_token_tree(
- tt_ast.syntax(),
+ pred_ast.syntax(),
DummyTestSpanMap,
DUMMY,
DocCommentDesugarMode::ProcMacro,
@@ -86,7 +83,7 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
let dnf = DnfExpr::new(&cfg);
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
assert_eq!(hints, expected_hints);
- let cfg = parse_ast_cfg(&tt_ast);
+ let cfg = parse_ast_cfg(pred_ast);
let dnf = DnfExpr::new(&cfg);
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
assert_eq!(hints, expected_hints);
@@ -119,20 +116,6 @@ fn test_cfg_expr_parser() {
.into_boxed_slice(),
),
);
-
- assert_parse_result(
- r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
- CfgExpr::Any(
- vec![
- CfgExpr::Not(Box::new(CfgExpr::Invalid)),
- CfgExpr::All(Box::new([])),
- CfgExpr::Invalid,
- CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }
- .into(),
- ]
- .into_boxed_slice(),
- ),
- );
}
#[test]
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs
index e3e1aac709..5cf5a9b6be 100644
--- a/crates/hir-def/src/attrs.rs
+++ b/crates/hir-def/src/attrs.rs
@@ -12,25 +12,17 @@
//! its value. This way, queries are only called on items that have the attribute, which is
//! usually only a few.
//!
-//! An exception to this model that is also defined in this module is documentation (doc
-//! comments and `#[doc = "..."]` attributes). But it also has a more compact form than
-//! the attribute: a concatenated string of the full docs as well as a source map
-//! to map it back to AST (which is needed for things like resolving links in doc comments
-//! and highlight injection). The lowering and upmapping of doc comments is a bit complicated,
-//! but it is encapsulated in the [`Docs`] struct.
-
-use std::{
- convert::Infallible,
- iter::Peekable,
- ops::{ControlFlow, Range},
-};
+//! Documentation (doc comments and `#[doc = "..."]` attributes) is handled by the [`docs`]
+//! submodule.
+
+use std::{convert::Infallible, iter::Peekable, ops::ControlFlow};
use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
- HirFileId, InFile, Lookup,
- attrs::{Meta, expand_cfg_attr, expand_cfg_attr_with_doc_comments},
+ InFile, Lookup,
+ attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr},
};
use intern::Symbol;
use itertools::Itertools;
@@ -39,10 +31,10 @@ use rustc_abi::ReprOptions;
use rustc_hash::FxHashSet;
use smallvec::SmallVec;
use syntax::{
- AstNode, AstToken, NodeOrToken, SmolStr, SourceFile, SyntaxNode, SyntaxToken, T,
- ast::{self, AttrDocCommentIter, HasAttrs, IsString, TokenTreeChildren},
+ AstNode, AstToken, NodeOrToken, SmolStr, SourceFile, T,
+ ast::{self, HasAttrs, TokenTreeChildren},
};
-use tt::{TextRange, TextSize};
+use tt::TextSize;
use crate::{
AdtId, AstIdLoc, AttrDefId, FieldId, FunctionId, GenericDefId, HasModule, LifetimeParamId,
@@ -50,9 +42,14 @@ use crate::{
db::DefDatabase,
hir::generics::{GenericParams, LocalLifetimeParamId, LocalTypeOrConstParamId},
nameres::ModuleOrigin,
+ resolver::{HasResolver, Resolver},
src::{HasChildSource, HasSource},
};
+pub mod docs;
+
+pub use self::docs::{Docs, IsInnerDoc};
+
#[inline]
fn attrs_from_ast_id_loc<N: AstNode + Into<ast::AnyHasAttrs>>(
db: &dyn DefDatabase,
@@ -131,69 +128,96 @@ fn extract_rustc_skip_during_method_dispatch(attr_flags: &mut AttrFlags, tt: ast
}
#[inline]
-fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infallible> {
+fn match_attr_flags(attr_flags: &mut AttrFlags, attr: ast::Meta) -> ControlFlow<Infallible> {
match attr {
- Meta::NamedKeyValue { name: Some(name), value, .. } => match name.text() {
- "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
- "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
- "lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
- "path" => attr_flags.insert(AttrFlags::HAS_PATH),
- "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
- "export_name" => {
- if let Some(value) = value
- && let Some(value) = ast::String::cast(value)
- && let Ok(value) = value.value()
- && *value == *"main"
- {
- attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN);
- }
- }
- _ => {}
- },
- Meta::TokenTree { path, tt } => match path.segments.len() {
- 1 => match path.segments[0].text() {
+ ast::Meta::CfgMeta(_) => attr_flags.insert(AttrFlags::HAS_CFG),
+ ast::Meta::KeyValueMeta(attr) => {
+ let Some(key) = attr.path().as_one_segment() else { return ControlFlow::Continue(()) };
+ match &*key {
"deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
- "cfg" => attr_flags.insert(AttrFlags::HAS_CFG),
- "doc" => extract_doc_tt_attr(attr_flags, tt),
- "repr" => attr_flags.insert(AttrFlags::HAS_REPR),
- "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
- "proc_macro_derive" | "rustc_builtin_macro" => {
- attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
- }
+ "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE),
+ "lang" => attr_flags.insert(AttrFlags::LANG_ITEM),
+ "path" => attr_flags.insert(AttrFlags::HAS_PATH),
"unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
- "rustc_layout_scalar_valid_range_start" | "rustc_layout_scalar_valid_range_end" => {
- attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
- }
- "rustc_legacy_const_generics" => {
- attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS)
- }
- "rustc_skip_during_method_dispatch" => {
- extract_rustc_skip_during_method_dispatch(attr_flags, tt)
- }
- "rustc_deprecated_safe_2024" => {
- attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
+ "export_name" => {
+ if let Some(value) = attr.value_string()
+ && *value == *"main"
+ {
+ attr_flags.insert(AttrFlags::IS_EXPORT_NAME_MAIN);
+ }
}
_ => {}
- },
- 2 => match path.segments[0].text() {
- "rust_analyzer" => match path.segments[1].text() {
- "completions" => extract_ra_completions(attr_flags, tt),
- "macro_style" => extract_ra_macro_style(attr_flags, tt),
+ }
+ }
+ ast::Meta::TokenTreeMeta(attr) => {
+ let (Some((first_segment, second_segment)), Some(tt)) =
+ (attr.path().as_up_to_two_segment(), attr.token_tree())
+ else {
+ return ControlFlow::Continue(());
+ };
+ match second_segment {
+ None => match &*first_segment {
+ "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED),
+ "doc" => extract_doc_tt_attr(attr_flags, tt),
+ "repr" | "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR),
+ "target_feature" => attr_flags.insert(AttrFlags::HAS_TARGET_FEATURE),
+ "proc_macro_derive" | "rustc_builtin_macro" => {
+ attr_flags.insert(AttrFlags::IS_DERIVE_OR_BUILTIN_MACRO)
+ }
+ "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE),
+ "rustc_layout_scalar_valid_range_start"
+ | "rustc_layout_scalar_valid_range_end" => {
+ attr_flags.insert(AttrFlags::RUSTC_LAYOUT_SCALAR_VALID_RANGE)
+ }
+ "rustc_legacy_const_generics" => {
+ attr_flags.insert(AttrFlags::HAS_LEGACY_CONST_GENERICS)
+ }
+ "rustc_skip_during_method_dispatch" => {
+ extract_rustc_skip_during_method_dispatch(attr_flags, tt)
+ }
+ "rustc_deprecated_safe_2024" => {
+ attr_flags.insert(AttrFlags::RUSTC_DEPRECATED_SAFE_2024)
+ }
_ => {}
},
- _ => {}
- },
- _ => {}
- },
- Meta::Path { path } => {
- match path.segments.len() {
- 1 => match path.segments[0].text() {
+ Some(second_segment) => match &*first_segment {
+ "rust_analyzer" => match &*second_segment {
+ "completions" => extract_ra_completions(attr_flags, tt),
+ "macro_style" => extract_ra_macro_style(attr_flags, tt),
+ _ => {}
+ },
+ _ => {}
+ },
+ }
+ }
+ ast::Meta::PathMeta(attr) => {
+ let is_test = attr.path().is_some_and(|path| {
+ let Some(segment1) = (|| path.segment()?.name_ref())() else { return false };
+ let segment2 = path.qualifier();
+ let segment3 = segment2.as_ref().and_then(|it| it.qualifier());
+ let segment4 = segment3.as_ref().and_then(|it| it.qualifier());
+ let segment3 = segment3.and_then(|it| it.segment()?.name_ref());
+ let segment4 = segment4.and_then(|it| it.segment()?.name_ref());
+ segment1.text() == "test"
+ && segment3.is_none_or(|it| it.text() == "prelude")
+ && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std"))
+ });
+ if is_test {
+ attr_flags.insert(AttrFlags::IS_TEST);
+ }
+
+ let Some((first_segment, second_segment)) = attr.path().as_up_to_two_segment() else {
+ return ControlFlow::Continue(());
+ };
+ match second_segment {
+ None => match &*first_segment {
"rustc_has_incoherent_inherent_impls" => {
attr_flags.insert(AttrFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
}
"rustc_allow_incoherent_impl" => {
attr_flags.insert(AttrFlags::RUSTC_ALLOW_INCOHERENT_IMPL)
}
+ "rustc_scalable_vector" => attr_flags.insert(AttrFlags::HAS_REPR),
"fundamental" => attr_flags.insert(AttrFlags::FUNDAMENTAL),
"no_std" => attr_flags.insert(AttrFlags::IS_NO_STD),
"may_dangle" => attr_flags.insert(AttrFlags::MAY_DANGLE),
@@ -231,18 +255,16 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow<Infal
}
_ => {}
},
- 2 => match path.segments[0].text() {
- "rust_analyzer" => match path.segments[1].text() {
+ Some(second_segment) => match &*first_segment {
+ "rust_analyzer" => match &*second_segment {
"skip" => attr_flags.insert(AttrFlags::RUST_ANALYZER_SKIP),
+ "prefer_underscore_import" => {
+ attr_flags.insert(AttrFlags::PREFER_UNDERSCORE_IMPORT)
+ }
_ => {}
},
_ => {}
},
- _ => {}
- }
-
- if path.is_test {
- attr_flags.insert(AttrFlags::IS_TEST);
}
}
_ => {}
@@ -311,6 +333,8 @@ bitflags::bitflags! {
const MACRO_STYLE_BRACES = 1 << 46;
const MACRO_STYLE_BRACKETS = 1 << 47;
const MACRO_STYLE_PARENTHESES = 1 << 48;
+
+ const PREFER_UNDERSCORE_IMPORT = 1 << 49;
}
}
@@ -354,13 +378,13 @@ fn attrs_source(
let krate = def_map.krate();
let (definition, declaration, extra_crate_attrs) = match def_map[id].origin {
ModuleOrigin::CrateRoot { definition } => {
- let definition_source = db.parse(definition).tree();
+ let definition_source = definition.parse(db).tree();
let definition = InFile::new(definition.into(), definition_source.into());
let extra_crate_attrs = parse_extra_crate_attrs(db, krate);
(definition, None, extra_crate_attrs)
}
ModuleOrigin::File { declaration, declaration_tree_id, definition, .. } => {
- let definition_source = db.parse(definition).tree();
+ let definition_source = definition.parse(db).tree();
let definition = InFile::new(definition.into(), definition_source.into());
let declaration = InFile::new(declaration_tree_id.file_id(), declaration);
let declaration = declaration.with_value(declaration.to_node(db));
@@ -398,10 +422,32 @@ fn attrs_source(
(owner, None, None, krate)
}
+fn resolver_for_attr_def_id(db: &dyn DefDatabase, owner: AttrDefId) -> Resolver<'_> {
+ match owner {
+ AttrDefId::ModuleId(id) => id.resolver(db),
+ AttrDefId::AdtId(AdtId::StructId(id)) => id.resolver(db),
+ AttrDefId::AdtId(AdtId::UnionId(id)) => id.resolver(db),
+ AttrDefId::AdtId(AdtId::EnumId(id)) => id.resolver(db),
+ AttrDefId::FunctionId(id) => id.resolver(db),
+ AttrDefId::EnumVariantId(id) => id.resolver(db),
+ AttrDefId::StaticId(id) => id.resolver(db),
+ AttrDefId::ConstId(id) => id.resolver(db),
+ AttrDefId::TraitId(id) => id.resolver(db),
+ AttrDefId::TypeAliasId(id) => id.resolver(db),
+ AttrDefId::MacroId(MacroId::Macro2Id(id)) => id.resolver(db),
+ AttrDefId::MacroId(MacroId::MacroRulesId(id)) => id.resolver(db),
+ AttrDefId::MacroId(MacroId::ProcMacroId(id)) => id.resolver(db),
+ AttrDefId::ImplId(id) => id.resolver(db),
+ AttrDefId::ExternBlockId(id) => id.resolver(db),
+ AttrDefId::ExternCrateId(id) => id.resolver(db),
+ AttrDefId::UseId(id) => id.resolver(db),
+ }
+}
+
fn collect_attrs<BreakValue>(
db: &dyn DefDatabase,
owner: AttrDefId,
- mut callback: impl FnMut(Meta) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(ast::Meta) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
let (source, outer_mod_decl, extra_crate_attrs, krate) = attrs_source(db, owner);
let extra_attrs = extra_crate_attrs
@@ -413,7 +459,7 @@ fn collect_attrs<BreakValue>(
expand_cfg_attr(
extra_attrs.chain(ast::attrs_including_inner(&source.value)),
|| cfg_options.get_or_insert_with(|| krate.cfg_options(db)),
- move |meta, _, _, _| callback(meta),
+ move |meta, _| callback(meta),
)
}
@@ -475,291 +521,16 @@ pub struct RustcLayoutScalarValidRange {
pub end: Option<u128>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-struct DocsSourceMapLine {
- /// The offset in [`Docs::docs`].
- string_offset: TextSize,
- /// The offset in the AST of the text.
- ast_offset: TextSize,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Docs {
- /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments.
- docs: String,
- /// A sorted map from an offset in `docs` to an offset in the source code.
- docs_source_map: Vec<DocsSourceMapLine>,
- /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated
- /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`]
- /// of the outline declaration, and the index in `docs` from which the inline docs
- /// begin.
- outline_mod: Option<(HirFileId, usize)>,
- inline_file: HirFileId,
- /// The size the prepended prefix, which does not map to real doc comments.
- prefix_len: TextSize,
- /// The offset in `docs` from which the docs are inner attributes/comments.
- inline_inner_docs_start: Option<TextSize>,
- /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs`
- /// (as outline modules don't have inner attributes).
- outline_inner_docs_start: Option<TextSize>,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum IsInnerDoc {
- No,
- Yes,
-}
-
-impl IsInnerDoc {
- #[inline]
- pub fn yes(self) -> bool {
- self == IsInnerDoc::Yes
- }
-}
-
-impl Docs {
- #[inline]
- pub fn docs(&self) -> &str {
- &self.docs
- }
-
- #[inline]
- pub fn into_docs(self) -> String {
- self.docs
- }
-
- pub fn find_ast_range(
- &self,
- mut string_range: TextRange,
- ) -> Option<(InFile<TextRange>, IsInnerDoc)> {
- if string_range.start() < self.prefix_len {
- return None;
- }
- string_range -= self.prefix_len;
-
- let mut file = self.inline_file;
- let mut inner_docs_start = self.inline_inner_docs_start;
- // Check whether the range is from the outline, the inline, or both.
- let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod {
- if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) {
- if string_range.end() <= first_inline.string_offset {
- // The range is completely in the outline.
- file = outline_mod_file;
- inner_docs_start = self.outline_inner_docs_start;
- &self.docs_source_map[..outline_mod_end]
- } else if string_range.start() >= first_inline.string_offset {
- // The range is completely in the inline.
- &self.docs_source_map[outline_mod_end..]
- } else {
- // The range is combined from the outline and the inline - cannot map it back.
- return None;
- }
- } else {
- // There is no inline.
- file = outline_mod_file;
- inner_docs_start = self.outline_inner_docs_start;
- &self.docs_source_map
- }
- } else {
- // There is no outline.
- &self.docs_source_map
- };
-
- let after_range =
- source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1;
- let after_range = &source_map[after_range..];
- let line = after_range.first()?;
- if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end())
- {
- // The range is combined from two lines - cannot map it back.
- return None;
- }
- let ast_range = string_range - line.string_offset + line.ast_offset;
- let is_inner = if inner_docs_start
- .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start)
- {
- IsInnerDoc::Yes
- } else {
- IsInnerDoc::No
- };
- Some((InFile::new(file, ast_range), is_inner))
- }
-
- #[inline]
- pub fn shift_by(&mut self, offset: TextSize) {
- self.prefix_len += offset;
- }
-
- pub fn prepend_str(&mut self, s: &str) {
- self.prefix_len += TextSize::of(s);
- self.docs.insert_str(0, s);
- }
-
- pub fn append_str(&mut self, s: &str) {
- self.docs.push_str(s);
- }
-
- pub fn append(&mut self, other: &Docs) {
- let other_offset = TextSize::of(&self.docs);
-
- assert!(
- self.outline_mod.is_none() && other.outline_mod.is_none(),
- "cannot merge `Docs` that have `outline_mod` set"
- );
- self.outline_mod = Some((self.inline_file, self.docs_source_map.len()));
- self.inline_file = other.inline_file;
- self.outline_inner_docs_start = self.inline_inner_docs_start;
- self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset);
-
- self.docs.push_str(&other.docs);
- self.docs_source_map.extend(other.docs_source_map.iter().map(
- |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine {
- ast_offset,
- string_offset: string_offset + other_offset,
- },
- ));
- }
-
- fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) {
- let Some((doc, offset)) = comment.doc_comment() else { return };
- self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent);
- }
-
- fn extend_with_doc_attr(&mut self, value: SyntaxToken, indent: &mut usize) {
- let Some(value) = ast::String::cast(value) else { return };
- let Some(value_offset) = value.text_range_between_quotes() else { return };
- let value_offset = value_offset.start();
- let Ok(value) = value.value() else { return };
- // FIXME: Handle source maps for escaped text.
- self.extend_with_doc_str(&value, value_offset, indent);
- }
-
- fn extend_with_doc_str(&mut self, doc: &str, mut offset_in_ast: TextSize, indent: &mut usize) {
- for line in doc.split('\n') {
- self.docs_source_map.push(DocsSourceMapLine {
- string_offset: TextSize::of(&self.docs),
- ast_offset: offset_in_ast,
- });
- offset_in_ast += TextSize::of(line) + TextSize::of("\n");
-
- let line = line.trim_end();
- if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) {
- // Empty lines are handled because `position()` returns `None` for them.
- *indent = std::cmp::min(*indent, line_indent);
- }
- self.docs.push_str(line);
- self.docs.push('\n');
- }
- }
-
- fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) {
- /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it.
- struct Guard<'a>(&'a mut Docs);
- impl Drop for Guard<'_> {
- fn drop(&mut self) {
- let Docs {
- docs,
- docs_source_map,
- outline_mod,
- inline_file: _,
- prefix_len: _,
- inline_inner_docs_start: _,
- outline_inner_docs_start: _,
- } = self.0;
- // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things,
- // and we may have temporarily broken the string's encoding.
- unsafe { docs.as_mut_vec() }.clear();
- // This is just to avoid panics down the road.
- docs_source_map.clear();
- *outline_mod = None;
- }
- }
-
- if self.docs.is_empty() {
- return;
- }
-
- let guard = Guard(self);
- let source_map = &mut guard.0.docs_source_map[start_source_map_index..];
- let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first()
- else {
- return;
- };
- // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2)
- // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into
- // consecutive to the previous line (which may have moved). Then at the end we truncate.
- let mut accumulated_offset = TextSize::new(0);
- for idx in 0..source_map.len() {
- let string_end_offset = source_map
- .get(idx + 1)
- .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset);
- let line_source = &mut source_map[idx];
- let line_docs =
- &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)];
- let line_docs_len = TextSize::of(line_docs);
- let indent_size = line_docs.char_indices().nth(indent).map_or_else(
- || TextSize::of(line_docs) - TextSize::of("\n"),
- |(offset, _)| TextSize::new(offset as u32),
- );
- unsafe { guard.0.docs.as_bytes_mut() }.copy_within(
- Range::<usize>::from(TextRange::new(
- line_source.string_offset + indent_size,
- string_end_offset,
- )),
- copy_into.into(),
- );
- copy_into += line_docs_len - indent_size;
-
- if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start
- && *inner_attrs_start == line_source.string_offset
- {
- *inner_attrs_start -= accumulated_offset;
- }
- // The removals in the string accumulate, but in the AST not, because it already points
- // to the beginning of each attribute.
- // Also, we need to shift the AST offset of every line, but the string offset of the first
- // line should not get shifted (in general, the shift for the string offset is by the
- // number of lines until the current one, excluding the current one).
- line_source.string_offset -= accumulated_offset;
- line_source.ast_offset += indent_size;
-
- accumulated_offset += indent_size;
- }
- // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things,
- // and we may have temporarily broken the string's encoding.
- unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into());
-
- std::mem::forget(guard);
- }
-
- fn remove_last_newline(&mut self) {
- self.docs.truncate(self.docs.len().saturating_sub(1));
- }
-
- fn shrink_to_fit(&mut self) {
- let Docs {
- docs,
- docs_source_map,
- outline_mod: _,
- inline_file: _,
- prefix_len: _,
- inline_inner_docs_start: _,
- outline_inner_docs_start: _,
- } = self;
- docs.shrink_to_fit();
- docs_source_map.shrink_to_fit();
- }
-}
-
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct DeriveInfo {
pub trait_name: Symbol,
pub helpers: Box<[Symbol]>,
}
-fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infallible> {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: ast::Meta) -> ControlFlow<Infallible> {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
{
for atom in DocAtom::parse(tt) {
match atom {
@@ -776,85 +547,15 @@ fn extract_doc_aliases(result: &mut Vec<Symbol>, attr: Meta) -> ControlFlow<Infa
ControlFlow::Continue(())
}
-fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: Meta) -> ControlFlow<Infallible> {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("cfg")
+fn extract_cfgs(result: &mut Vec<CfgExpr>, attr: ast::Meta) -> ControlFlow<Infallible> {
+ if let ast::Meta::CfgMeta(attr) = attr
+ && let Some(cfg_predicate) = attr.cfg_predicate()
{
- result.push(CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable()));
+ result.push(CfgExpr::parse_from_ast(cfg_predicate));
}
ControlFlow::Continue(())
}
-fn extract_docs<'a>(
- get_cfg_options: &dyn Fn() -> &'a CfgOptions,
- source: InFile<ast::AnyHasAttrs>,
- outer_mod_decl: Option<InFile<ast::Module>>,
- inner_attrs_node: Option<SyntaxNode>,
-) -> Option<Box<Docs>> {
- let mut result = Docs {
- docs: String::new(),
- docs_source_map: Vec::new(),
- outline_mod: None,
- inline_file: source.file_id,
- prefix_len: TextSize::new(0),
- inline_inner_docs_start: None,
- outline_inner_docs_start: None,
- };
-
- let mut cfg_options = None;
- let mut extend_with_attrs =
- |result: &mut Docs, node: &SyntaxNode, expect_inner_attrs, indent: &mut usize| {
- expand_cfg_attr_with_doc_comments::<_, Infallible>(
- AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr {
- Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs,
- Either::Right(comment) => comment.kind().doc.is_some_and(|kind| {
- (kind == ast::CommentPlacement::Inner) == expect_inner_attrs
- }),
- }),
- || cfg_options.get_or_insert_with(get_cfg_options),
- |attr| {
- match attr {
- Either::Right(doc_comment) => {
- result.extend_with_doc_comment(doc_comment, indent)
- }
- Either::Left((attr, _, _, _)) => match attr {
- // FIXME: Handle macros: `#[doc = concat!("foo", "bar")]`.
- Meta::NamedKeyValue {
- name: Some(name), value: Some(value), ..
- } if name.text() == "doc" => {
- result.extend_with_doc_attr(value, indent);
- }
- _ => {}
- },
- }
- ControlFlow::Continue(())
- },
- );
- };
-
- if let Some(outer_mod_decl) = outer_mod_decl {
- let mut indent = usize::MAX;
- extend_with_attrs(&mut result, outer_mod_decl.value.syntax(), false, &mut indent);
- result.remove_indent(indent, 0);
- result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len()));
- }
-
- let inline_source_map_start = result.docs_source_map.len();
- let mut indent = usize::MAX;
- extend_with_attrs(&mut result, source.value.syntax(), false, &mut indent);
- if let Some(inner_attrs_node) = &inner_attrs_node {
- result.inline_inner_docs_start = Some(TextSize::of(&result.docs));
- extend_with_attrs(&mut result, inner_attrs_node, true, &mut indent);
- }
- result.remove_indent(indent, inline_source_map_start);
-
- result.remove_last_newline();
-
- result.shrink_to_fit();
-
- if result.docs.is_empty() { None } else { Some(Box::new(result)) }
-}
-
#[salsa::tracked]
impl AttrFlags {
#[salsa::tracked]
@@ -881,7 +582,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| match_attr_flags(&mut attr_flags, attr),
+ |attr, _| match_attr_flags(&mut attr_flags, attr),
);
attr_flags
})
@@ -918,7 +619,7 @@ impl AttrFlags {
let lifetimes_source = HasChildSource::<LocalLifetimeParamId>::child_source(&def, db);
for (lifetime_id, lifetime) in lifetimes_source.value.iter() {
let mut attr_flags = AttrFlags::empty();
- expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _, _, _| {
+ expand_cfg_attr(lifetime.attrs(), &mut cfg_options, |attr, _| {
match_attr_flags(&mut attr_flags, attr)
});
if !attr_flags.is_empty() {
@@ -930,7 +631,7 @@ impl AttrFlags {
HasChildSource::<LocalTypeOrConstParamId>::child_source(&def, db);
for (type_or_const_id, type_or_const) in type_and_consts_source.value.iter() {
let mut attr_flags = AttrFlags::empty();
- expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _, _, _| {
+ expand_cfg_attr(type_or_const.attrs(), &mut cfg_options, |attr, _| {
match_attr_flags(&mut attr_flags, attr)
});
if !attr_flags.is_empty() {
@@ -969,11 +670,10 @@ impl AttrFlags {
let result = expand_cfg_attr(
attrs,
|| cfg_options,
- |attr, _, _, _| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("cfg")
- && let cfg =
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(&tt).peekable())
+ |attr, _| {
+ if let ast::Meta::CfgMeta(attr) = attr
+ && let Some(cfg_predicate) = attr.cfg_predicate()
+ && let cfg = CfgExpr::parse_from_ast(cfg_predicate)
&& cfg_options.check(&cfg) == Some(false)
{
ControlFlow::Break(cfg)
@@ -1005,10 +705,9 @@ impl AttrFlags {
#[salsa::tracked]
fn lang_item(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
collect_attrs(db, owner, |attr| {
- if let Meta::NamedKeyValue { name: Some(name), value: Some(value), .. } = attr
- && name.text() == "lang"
- && let Some(value) = ast::String::cast(value)
- && let Ok(value) = value.value()
+ if let ast::Meta::KeyValueMeta(attr) = attr
+ && attr.path().is1("lang")
+ && let Some(value) = attr.value_string()
{
ControlFlow::Break(Symbol::intern(&value))
} else {
@@ -1031,13 +730,40 @@ impl AttrFlags {
fn repr(db: &dyn DefDatabase, owner: AdtId) -> Option<ReprOptions> {
let mut result = None;
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("repr")
- && let Some(repr) = parse_repr_tt(&tt)
+ 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)
+ {
+ 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(ReprOptions {
+ scalable: Some(rustc_abi::ScalableElt::Container),
+ ..ReprOptions::default()
+ });
+ }
+
+ if let Some(current) = current {
match &mut result {
- Some(existing) => merge_repr(existing, repr),
- None => result = Some(repr),
+ Some(existing) => merge_repr(existing, current),
+ None => result = Some(current),
}
}
ControlFlow::Continue(())
@@ -1053,8 +779,9 @@ impl AttrFlags {
owner: FunctionId,
) -> Option<Box<[u32]>> {
let result = collect_attrs(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("rustc_legacy_const_generics")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("rustc_legacy_const_generics")
+ && let Some(tt) = attr.token_tree()
{
let result = parse_rustc_legacy_const_generics(tt);
ControlFlow::Break(result)
@@ -1069,7 +796,7 @@ impl AttrFlags {
#[salsa::tracked(returns(ref))]
pub fn doc_html_root_url(db: &dyn DefDatabase, krate: Crate) -> Option<SmolStr> {
let root_file_id = krate.root_file_id(db);
- let syntax = db.parse(root_file_id).tree();
+ let syntax = root_file_id.parse(db).tree();
let extra_crate_attrs =
parse_extra_crate_attrs(db, krate).into_iter().flat_map(|src| src.attrs());
@@ -1077,9 +804,10 @@ impl AttrFlags {
expand_cfg_attr(
extra_crate_attrs.chain(syntax.attrs()),
|| cfg_options.get_or_insert(krate.cfg_options(db)),
- |attr, _, _, _| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+ |attr, _| {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
&& let Some(result) = DocAtom::parse(tt).into_iter().find_map(|atom| {
if let DocAtom::KeyValue { key, value } = atom
&& key == "html_root_url"
@@ -1110,8 +838,9 @@ impl AttrFlags {
fn target_features(db: &dyn DefDatabase, owner: FunctionId) -> FxHashSet<Symbol> {
let mut result = FxHashSet::default();
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("target_feature")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("target_feature")
+ && let Some(tt) = attr.token_tree()
{
let mut tt = TokenTreeChildren::new(&tt);
while let Some(NodeOrToken::Token(enable_ident)) = tt.next()
@@ -1158,9 +887,11 @@ impl AttrFlags {
) -> RustcLayoutScalarValidRange {
let mut result = RustcLayoutScalarValidRange::default();
collect_attrs::<Infallible>(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && let path = attr.path()
&& (path.is1("rustc_layout_scalar_valid_range_start")
|| path.is1("rustc_layout_scalar_valid_range_end"))
+ && let Some(tt) = attr.token_tree()
&& let tt = TokenTreeChildren::new(&tt)
&& let Ok(NodeOrToken::Token(value)) = Itertools::exactly_one(tt)
&& let Some(value) = ast::IntNumber::cast(value)
@@ -1208,7 +939,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| extract_doc_aliases(&mut result, attr),
+ |attr, _| extract_doc_aliases(&mut result, attr),
);
result.into_boxed_slice()
})
@@ -1250,7 +981,7 @@ impl AttrFlags {
expand_cfg_attr(
field.value.attrs(),
|| cfg_options,
- |attr, _, _, _| extract_cfgs(&mut result, attr),
+ |attr, _| extract_cfgs(&mut result, attr),
);
match result.len() {
0 => None,
@@ -1271,8 +1002,9 @@ impl AttrFlags {
#[salsa::tracked]
fn doc_keyword(db: &dyn DefDatabase, owner: ModuleId) -> Option<Symbol> {
collect_attrs(db, AttrDefId::ModuleId(owner), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.is1("doc")
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && attr.path().is1("doc")
+ && let Some(tt) = attr.token_tree()
{
for atom in DocAtom::parse(tt) {
if let DocAtom::KeyValue { key, value } = atom
@@ -1295,7 +1027,15 @@ impl AttrFlags {
// Note: we don't have to pass down `_extra_crate_attrs` here, since `extract_docs`
// does not handle crate-level attributes related to docs.
// See: https://doc.rust-lang.org/rustdoc/write-documentation/the-doc-attribute.html#at-the-crate-level
- extract_docs(&|| krate.cfg_options(db), source, outer_mod_decl, inner_attrs_node)
+ self::docs::extract_docs(
+ db,
+ krate,
+ &|| resolver_for_attr_def_id(db, owner),
+ &|| krate.cfg_options(db),
+ source,
+ outer_mod_decl,
+ inner_attrs_node,
+ )
}
#[inline]
@@ -1308,8 +1048,17 @@ impl AttrFlags {
db: &dyn DefDatabase,
variant: VariantId,
) -> ArenaMap<LocalFieldId, Option<Box<Docs>>> {
+ let krate = variant.module(db).krate(db);
collect_field_attrs(db, variant, |cfg_options, field| {
- extract_docs(&|| cfg_options, field, None, None)
+ self::docs::extract_docs(
+ db,
+ krate,
+ &|| variant.resolver(db),
+ &|| cfg_options,
+ field,
+ None,
+ None,
+ )
})
}
}
@@ -1325,12 +1074,10 @@ impl AttrFlags {
#[salsa::tracked(returns(ref))]
fn derive_info(db: &dyn DefDatabase, owner: MacroId) -> Option<DeriveInfo> {
collect_attrs(db, owner.into(), |attr| {
- if let Meta::TokenTree { path, tt } = attr
- && path.segments.len() == 1
- && matches!(
- path.segments[0].text(),
- "proc_macro_derive" | "rustc_builtin_macro"
- )
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && (attr.path().is1("proc_macro_derive")
+ || attr.path().is1("rustc_builtin_macro"))
+ && let Some(tt) = attr.token_tree()
&& let mut tt = TokenTreeChildren::new(&tt)
&& let Some(NodeOrToken::Token(trait_name)) = tt.next()
&& trait_name.kind().is_any_identifier()
@@ -1361,10 +1108,45 @@ impl AttrFlags {
})
}
}
+
+ pub fn unstable_feature(self, db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ if !self.contains(AttrFlags::IS_UNSTABLE) {
+ return None;
+ }
+
+ return unstable_feature(db, owner);
+
+ #[salsa::tracked]
+ fn unstable_feature(db: &dyn DefDatabase, owner: AttrDefId) -> Option<Symbol> {
+ collect_attrs(db, owner, |attr| {
+ if let ast::Meta::TokenTreeMeta(attr) = attr
+ && let path = attr.path()
+ && path.is1("unstable")
+ && let Some(tt) = attr.token_tree()
+ {
+ let mut tt = TokenTreeChildren::new(&tt);
+ // Technically the `feature = "..."` always comes first, but it's not a requirement.
+ while let Some(token) = tt.next() {
+ if let NodeOrToken::Token(token) = token
+ && token.text() == "feature"
+ && let Some(NodeOrToken::Token(eq)) = tt.next()
+ && eq.kind() == T![=]
+ && let Some(NodeOrToken::Token(feature)) = tt.next()
+ && let Some(feature) = ast::String::cast(feature)
+ && let Ok(feature) = feature.value()
+ {
+ return ControlFlow::Break(Symbol::intern(&feature));
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ })
+ }
+ }
}
fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
- let ReprOptions { int, align, pack, flags, field_shuffle_seed: _ } = this;
+ let ReprOptions { int, align, pack, flags, scalable, field_shuffle_seed: _ } = this;
flags.insert(other.flags);
*align = (*align).max(other.align);
*pack = match (*pack, other.pack) {
@@ -1374,6 +1156,9 @@ fn merge_repr(this: &mut ReprOptions, other: ReprOptions) {
if other.int.is_some() {
*int = other.int;
}
+ if other.scalable.is_some() {
+ *scalable = other.scalable;
+ }
}
fn parse_repr_tt(tt: &ast::TokenTree) -> Option<ReprOptions> {
@@ -1537,151 +1322,13 @@ fn next_doc_expr(it: &mut Peekable<TokenTreeChildren>) -> Option<DocAtom> {
#[cfg(test)]
mod tests {
- use expect_test::expect;
- use hir_expand::InFile;
use test_fixture::WithFixture;
- use tt::{TextRange, TextSize};
use crate::AttrDefId;
- use crate::attrs::{AttrFlags, Docs, IsInnerDoc};
+ use crate::attrs::AttrFlags;
use crate::test_db::TestDB;
#[test]
- fn docs() {
- let (_db, file_id) = TestDB::with_single_file("");
- let mut docs = Docs {
- docs: String::new(),
- docs_source_map: Vec::new(),
- outline_mod: None,
- inline_file: file_id.into(),
- prefix_len: TextSize::new(0),
- inline_inner_docs_start: None,
- outline_inner_docs_start: None,
- };
- let mut indent = usize::MAX;
-
- let outer = " foo\n\tbar baz";
- let mut ast_offset = TextSize::new(123);
- for line in outer.split('\n') {
- docs.extend_with_doc_str(line, ast_offset, &mut indent);
- ast_offset += TextSize::of(line) + TextSize::of("\n");
- }
-
- docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs));
- ast_offset += TextSize::new(123);
- let inner = " bar \n baz";
- for line in inner.split('\n') {
- docs.extend_with_doc_str(line, ast_offset, &mut indent);
- ast_offset += TextSize::of(line) + TextSize::of("\n");
- }
-
- assert_eq!(indent, 1);
- expect![[r#"
- [
- DocsSourceMapLine {
- string_offset: 0,
- ast_offset: 123,
- },
- DocsSourceMapLine {
- string_offset: 5,
- ast_offset: 128,
- },
- DocsSourceMapLine {
- string_offset: 15,
- ast_offset: 261,
- },
- DocsSourceMapLine {
- string_offset: 20,
- ast_offset: 267,
- },
- ]
- "#]]
- .assert_debug_eq(&docs.docs_source_map);
-
- docs.remove_indent(indent, 0);
-
- assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13)));
-
- assert_eq!(docs.docs, "foo\nbar baz\nbar\nbaz\n");
- expect![[r#"
- [
- DocsSourceMapLine {
- string_offset: 0,
- ast_offset: 124,
- },
- DocsSourceMapLine {
- string_offset: 4,
- ast_offset: 129,
- },
- DocsSourceMapLine {
- string_offset: 13,
- ast_offset: 262,
- },
- DocsSourceMapLine {
- string_offset: 17,
- ast_offset: 268,
- },
- ]
- "#]]
- .assert_debug_eq(&docs.docs_source_map);
-
- docs.append(&docs.clone());
- docs.prepend_str("prefix---");
- assert_eq!(docs.docs, "prefix---foo\nbar baz\nbar\nbaz\nfoo\nbar baz\nbar\nbaz\n");
- expect![[r#"
- [
- DocsSourceMapLine {
- string_offset: 0,
- ast_offset: 124,
- },
- DocsSourceMapLine {
- string_offset: 4,
- ast_offset: 129,
- },
- DocsSourceMapLine {
- string_offset: 13,
- ast_offset: 262,
- },
- DocsSourceMapLine {
- string_offset: 17,
- ast_offset: 268,
- },
- DocsSourceMapLine {
- string_offset: 21,
- ast_offset: 124,
- },
- DocsSourceMapLine {
- string_offset: 25,
- ast_offset: 129,
- },
- DocsSourceMapLine {
- string_offset: 34,
- ast_offset: 262,
- },
- DocsSourceMapLine {
- string_offset: 38,
- ast_offset: 268,
- },
- ]
- "#]]
- .assert_debug_eq(&docs.docs_source_map);
-
- let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end));
- let in_file = |range| InFile::new(file_id.into(), range);
- assert_eq!(docs.find_ast_range(range(0, 2)), None);
- assert_eq!(docs.find_ast_range(range(8, 10)), None);
- assert_eq!(
- docs.find_ast_range(range(9, 10)),
- Some((in_file(range(124, 125)), IsInnerDoc::No))
- );
- assert_eq!(docs.find_ast_range(range(20, 23)), None);
- assert_eq!(
- docs.find_ast_range(range(23, 25)),
- Some((in_file(range(263, 265)), IsInnerDoc::Yes))
- );
- }
-
- #[test]
fn crate_attrs() {
let fixture = r#"
//- /lib.rs crate:foo crate-attr:no_std crate-attr:cfg(target_arch="x86")
diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs
new file mode 100644
index 0000000000..9a715b1968
--- /dev/null
+++ b/crates/hir-def/src/attrs/docs.rs
@@ -0,0 +1,736 @@
+//! Documentation extraction and source mapping.
+//!
+//! This module handles the extraction and processing of doc comments and `#[doc = "..."]`
+//! attributes, including macro expansion for `#[doc = macro!()]` patterns.
+//! It builds a concatenated string of the full docs as well as a source map
+//! to map it back to AST (which is needed for things like resolving links in doc comments
+//! and highlight injection).
+
+use std::{
+ convert::Infallible,
+ ops::{ControlFlow, Range},
+};
+
+use base_db::Crate;
+use cfg::CfgOptions;
+use either::Either;
+use hir_expand::{
+ AstId, ExpandTo, HirFileId, InFile,
+ attrs::{AstPathExt, expand_cfg_attr_with_doc_comments},
+ mod_path::ModPath,
+ span_map::SpanMap,
+};
+use span::AstIdMap;
+use syntax::{
+ AstNode, AstToken, SyntaxNode,
+ ast::{self, AttrDocCommentIter, IsString},
+};
+use tt::{TextRange, TextSize};
+
+use crate::{db::DefDatabase, macro_call_as_call_id, nameres::MacroSubNs, resolver::Resolver};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(crate) struct DocsSourceMapLine {
+ /// The offset in [`Docs::docs`].
+ string_offset: TextSize,
+ /// The offset in the AST of the text. `None` for macro-expanded doc strings
+ /// where we cannot provide a faithful source mapping.
+ ast_offset: Option<TextSize>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Docs {
+ /// The concatenated string of all `#[doc = "..."]` attributes and documentation comments.
+ docs: String,
+ /// A sorted map from an offset in `docs` to an offset in the source code.
+ docs_source_map: Vec<DocsSourceMapLine>,
+ /// If the item is an outlined module (`mod foo;`), `docs_source_map` store the concatenated
+ /// list of the outline and inline docs (outline first). Then, this field contains the [`HirFileId`]
+ /// of the outline declaration, and the index in `docs` from which the inline docs
+ /// begin.
+ outline_mod: Option<(HirFileId, usize)>,
+ inline_file: HirFileId,
+ /// The size the prepended prefix, which does not map to real doc comments.
+ prefix_len: TextSize,
+ /// The offset in `docs` from which the docs are inner attributes/comments.
+ inline_inner_docs_start: Option<TextSize>,
+ /// Like `inline_inner_docs_start`, but for `outline_mod`. This can happen only when merging `Docs`
+ /// (as outline modules don't have inner attributes).
+ outline_inner_docs_start: Option<TextSize>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum IsInnerDoc {
+ No,
+ Yes,
+}
+
+impl IsInnerDoc {
+ #[inline]
+ pub fn yes(self) -> bool {
+ self == IsInnerDoc::Yes
+ }
+}
+
+impl Docs {
+ #[inline]
+ pub fn docs(&self) -> &str {
+ &self.docs
+ }
+
+ #[inline]
+ pub fn into_docs(self) -> String {
+ self.docs
+ }
+
+ pub fn find_ast_range(
+ &self,
+ mut string_range: TextRange,
+ ) -> Option<(InFile<TextRange>, IsInnerDoc)> {
+ if string_range.start() < self.prefix_len {
+ return None;
+ }
+ string_range -= self.prefix_len;
+
+ let mut file = self.inline_file;
+ let mut inner_docs_start = self.inline_inner_docs_start;
+ // Check whether the range is from the outline, the inline, or both.
+ let source_map = if let Some((outline_mod_file, outline_mod_end)) = self.outline_mod {
+ if let Some(first_inline) = self.docs_source_map.get(outline_mod_end) {
+ if string_range.end() <= first_inline.string_offset {
+ // The range is completely in the outline.
+ file = outline_mod_file;
+ inner_docs_start = self.outline_inner_docs_start;
+ &self.docs_source_map[..outline_mod_end]
+ } else if string_range.start() >= first_inline.string_offset {
+ // The range is completely in the inline.
+ &self.docs_source_map[outline_mod_end..]
+ } else {
+ // The range is combined from the outline and the inline - cannot map it back.
+ return None;
+ }
+ } else {
+ // There is no inline.
+ file = outline_mod_file;
+ inner_docs_start = self.outline_inner_docs_start;
+ &self.docs_source_map
+ }
+ } else {
+ // There is no outline.
+ &self.docs_source_map
+ };
+
+ let after_range =
+ source_map.partition_point(|line| line.string_offset <= string_range.start()) - 1;
+ let after_range = &source_map[after_range..];
+ let line = after_range.first()?;
+ // Unmapped lines (from macro-expanded docs) cannot be mapped back to AST.
+ let ast_offset = line.ast_offset?;
+ if after_range.get(1).is_some_and(|next_line| next_line.string_offset < string_range.end())
+ {
+ // The range is combined from two lines - cannot map it back.
+ return None;
+ }
+ let ast_range = string_range - line.string_offset + ast_offset;
+ let is_inner = if inner_docs_start
+ .is_some_and(|inner_docs_start| string_range.start() >= inner_docs_start)
+ {
+ IsInnerDoc::Yes
+ } else {
+ IsInnerDoc::No
+ };
+ Some((InFile::new(file, ast_range), is_inner))
+ }
+
+ #[inline]
+ pub fn shift_by(&mut self, offset: TextSize) {
+ self.prefix_len += offset;
+ }
+
+ pub fn prepend_str(&mut self, s: &str) {
+ self.prefix_len += TextSize::of(s);
+ self.docs.insert_str(0, s);
+ }
+
+ pub fn append_str(&mut self, s: &str) {
+ self.docs.push_str(s);
+ }
+
+ pub fn append(&mut self, other: &Docs) {
+ let other_offset = TextSize::of(&self.docs);
+
+ assert!(
+ self.outline_mod.is_none() && other.outline_mod.is_none(),
+ "cannot merge `Docs` that have `outline_mod` set"
+ );
+ self.outline_mod = Some((self.inline_file, self.docs_source_map.len()));
+ self.inline_file = other.inline_file;
+ self.outline_inner_docs_start = self.inline_inner_docs_start;
+ self.inline_inner_docs_start = other.inline_inner_docs_start.map(|it| it + other_offset);
+
+ self.docs.push_str(&other.docs);
+ self.docs_source_map.extend(other.docs_source_map.iter().map(
+ |&DocsSourceMapLine { string_offset, ast_offset }| DocsSourceMapLine {
+ ast_offset,
+ string_offset: string_offset + other_offset,
+ },
+ ));
+ }
+
+ fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut usize) {
+ let Some((doc, offset)) = comment.doc_comment() else { return };
+ self.extend_with_doc_str(doc, comment.syntax().text_range().start() + offset, indent);
+ }
+
+ fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut usize) {
+ let Some(value_offset) = value.text_range_between_quotes() else { return };
+ let value_offset = value_offset.start();
+ let Ok(value) = value.value() else { return };
+ // FIXME: Handle source maps for escaped text.
+ self.extend_with_doc_str(&value, value_offset, indent);
+ }
+
+ pub(crate) fn extend_with_doc_str(
+ &mut self,
+ doc: &str,
+ offset_in_ast: TextSize,
+ indent: &mut usize,
+ ) {
+ self.push_doc_lines(doc, Some(offset_in_ast), indent);
+ }
+
+ fn extend_with_unmapped_doc_str(&mut self, doc: &str, indent: &mut usize) {
+ self.push_doc_lines(doc, None, indent);
+ }
+
+ fn push_doc_lines(&mut self, doc: &str, mut ast_offset: Option<TextSize>, indent: &mut usize) {
+ for line in doc.split('\n') {
+ self.docs_source_map
+ .push(DocsSourceMapLine { string_offset: TextSize::of(&self.docs), ast_offset });
+ if let Some(ref mut offset) = ast_offset {
+ *offset += TextSize::of(line) + TextSize::of("\n");
+ }
+
+ let line = line.trim_end();
+ if let Some(line_indent) = line.chars().position(|ch| !ch.is_whitespace()) {
+ // Empty lines are handled because `position()` returns `None` for them.
+ *indent = std::cmp::min(*indent, line_indent);
+ }
+ self.docs.push_str(line);
+ self.docs.push('\n');
+ }
+ }
+
+ fn remove_indent(&mut self, indent: usize, start_source_map_index: usize) {
+ /// In case of panics, we want to avoid corrupted UTF-8 in `self.docs`, so we clear it.
+ struct Guard<'a>(&'a mut Docs);
+ impl Drop for Guard<'_> {
+ fn drop(&mut self) {
+ let Docs {
+ docs,
+ docs_source_map,
+ outline_mod,
+ inline_file: _,
+ prefix_len: _,
+ inline_inner_docs_start: _,
+ outline_inner_docs_start: _,
+ } = self.0;
+ // Don't use `String::clear()` here because it's not guaranteed to not do UTF-8-dependent things,
+ // and we may have temporarily broken the string's encoding.
+ unsafe { docs.as_mut_vec() }.clear();
+ // This is just to avoid panics down the road.
+ docs_source_map.clear();
+ *outline_mod = None;
+ }
+ }
+
+ if self.docs.is_empty() {
+ return;
+ }
+
+ let guard = Guard(self);
+ let source_map = &mut guard.0.docs_source_map[start_source_map_index..];
+ let Some(&DocsSourceMapLine { string_offset: mut copy_into, .. }) = source_map.first()
+ else {
+ return;
+ };
+ // We basically want to remove multiple ranges from a string. Doing this efficiently (without O(N^2)
+ // or allocations) requires unsafe. Basically, for each line, we copy the line minus the indent into
+ // consecutive to the previous line (which may have moved). Then at the end we truncate.
+ let mut accumulated_offset = TextSize::new(0);
+ for idx in 0..source_map.len() {
+ let string_end_offset = source_map
+ .get(idx + 1)
+ .map_or_else(|| TextSize::of(&guard.0.docs), |next_attr| next_attr.string_offset);
+ let line_source = &mut source_map[idx];
+ let line_docs =
+ &guard.0.docs[TextRange::new(line_source.string_offset, string_end_offset)];
+ let line_docs_len = TextSize::of(line_docs);
+ let indent_size = line_docs.char_indices().nth(indent).map_or_else(
+ || TextSize::of(line_docs) - TextSize::of("\n"),
+ |(offset, _)| TextSize::new(offset as u32),
+ );
+ unsafe { guard.0.docs.as_bytes_mut() }.copy_within(
+ Range::<usize>::from(TextRange::new(
+ line_source.string_offset + indent_size,
+ string_end_offset,
+ )),
+ copy_into.into(),
+ );
+ copy_into += line_docs_len - indent_size;
+
+ if let Some(inner_attrs_start) = &mut guard.0.inline_inner_docs_start
+ && *inner_attrs_start == line_source.string_offset
+ {
+ *inner_attrs_start -= accumulated_offset;
+ }
+ // The removals in the string accumulate, but in the AST not, because it already points
+ // to the beginning of each attribute.
+ // Also, we need to shift the AST offset of every line, but the string offset of the first
+ // line should not get shifted (in general, the shift for the string offset is by the
+ // number of lines until the current one, excluding the current one).
+ line_source.string_offset -= accumulated_offset;
+ if let Some(ref mut ast_offset) = line_source.ast_offset {
+ *ast_offset += indent_size;
+ }
+
+ accumulated_offset += indent_size;
+ }
+ // Don't use `String::truncate()` here because it's not guaranteed to not do UTF-8-dependent things,
+ // and we may have temporarily broken the string's encoding.
+ unsafe { guard.0.docs.as_mut_vec() }.truncate(copy_into.into());
+
+ std::mem::forget(guard);
+ }
+
+ fn remove_last_newline(&mut self) {
+ self.docs.truncate(self.docs.len().saturating_sub(1));
+ }
+
+ fn shrink_to_fit(&mut self) {
+ let Docs {
+ docs,
+ docs_source_map,
+ outline_mod: _,
+ inline_file: _,
+ prefix_len: _,
+ inline_inner_docs_start: _,
+ outline_inner_docs_start: _,
+ } = self;
+ docs.shrink_to_fit();
+ docs_source_map.shrink_to_fit();
+ }
+}
+
+struct DocMacroExpander<'db> {
+ db: &'db dyn DefDatabase,
+ krate: Crate,
+ recursion_depth: usize,
+ recursion_limit: usize,
+}
+
+struct DocExprSourceCtx<'db> {
+ resolver: Resolver<'db>,
+ file_id: HirFileId,
+ ast_id_map: &'db AstIdMap,
+ span_map: SpanMap,
+}
+
+fn expand_doc_expr_via_macro_pipeline<'db>(
+ expander: &mut DocMacroExpander<'db>,
+ source_ctx: &DocExprSourceCtx<'db>,
+ expr: ast::Expr,
+) -> Option<String> {
+ match expr {
+ ast::Expr::ParenExpr(paren_expr) => {
+ expand_doc_expr_via_macro_pipeline(expander, source_ctx, paren_expr.expr()?)
+ }
+ ast::Expr::Literal(literal) => match literal.kind() {
+ ast::LiteralKind::String(string) => string.value().ok().map(Into::into),
+ _ => None,
+ },
+ ast::Expr::MacroExpr(macro_expr) => {
+ let macro_call = macro_expr.macro_call()?;
+ let (expr, new_source_ctx) = expand_doc_macro_call(expander, source_ctx, macro_call)?;
+ // After expansion, the expr lives in the expansion file; use its source context.
+ expand_doc_expr_via_macro_pipeline(expander, &new_source_ctx, expr)
+ }
+ _ => None,
+ }
+}
+
+fn expand_doc_macro_call<'db>(
+ expander: &mut DocMacroExpander<'db>,
+ source_ctx: &DocExprSourceCtx<'db>,
+ macro_call: ast::MacroCall,
+) -> Option<(ast::Expr, DocExprSourceCtx<'db>)> {
+ if expander.recursion_depth >= expander.recursion_limit {
+ return None;
+ }
+
+ let path = macro_call.path()?;
+ let mod_path = ModPath::from_src(expander.db, path, &mut |range| {
+ source_ctx.span_map.span_for_range(range).ctx
+ })?;
+ let call_site = source_ctx.span_map.span_for_range(macro_call.syntax().text_range());
+ let ast_id = AstId::new(source_ctx.file_id, source_ctx.ast_id_map.ast_id(&macro_call));
+ let call_id = macro_call_as_call_id(
+ expander.db,
+ ast_id,
+ &mod_path,
+ call_site.ctx,
+ ExpandTo::Expr,
+ expander.krate,
+ |path| {
+ source_ctx.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang))
+ },
+ &mut |_, _| (),
+ )
+ .ok()?
+ .value?;
+
+ expander.recursion_depth += 1;
+ let parse = expander.db.parse_macro_expansion(call_id).value.0;
+ let expr = parse.cast::<ast::Expr>().map(|parse| parse.tree())?;
+ expander.recursion_depth -= 1;
+
+ // Build a new source context for the expansion file so that any further
+ // recursive expansion (e.g. a user macro expanding to `concat!(...)`)
+ // correctly resolves AstIds and spans in the expansion.
+ let expansion_file_id: HirFileId = call_id.into();
+ let new_source_ctx = DocExprSourceCtx {
+ resolver: source_ctx.resolver.clone(),
+ file_id: expansion_file_id,
+ ast_id_map: expander.db.ast_id_map(expansion_file_id),
+ span_map: expander.db.span_map(expansion_file_id),
+ };
+ Some((expr, new_source_ctx))
+}
+
+fn extend_with_attrs<'a, 'db>(
+ result: &mut Docs,
+ db: &'db dyn DefDatabase,
+ krate: Crate,
+ node: &SyntaxNode,
+ file_id: HirFileId,
+ expect_inner_attrs: bool,
+ indent: &mut usize,
+ get_cfg_options: &dyn Fn() -> &'a CfgOptions,
+ cfg_options: &mut Option<&'a CfgOptions>,
+ make_resolver: &dyn Fn() -> Resolver<'db>,
+) {
+ // Lazily initialised when we first encounter a `#[doc = macro!()]`.
+ let mut expander: Option<(DocMacroExpander<'db>, DocExprSourceCtx<'db>)> = None;
+
+ expand_cfg_attr_with_doc_comments::<_, Infallible>(
+ AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr {
+ Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs,
+ Either::Right(comment) => comment
+ .kind()
+ .doc
+ .is_some_and(|kind| (kind == ast::CommentPlacement::Inner) == expect_inner_attrs),
+ }),
+ || *cfg_options.get_or_insert_with(get_cfg_options),
+ |attr| {
+ match attr {
+ Either::Right(doc_comment) => result.extend_with_doc_comment(doc_comment, indent),
+ Either::Left((attr, _)) => match attr {
+ ast::Meta::KeyValueMeta(attr) if attr.path().is1("doc") => {
+ if let Some(value) = attr.expr() {
+ if let ast::Expr::Literal(value) = &value
+ && let ast::LiteralKind::String(value) = value.kind()
+ {
+ result.extend_with_doc_attr(value, indent);
+ } else {
+ let (exp, ctx) = expander.get_or_insert_with(|| {
+ let resolver = make_resolver();
+ let def_map = resolver.top_level_def_map();
+ let recursion_limit = def_map.recursion_limit() as usize;
+ (
+ DocMacroExpander {
+ db,
+ krate,
+ recursion_depth: 0,
+ recursion_limit,
+ },
+ DocExprSourceCtx {
+ resolver,
+ file_id,
+ ast_id_map: db.ast_id_map(file_id),
+ span_map: db.span_map(file_id),
+ },
+ )
+ });
+ if let Some(expanded) =
+ expand_doc_expr_via_macro_pipeline(exp, ctx, value)
+ {
+ result.extend_with_unmapped_doc_str(&expanded, indent);
+ }
+ }
+ }
+ }
+ _ => {}
+ },
+ }
+ ControlFlow::Continue(())
+ },
+ );
+}
+
+pub(crate) fn extract_docs<'a, 'db>(
+ db: &'db dyn DefDatabase,
+ krate: Crate,
+ resolver: &dyn Fn() -> Resolver<'db>,
+ get_cfg_options: &dyn Fn() -> &'a CfgOptions,
+ source: InFile<ast::AnyHasAttrs>,
+ outer_mod_decl: Option<InFile<ast::Module>>,
+ inner_attrs_node: Option<SyntaxNode>,
+) -> Option<Box<Docs>> {
+ let mut result = Docs {
+ docs: String::new(),
+ docs_source_map: Vec::new(),
+ outline_mod: None,
+ inline_file: source.file_id,
+ prefix_len: TextSize::new(0),
+ inline_inner_docs_start: None,
+ outline_inner_docs_start: None,
+ };
+
+ let mut cfg_options = None;
+
+ if let Some(outer_mod_decl) = outer_mod_decl {
+ let mut indent = usize::MAX;
+ // For outer docs (the `mod foo;` declaration), use the module's own resolver.
+ extend_with_attrs(
+ &mut result,
+ db,
+ krate,
+ outer_mod_decl.value.syntax(),
+ outer_mod_decl.file_id,
+ false,
+ &mut indent,
+ get_cfg_options,
+ &mut cfg_options,
+ resolver,
+ );
+ result.remove_indent(indent, 0);
+ result.outline_mod = Some((outer_mod_decl.file_id, result.docs_source_map.len()));
+ }
+
+ let inline_source_map_start = result.docs_source_map.len();
+ let mut indent = usize::MAX;
+ // For inline docs, use the item's own resolver.
+ extend_with_attrs(
+ &mut result,
+ db,
+ krate,
+ source.value.syntax(),
+ source.file_id,
+ false,
+ &mut indent,
+ get_cfg_options,
+ &mut cfg_options,
+ resolver,
+ );
+ if let Some(inner_attrs_node) = &inner_attrs_node {
+ result.inline_inner_docs_start = Some(TextSize::of(&result.docs));
+ extend_with_attrs(
+ &mut result,
+ db,
+ krate,
+ inner_attrs_node,
+ source.file_id,
+ true,
+ &mut indent,
+ get_cfg_options,
+ &mut cfg_options,
+ resolver,
+ );
+ }
+ result.remove_indent(indent, inline_source_map_start);
+
+ result.remove_last_newline();
+
+ result.shrink_to_fit();
+
+ if result.docs.is_empty() { None } else { Some(Box::new(result)) }
+}
+
+#[cfg(test)]
+mod tests {
+ use expect_test::expect;
+ use hir_expand::InFile;
+ use test_fixture::WithFixture;
+ use tt::{TextRange, TextSize};
+
+ use crate::test_db::TestDB;
+
+ use super::{Docs, IsInnerDoc};
+
+ #[test]
+ fn docs() {
+ let (_db, file_id) = TestDB::with_single_file("");
+ let mut docs = Docs {
+ docs: String::new(),
+ docs_source_map: Vec::new(),
+ outline_mod: None,
+ inline_file: file_id.into(),
+ prefix_len: TextSize::new(0),
+ inline_inner_docs_start: None,
+ outline_inner_docs_start: None,
+ };
+ let mut indent = usize::MAX;
+
+ let outer = " foo\n\tbar baz";
+ let mut ast_offset = TextSize::new(123);
+ for line in outer.split('\n') {
+ docs.extend_with_doc_str(line, ast_offset, &mut indent);
+ ast_offset += TextSize::of(line) + TextSize::of("\n");
+ }
+
+ docs.inline_inner_docs_start = Some(TextSize::of(&docs.docs));
+ ast_offset += TextSize::new(123);
+ let inner = " bar \n baz";
+ for line in inner.split('\n') {
+ docs.extend_with_doc_str(line, ast_offset, &mut indent);
+ ast_offset += TextSize::of(line) + TextSize::of("\n");
+ }
+
+ assert_eq!(indent, 1);
+ expect![[r#"
+ [
+ DocsSourceMapLine {
+ string_offset: 0,
+ ast_offset: Some(
+ 123,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 5,
+ ast_offset: Some(
+ 128,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 15,
+ ast_offset: Some(
+ 261,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 20,
+ ast_offset: Some(
+ 267,
+ ),
+ },
+ ]
+ "#]]
+ .assert_debug_eq(&docs.docs_source_map);
+
+ docs.remove_indent(indent, 0);
+
+ assert_eq!(docs.inline_inner_docs_start, Some(TextSize::new(13)));
+
+ assert_eq!(docs.docs, "foo\nbar baz\nbar\nbaz\n");
+ expect![[r#"
+ [
+ DocsSourceMapLine {
+ string_offset: 0,
+ ast_offset: Some(
+ 124,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 4,
+ ast_offset: Some(
+ 129,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 13,
+ ast_offset: Some(
+ 262,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 17,
+ ast_offset: Some(
+ 268,
+ ),
+ },
+ ]
+ "#]]
+ .assert_debug_eq(&docs.docs_source_map);
+
+ docs.append(&docs.clone());
+ docs.prepend_str("prefix---");
+ assert_eq!(docs.docs, "prefix---foo\nbar baz\nbar\nbaz\nfoo\nbar baz\nbar\nbaz\n");
+ expect![[r#"
+ [
+ DocsSourceMapLine {
+ string_offset: 0,
+ ast_offset: Some(
+ 124,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 4,
+ ast_offset: Some(
+ 129,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 13,
+ ast_offset: Some(
+ 262,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 17,
+ ast_offset: Some(
+ 268,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 21,
+ ast_offset: Some(
+ 124,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 25,
+ ast_offset: Some(
+ 129,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 34,
+ ast_offset: Some(
+ 262,
+ ),
+ },
+ DocsSourceMapLine {
+ string_offset: 38,
+ ast_offset: Some(
+ 268,
+ ),
+ },
+ ]
+ "#]]
+ .assert_debug_eq(&docs.docs_source_map);
+
+ let range = |start, end| TextRange::new(TextSize::new(start), TextSize::new(end));
+ let in_file = |range| InFile::new(file_id.into(), range);
+ assert_eq!(docs.find_ast_range(range(0, 2)), None);
+ assert_eq!(docs.find_ast_range(range(8, 10)), None);
+ assert_eq!(
+ docs.find_ast_range(range(9, 10)),
+ Some((in_file(range(124, 125)), IsInnerDoc::No))
+ );
+ assert_eq!(docs.find_ast_range(range(20, 23)), None);
+ assert_eq!(
+ docs.find_ast_range(range(23, 25)),
+ Some((in_file(range(263, 265)), IsInnerDoc::Yes))
+ );
+ }
+}
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 5d5d435398..9dd7768ead 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -1,5 +1,5 @@
//! Defines database & queries for name resolution.
-use base_db::{Crate, RootQueryDb, SourceDatabase};
+use base_db::{Crate, SourceDatabase};
use hir_expand::{
EditionedFileId, HirFileId, InFile, Lookup, MacroCallId, MacroDefId, MacroDefKind,
db::ExpandDatabase,
@@ -22,7 +22,7 @@ use crate::{
use salsa::plumbing::AsId;
#[query_group::query_group(InternDatabaseStorage)]
-pub trait InternDatabase: RootQueryDb {
+pub trait InternDatabase: SourceDatabase {
// region: items
#[salsa::interned]
fn intern_use(&self, loc: UseLoc) -> UseId;
diff --git a/crates/hir-def/src/dyn_map.rs b/crates/hir-def/src/dyn_map.rs
index 4308d0ef1c..c38ceccd1f 100644
--- a/crates/hir-def/src/dyn_map.rs
+++ b/crates/hir-def/src/dyn_map.rs
@@ -68,7 +68,7 @@ pub mod keys {
pub const MACRO_CALL: Key<ast::MacroCall, MacroCallId> = Key::new();
pub const ATTR_MACRO_CALL: Key<ast::Item, MacroCallId> = Key::new();
pub const DERIVE_MACRO_CALL: Key<
- ast::Attr,
+ ast::Meta,
(
AttrId,
/* derive() */ MacroCallId,
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index ca523622ec..497ed7d37f 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -520,7 +520,7 @@ impl ExpressionStore {
self.const_expr_origins().iter().map(|&(id, _)| id)
}
- /// Like [`Self::signature_const_expr_roots`], but also returns the origin
+ /// 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))
@@ -642,9 +642,7 @@ impl ExpressionStore {
self.walk_exprs_in_pat(*pat, &mut f);
f(*expr);
}
- Expr::Block { statements, tail, .. }
- | Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. } => {
+ Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, else_branch, pat, .. } => {
@@ -677,6 +675,9 @@ impl ExpressionStore {
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);
});
}
@@ -777,9 +778,7 @@ impl ExpressionStore {
Expr::Let { expr, .. } => {
f(*expr);
}
- Expr::Block { statements, tail, .. }
- | Expr::Unsafe { statements, tail, .. }
- | Expr::Async { statements, tail, .. } => {
+ Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
Statement::Let { initializer, else_branch, .. } => {
@@ -923,6 +922,20 @@ impl ExpressionStore {
None => const { &Arena::new() }.iter(),
}
}
+
+ /// The coroutine associated with a coroutine closure.
+ #[inline]
+ pub fn coroutine_for_closure(coroutine_closure: ExprId) -> ExprId {
+ // We keep the async closure exactly one expr before.
+ ExprId::from_raw(la_arena::RawIdx::from_u32(coroutine_closure.into_raw().into_u32() - 1))
+ }
+
+ /// The opposite of [`Self::coroutine_for_closure()`].
+ #[inline]
+ pub fn closure_for_coroutine(coroutine: ExprId) -> ExprId {
+ // We keep the async closure exactly one expr before.
+ ExprId::from_raw(la_arena::RawIdx::from_u32(coroutine.into_raw().into_u32() + 1))
+ }
}
impl Index<ExprId> for ExpressionStore {
diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs
index 0c8320369f..6be3e49a70 100644
--- a/crates/hir-def/src/expr_store/body.rs
+++ b/crates/hir-def/src/expr_store/body.rs
@@ -133,7 +133,7 @@ impl Body {
expr: ExprId,
edition: Edition,
) -> String {
- pretty::print_expr_hir(db, self, owner, expr, edition)
+ pretty::print_expr_hir(db, self, owner.into(), expr, edition)
}
pub fn pretty_print_pat(
@@ -144,7 +144,7 @@ impl Body {
oneline: bool,
edition: Edition,
) -> String {
- pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
+ pretty::print_pat_hir(db, self, owner.into(), pat, oneline, edition)
}
}
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 74006c6037..04437a59ac 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -46,8 +46,9 @@ use crate::{
},
hir::{
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
- Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
- RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams,
+ CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability,
+ OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, Statement,
+ generics::GenericParams,
},
item_scope::BuiltinShadowMode,
item_tree::FieldsShape,
@@ -944,12 +945,19 @@ impl<'db> ExprCollector<'db> {
})
}
- /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
- /// non-captured patterns such as wildcards (to ensure consistent drop order).
- fn lower_async_fn(&mut self, params: &mut Vec<PatId>, body: ExprId) -> ExprId {
+ /// Lowers a desugared coroutine body after moving all of the arguments
+ /// 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(
+ &mut self,
+ params: &mut [PatId],
+ body: ExprId,
+ coroutine_source: CoroutineSource,
+ ) -> ExprId {
let mut statements = Vec::new();
for param in params {
- let name = match self.store.pats[*param] {
+ let (name, hygiene) = match self.store.pats[*param] {
Pat::Bind { id, .. }
if matches!(
self.store.bindings[id].mode,
@@ -961,14 +969,16 @@ impl<'db> ExprCollector<'db> {
}
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].name.clone(), self.store.bindings[id].hygiene)
}
- _ => self.generate_new_name(),
+ _ => (self.generate_new_name(), HygieneId::ROOT),
};
- let binding_id =
- self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
+ 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()));
+ if !hygiene.is_root() {
+ self.store.ident_hygiene.insert(expr.into(), hygiene);
+ }
statements.push(Statement::Let {
pat: *param,
type_ref: None,
@@ -978,23 +988,54 @@ impl<'db> ExprCollector<'db> {
*param = pat_id;
}
- self.alloc_expr_desugared(Expr::Async {
- id: None,
- statements: statements.into_boxed_slice(),
- tail: Some(body),
- })
+ let async_ = self.async_block(
+ 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,
+ // we want to make sure that we avoid unnecessarily moving captures, or else
+ // all async closures would default to `FnOnce` as their calling mode.
+ CaptureBy::Ref,
+ None,
+ statements.into_boxed_slice(),
+ Some(body),
+ );
+ // It's important that this comes last, see the lowering of async closures for why.
+ self.alloc_expr_desugared(async_)
+ }
+
+ fn async_block(
+ &mut self,
+ source: CoroutineSource,
+ capture_by: CaptureBy,
+ id: Option<BlockId>,
+ statements: Box<[Statement]>,
+ tail: Option<ExprId>,
+ ) -> Expr {
+ let block = self.alloc_expr_desugared(Expr::Block { label: None, id, statements, tail });
+ Expr::Closure {
+ args: Box::default(),
+ arg_types: Box::default(),
+ ret_type: None,
+ body: block,
+ closure_kind: ClosureKind::AsyncBlock { source },
+ capture_by,
+ }
}
fn collect(
&mut self,
- params: &mut Vec<PatId>,
+ params: &mut [PatId],
expr: Option<ast::Expr>,
awaitable: Awaitable,
) -> 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_fn(params, body) } else { body }
+ if awaitable == Awaitable::Yes {
+ this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn)
+ } else {
+ body
+ }
})
}
@@ -1126,7 +1167,7 @@ impl<'db> ExprCollector<'db> {
self.desugar_try_block(e, result_type)
}
Some(ast::BlockModifier::Unsafe(_)) => {
- self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
+ self.collect_block_(e, |_, id, statements, tail| Expr::Unsafe {
id,
statements,
tail,
@@ -1136,7 +1177,7 @@ impl<'db> ExprCollector<'db> {
let label_hygiene = self.hygiene_id_for(label.syntax().text_range());
let label_id = self.collect_label(label);
self.with_labeled_rib(label_id, label_hygiene, |this| {
- this.collect_block_(e, |id, statements, tail| Expr::Block {
+ this.collect_block_(e, |_, id, statements, tail| Expr::Block {
id,
statements,
tail,
@@ -1145,12 +1186,18 @@ impl<'db> ExprCollector<'db> {
})
}
Some(ast::BlockModifier::Async(_)) => {
+ 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, |id, statements, tail| Expr::Async {
- id,
- statements,
- tail,
+ this.collect_block_(e, |this, id, statements, tail| {
+ this.async_block(
+ CoroutineSource::Block,
+ capture_by,
+ id,
+ statements,
+ tail,
+ )
})
})
})
@@ -1378,9 +1425,11 @@ impl<'db> ExprCollector<'db> {
}
}
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
- this.with_binding_owner(|this| {
+ 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);
@@ -1406,7 +1455,7 @@ impl<'db> ExprCollector<'db> {
} else {
Awaitable::No("non-async closure")
};
- let body = this
+ let mut body = this
.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
let closure_kind = if this.is_lowering_coroutine {
@@ -1417,7 +1466,16 @@ impl<'db> ExprCollector<'db> {
};
ClosureKind::Coroutine(movability)
} else if e.async_token().is_some() {
- ClosureKind::Async
+ // 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(
+ &mut args,
+ body,
+ CoroutineSource::Closure,
+ );
+ body_is_bindings_owner = true;
+
+ ClosureKind::AsyncClosure
} else {
ClosureKind::Closure
};
@@ -1425,7 +1483,7 @@ impl<'db> ExprCollector<'db> {
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
this.is_lowering_coroutine = prev_is_lowering_coroutine;
this.current_try_block = prev_try_block;
- this.alloc_expr(
+ let closure = this.alloc_expr(
Expr::Closure {
args: args.into(),
arg_types: arg_types.into(),
@@ -1435,7 +1493,9 @@ impl<'db> ExprCollector<'db> {
capture_by,
},
syntax_ptr,
- )
+ );
+
+ (if body_is_bindings_owner { body } else { closure }, closure)
})
}),
ast::Expr::BinExpr(e) => {
@@ -1465,7 +1525,15 @@ impl<'db> ExprCollector<'db> {
match kind {
ArrayExprKind::ElementList(e) => {
- let elements = e.map(|expr| self.collect_expr(expr)).collect();
+ let elements = e
+ .filter_map(|expr| {
+ if self.check_cfg(&expr) {
+ Some(self.collect_expr(expr))
+ } else {
+ None
+ }
+ })
+ .collect();
self.alloc_expr(Expr::Array(Array::ElementList { elements }), syntax_ptr)
}
ArrayExprKind::Repeat { initializer, repeat } => {
@@ -1729,13 +1797,24 @@ impl<'db> ExprCollector<'db> {
}
}
- fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ /// The callback should return two exprs: the first is the bindings owner, the second is the expr to return.
+ fn with_binding_owner_and_return(
+ &mut self,
+ create_expr: impl FnOnce(&mut Self) -> (ExprId, ExprId),
+ ) -> ExprId {
let prev_unowned_bindings_len = self.unowned_bindings.len();
- let expr_id = create_expr(self);
+ let (bindings_owner, expr_to_return) = create_expr(self);
for binding in self.unowned_bindings.drain(prev_unowned_bindings_len..) {
- self.store.binding_owners.insert(binding, expr_id);
+ self.store.binding_owners.insert(binding, bindings_owner);
}
- expr_id
+ expr_to_return
+ }
+
+ fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
+ self.with_binding_owner_and_return(move |this| {
+ let expr = create_expr(this);
+ (expr, expr)
+ })
}
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@@ -1754,7 +1833,7 @@ impl<'db> ExprCollector<'db> {
let ptr = AstPtr::new(&e).upcast();
let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| {
let mut btail = None;
- let block = this.collect_block_(e, |id, statements, tail| {
+ let block = this.collect_block_(e, |_, id, statements, tail| {
btail = tail;
Expr::Block { id, statements, tail, label: Some(label) }
});
@@ -2212,7 +2291,7 @@ impl<'db> ExprCollector<'db> {
}
fn collect_block(&mut self, block: ast::BlockExpr) -> ExprId {
- self.collect_block_(block, |id, statements, tail| Expr::Block {
+ self.collect_block_(block, |_, id, statements, tail| Expr::Block {
id,
statements,
tail,
@@ -2223,7 +2302,7 @@ impl<'db> ExprCollector<'db> {
fn collect_block_(
&mut self,
block: ast::BlockExpr,
- mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
+ mk_block: impl FnOnce(&mut Self, Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> 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);
@@ -2258,8 +2337,8 @@ impl<'db> ExprCollector<'db> {
});
let syntax_node_ptr = AstPtr::new(&block.into());
- let expr_id = self
- .alloc_expr(mk_block(block_id, statements.into_boxed_slice(), tail), syntax_node_ptr);
+ let expr = mk_block(self, block_id, statements.into_boxed_slice(), tail);
+ let expr_id = self.alloc_expr(expr, syntax_node_ptr);
self.def_map = prev_def_map;
self.module = prev_local_module;
@@ -2685,17 +2764,17 @@ impl<'db> ExprCollector<'db> {
for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() {
match &rib.kind {
- RibKind::Normal(label_name, id, label_hygiene) => {
- if *label_name == name && *label_hygiene == hygiene_id {
- return if self.is_label_valid_from_rib(rib_idx) {
- Ok(Some(*id))
- } else {
- Err(ExpressionStoreDiagnostics::UnreachableLabel {
- name,
- node: self.expander.in_file(AstPtr::new(&lifetime)),
- })
- };
- }
+ RibKind::Normal(label_name, id, label_hygiene)
+ if *label_name == name && *label_hygiene == hygiene_id =>
+ {
+ return if self.is_label_valid_from_rib(rib_idx) {
+ Ok(Some(*id))
+ } else {
+ Err(ExpressionStoreDiagnostics::UnreachableLabel {
+ name,
+ node: self.expander.in_file(AstPtr::new(&lifetime)),
+ })
+ };
}
RibKind::MacroDef(macro_id) => {
if let Some((parent_ctx, label_macro_id)) = hygiene_info
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index 9c9c4db3b2..70ea54c734 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -9,6 +9,7 @@ use std::{
use hir_expand::{Lookup, mod_path::PathKind};
use itertools::Itertools;
use span::Edition;
+use stdx::never;
use syntax::ast::{HasName, RangeOp};
use crate::{
@@ -400,7 +401,7 @@ fn print_generic_params(db: &dyn DefDatabase, generic_params: &GenericParams, p:
pub fn print_expr_hir(
db: &dyn DefDatabase,
store: &ExpressionStore,
- _owner: DefWithBodyId,
+ _owner: ExpressionStoreOwnerId,
expr: ExprId,
edition: Edition,
) -> String {
@@ -419,7 +420,7 @@ pub fn print_expr_hir(
pub fn print_pat_hir(
db: &dyn DefDatabase,
store: &ExpressionStore,
- _owner: DefWithBodyId,
+ _owner: ExpressionStoreOwnerId,
pat: PatId,
oneline: bool,
edition: Edition,
@@ -760,14 +761,31 @@ impl Printer<'_> {
w!(self, "]");
}
Expr::Closure { args, arg_types, ret_type, body, closure_kind, capture_by } => {
+ let mut body = *body;
+ let mut print_pipes = true;
match closure_kind {
ClosureKind::Coroutine(Movability::Static) => {
w!(self, "static ");
}
- ClosureKind::Async => {
+ ClosureKind::AsyncClosure => {
+ if let Expr::Closure {
+ body: inner_body,
+ closure_kind: ClosureKind::AsyncBlock { .. },
+ ..
+ } = self.store[body]
+ {
+ body = inner_body;
+ } else {
+ never!("async closure should always have an async block body");
+ }
+
w!(self, "async ");
}
- _ => (),
+ ClosureKind::AsyncBlock { .. } => {
+ w!(self, "async ");
+ print_pipes = false;
+ }
+ ClosureKind::Closure | ClosureKind::Coroutine(Movability::Movable) => (),
}
match capture_by {
CaptureBy::Value => {
@@ -775,24 +793,26 @@ impl Printer<'_> {
}
CaptureBy::Ref => (),
}
- w!(self, "|");
- for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
- if i != 0 {
- w!(self, ", ");
+ if print_pipes {
+ w!(self, "|");
+ for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
+ if i != 0 {
+ w!(self, ", ");
+ }
+ self.print_pat(*pat);
+ if let Some(ty) = ty {
+ w!(self, ": ");
+ self.print_type_ref(*ty);
+ }
}
- self.print_pat(*pat);
- if let Some(ty) = ty {
- w!(self, ": ");
- self.print_type_ref(*ty);
+ w!(self, "|");
+ if let Some(ret_ty) = ret_type {
+ w!(self, " -> ");
+ self.print_type_ref(*ret_ty);
}
+ self.whitespace();
}
- w!(self, "|");
- if let Some(ret_ty) = ret_type {
- w!(self, " -> ");
- self.print_type_ref(*ret_ty);
- }
- self.whitespace();
- self.print_expr(*body);
+ self.print_expr(body);
}
Expr::Tuple { exprs } => {
w!(self, "(");
@@ -832,9 +852,6 @@ impl Printer<'_> {
Expr::Unsafe { id: _, statements, tail } => {
self.print_block(Some("unsafe "), statements, tail);
}
- Expr::Async { id: _, statements, tail } => {
- self.print_block(Some("async "), statements, tail);
- }
Expr::Const(id) => {
w!(self, "const {{ /* {id:?} */ }}");
}
diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs
index 40ae0b7de4..c6ba0241b7 100644
--- a/crates/hir-def/src/expr_store/scope.rs
+++ b/crates/hir-def/src/expr_store/scope.rs
@@ -324,7 +324,7 @@ fn compute_expr_scopes(
let mut scope = scopes.root_scope();
compute_expr_scopes(scopes, *id, &mut scope);
}
- Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => {
+ 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).
@@ -371,7 +371,6 @@ fn compute_expr_scopes(
#[cfg(test)]
mod tests {
- use base_db::RootQueryDb;
use hir_expand::{InFile, name::AsName};
use span::FileId;
use syntax::{AstNode, algo::find_node_at_offset, ast};
@@ -414,7 +413,7 @@ mod tests {
let (file_id, _) = editioned_file_id.unpack(&db);
- let file_syntax = db.parse(editioned_file_id).syntax_node();
+ let file_syntax = editioned_file_id.parse(&db).syntax_node();
let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap();
let function = find_function(&db, file_id);
@@ -570,7 +569,7 @@ fn foo() {
let (file_id, _) = editioned_file_id.unpack(&db);
- let file = db.parse(editioned_file_id).ok().unwrap();
+ let file = editioned_file_id.parse(&db).ok().unwrap();
let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
.expect("failed to find a name at the target offset");
let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset).unwrap();
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index 985cd96662..db12775df9 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -652,11 +652,32 @@ 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 {
- let ref mut param2 = param2;
- let _ = <ra@gennew>0;
- let 123 = <ra@gennew>1;
- {}
- }"#]],
+ fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async {
+ let ref mut param2 = param2;
+ let _ = <ra@gennew>0;
+ let 123 = <ra@gennew>1;
+ {}
+ }"#]],
)
}
+
+#[test]
+fn array_element_cfg() {
+ pretty_print(
+ r#"
+fn foo() {
+ [
+ (),
+ #[cfg(false)]
+ ()
+ ];
+}
+ "#,
+ expect![[r#"
+ fn foo() {
+ [
+ (),
+ ];
+ }"#]],
+ );
+}
diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs
index 83594ee021..71fcced2d8 100644
--- a/crates/hir-def/src/expr_store/tests/body/block.rs
+++ b/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -190,13 +190,13 @@ fn f() {
"#,
expect![[r#"
ModuleIdLt {
- [salsa id]: Id(3803),
+ [salsa id]: Id(3403),
krate: Crate(
- Id(2400),
+ Id(2000),
),
block: Some(
BlockId(
- 4801,
+ 4401,
),
),
}"#]],
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index 7781a8fe54..9e51d0eac9 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -21,7 +21,7 @@ use std::fmt;
use hir_expand::{MacroDefId, name::Name};
use intern::Symbol;
use la_arena::Idx;
-use rustc_apfloat::ieee::{Half as f16, Quad as f128};
+use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use syntax::ast;
use type_ref::TypeRefId;
@@ -94,19 +94,19 @@ impl FloatTypeWrapper {
Self(sym)
}
- pub fn to_f128(&self) -> f128 {
+ pub fn to_f128(&self) -> Quad {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f64(&self) -> f64 {
+ pub fn to_f64(&self) -> Double {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f32(&self) -> f32 {
+ pub fn to_f32(&self) -> Single {
self.0.as_str().parse().unwrap_or_default()
}
- pub fn to_f16(&self) -> f16 {
+ pub fn to_f16(&self) -> Half {
self.0.as_str().parse().unwrap_or_default()
}
}
@@ -214,11 +214,6 @@ pub enum Expr {
tail: Option<ExprId>,
label: Option<LabelId>,
},
- Async {
- id: Option<BlockId>,
- statements: Box<[Statement]>,
- tail: Option<ExprId>,
- },
Const(ExprId),
// FIXME: Fold this into Block with an unsafe flag?
Unsafe {
@@ -339,7 +334,6 @@ impl Expr {
| Expr::Block { .. }
| Expr::Unsafe { .. }
| Expr::Const(_)
- | Expr::Async { .. }
| Expr::If { .. }
| Expr::Literal(_)
| Expr::Loop { .. }
@@ -534,7 +528,25 @@ pub enum InlineAsmRegOrRegClass {
pub enum ClosureKind {
Closure,
Coroutine(Movability),
- Async,
+ AsyncBlock { source: CoroutineSource },
+ AsyncClosure,
+}
+
+/// In the case of a coroutine created as part of an async/gen construct,
+/// which kind of async/gen construct caused it to be created?
+///
+/// This helps error messages but is also used to drive coercions in
+/// type-checking (see #60424).
+#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
+pub enum CoroutineSource {
+ /// An explicit `async`/`gen` block written by the user.
+ Block,
+
+ /// An explicit `async`/`gen` closure written by the user.
+ Closure,
+
+ /// The `async`/`gen` block generated as the body of an async/gen function.
+ Fn,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
diff --git a/crates/hir-def/src/import_map.rs b/crates/hir-def/src/import_map.rs
index 0014e1af5c..ba077b1b2e 100644
--- a/crates/hir-def/src/import_map.rs
+++ b/crates/hir-def/src/import_map.rs
@@ -499,7 +499,7 @@ fn search_maps(
#[cfg(test)]
mod tests {
- use base_db::RootQueryDb;
+ use base_db::all_crates;
use expect_test::{Expect, expect};
use test_fixture::WithFixture;
@@ -536,7 +536,7 @@ mod tests {
expect: Expect,
) {
let db = TestDB::with_files(ra_fixture);
- let all_crates = db.all_crates();
+ let all_crates = all_crates(&db);
let krate = all_crates
.iter()
.copied()
@@ -616,7 +616,7 @@ mod tests {
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let db = TestDB::with_files(ra_fixture);
- let all_crates = db.all_crates();
+ let all_crates = all_crates(&db);
let actual = all_crates
.iter()
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index b11a8bcd90..fe7d180685 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -680,20 +680,19 @@ impl ItemScope {
changed = true;
}
Entry::Occupied(mut entry)
- if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
+ if !matches!(import, Some(ImportOrExternCrate::Glob(..)))
+ && glob_imports.values.remove(&lookup) =>
{
- if glob_imports.values.remove(&lookup) {
- cov_mark::hit!(import_shadowed);
-
- let import = import.and_then(ImportOrExternCrate::import_or_glob);
- let prev = std::mem::replace(&mut fld.import, import);
- if let Some(import) = import {
- self.use_imports_values
- .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
- }
- entry.insert(fld);
- changed = true;
+ cov_mark::hit!(import_shadowed);
+
+ let import = import.and_then(ImportOrExternCrate::import_or_glob);
+ let prev = std::mem::replace(&mut fld.import, import);
+ if let Some(import) = import {
+ self.use_imports_values
+ .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
}
+ entry.insert(fld);
+ changed = true;
}
_ => {}
}
@@ -720,20 +719,19 @@ impl ItemScope {
changed = true;
}
Entry::Occupied(mut entry)
- if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
+ if !matches!(import, Some(ImportOrExternCrate::Glob(..)))
+ && glob_imports.macros.remove(&lookup) =>
{
- if glob_imports.macros.remove(&lookup) {
- cov_mark::hit!(import_shadowed);
- let prev = std::mem::replace(&mut fld.import, import);
- if let Some(import) = import {
- self.use_imports_macros.insert(
- import,
- prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
- );
- }
- entry.insert(fld);
- changed = true;
+ cov_mark::hit!(import_shadowed);
+ let prev = std::mem::replace(&mut fld.import, import);
+ if let Some(import) = import {
+ self.use_imports_macros.insert(
+ import,
+ prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
+ );
}
+ entry.insert(fld);
+ changed = true;
}
_ => {}
}
diff --git a/crates/hir-def/src/item_tree/attrs.rs b/crates/hir-def/src/item_tree/attrs.rs
index 7907611284..867d813e3f 100644
--- a/crates/hir-def/src/item_tree/attrs.rs
+++ b/crates/hir-def/src/item_tree/attrs.rs
@@ -13,12 +13,12 @@ use std::{
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_expand::{
- attrs::{Attr, AttrId, AttrInput, Meta, collect_item_tree_attrs},
+ attrs::{Attr, AttrId, AttrInput, collect_item_tree_attrs},
mod_path::ModPath,
name::Name,
};
use intern::{Interned, Symbol, sym};
-use syntax::{AstNode, T, ast};
+use syntax::{AstNode, ast};
use syntax_bridge::DocCommentDesugarMode;
use tt::token_to_literal;
@@ -51,58 +51,62 @@ impl AttrsOrCfg {
S: syntax_bridge::SpanMapper + Copy,
{
let mut attrs = Vec::new();
- let result =
- collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, container, _, _| {
- // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId`
- // tracking.
- let (span, path_range, input) = match meta {
- Meta::NamedKeyValue { path_range, name: _, value } => {
- let span = span_map.span_for(path_range);
- let input = value.map(|value| {
- Box::new(AttrInput::Literal(token_to_literal(
- value.text(),
- span_map.span_for(value.text_range()),
- )))
- });
- (span, path_range, input)
- }
- Meta::TokenTree { path, tt } => {
- let span = span_map.span_for(path.range);
- let tt = syntax_bridge::syntax_node_to_token_tree(
- tt.syntax(),
- span_map,
- span,
- DocCommentDesugarMode::ProcMacro,
- );
- let input = Some(Box::new(AttrInput::TokenTree(tt)));
- (span, path.range, input)
- }
- Meta::Path { path } => {
- let span = span_map.span_for(path.range);
- (span, path.range, None)
- }
- };
+ let result = collect_item_tree_attrs::<Infallible>(owner, cfg_options, |meta, _| {
+ // NOTE: We cannot early return from this function, *every* attribute must be pushed, otherwise we'll mess the `AttrId`
+ // tracking.
+ let path = meta.path();
+ let path_range = path
+ .as_ref()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| meta.syntax().text_range());
+ let (span, input) = match &meta {
+ ast::Meta::KeyValueMeta(meta) => {
+ let span = span_map.span_for(path_range);
+ let input = meta.expr().and_then(|value| {
+ if let ast::Expr::Literal(value) = value {
+ Some(Box::new(AttrInput::Literal(token_to_literal(
+ value.token().text(),
+ span_map.span_for(value.syntax().text_range()),
+ ))))
+ } else {
+ None
+ }
+ });
+ (span, input)
+ }
+ ast::Meta::TokenTreeMeta(meta) => {
+ let span = span_map.span_for(path_range);
+ let tt = syntax_bridge::syntax_node_to_token_tree(
+ &meta
+ .token_tree()
+ .map(|it| it.syntax().clone())
+ .unwrap_or_else(|| meta.syntax().clone()),
+ span_map,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ );
+ let input = Some(Box::new(AttrInput::TokenTree(tt)));
+ (span, input)
+ }
+ ast::Meta::PathMeta(_) => {
+ let span = span_map.span_for(path_range);
+ (span, None)
+ }
+ ast::Meta::CfgMeta(_) | ast::Meta::CfgAttrMeta(_) | ast::Meta::UnsafeMeta(_) => {
+ unreachable!(
+ "`cfg`, `cfg_attr` and `unsafe(...)` are handled in `collect_item_tree_attrs()`"
+ )
+ }
+ };
- let path = container.token_at_offset(path_range.start()).right_biased().and_then(
- |first_path_token| {
- let is_abs = matches!(first_path_token.kind(), T![:] | T![::]);
- let segments =
- std::iter::successors(Some(first_path_token), |it| it.next_token())
- .take_while(|it| it.text_range().end() <= path_range.end())
- .filter(|it| it.kind().is_any_identifier());
- ModPath::from_tokens(
- db,
- &mut |range| span_map.span_for(range).ctx,
- is_abs,
- segments,
- )
- },
- );
- let path = path.unwrap_or_else(|| Name::missing().into());
-
- attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
- ControlFlow::Continue(())
+ let path = path.and_then(|path| {
+ ModPath::from_src(db, path, &mut |range| span_map.span_for(range).ctx)
});
+ let path = path.unwrap_or_else(|| Name::missing().into());
+
+ attrs.push(Attr { path: Interned::new(path), input, ctxt: span.ctx });
+ ControlFlow::Continue(())
+ });
let attrs = AttrsOwned(attrs.into_boxed_slice());
match result {
Some(Either::Right(cfg)) => AttrsOrCfg::CfgDisabled(Box::new((cfg, attrs))),
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index fef92c89b1..37d70b1e33 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -306,6 +306,7 @@ language_item_table! { LangItems =>
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
StructuralTeq, sym::structural_teq, TraitId;
Copy, sym::copy, TraitId;
+ UseCloned, sym::use_cloned, TraitId;
Clone, sym::clone, TraitId;
TrivialClone, sym::trivial_clone, TraitId;
Sync, sym::sync, TraitId;
@@ -324,6 +325,7 @@ language_item_table! { LangItems =>
Drop, sym::drop, TraitId;
Destruct, sym::destruct, TraitId;
+ BikeshedGuaranteedNoDrop,sym::bikeshed_guaranteed_no_drop, TraitId;
CoerceUnsized, sym::coerce_unsized, TraitId;
DispatchFromDyn, sym::dispatch_from_dyn, TraitId;
@@ -373,6 +375,8 @@ language_item_table! { LangItems =>
AsyncFn, sym::async_fn, TraitId;
AsyncFnMut, sym::async_fn_mut, TraitId;
AsyncFnOnce, sym::async_fn_once, TraitId;
+ AsyncFnKindHelper, sym::async_fn_kind_helper,TraitId;
+ AsyncFnKindUpvars, sym::async_fn_kind_upvars,TypeAliasId;
CallRefFuture, sym::call_ref_future, TypeAliasId;
CallOnceFuture, sym::call_once_future, TypeAliasId;
@@ -489,6 +493,8 @@ language_item_table! { LangItems =>
IntoIterIntoIter, sym::into_iter, FunctionId;
IteratorNext, sym::next, FunctionId;
Iterator, sym::iterator, TraitId;
+ FusedIterator, sym::fused_iterator, TraitId;
+ AsyncIterator, sym::async_iterator, TraitId;
PinNewUnchecked, sym::new_unchecked, FunctionId;
@@ -509,6 +515,10 @@ language_item_table! { LangItems =>
CStr, sym::CStr, StructId;
Ordering, sym::Ordering, EnumId;
+ Field, sym::field, TraitId;
+ FieldBase, sym::field_base, TypeAliasId;
+ FieldType, sym::field_type, TypeAliasId;
+
@non_lang_core_traits:
core::default, Default;
core::fmt, Debug;
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index d93df7af6a..e75c96b630 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ macro_rules! f {
};
}
-struct#0:MacroRules[BE8F, 0]@58..64#18432# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#18432#
- map#0:MacroRules[BE8F, 0]@86..89#18432#:#0:MacroRules[BE8F, 0]@89..90#18432# #0:MacroRules[BE8F, 0]@89..90#18432#::#0:MacroRules[BE8F, 0]@91..93#18432#std#0:MacroRules[BE8F, 0]@93..96#18432#::#0:MacroRules[BE8F, 0]@96..98#18432#collections#0:MacroRules[BE8F, 0]@98..109#18432#::#0:MacroRules[BE8F, 0]@109..111#18432#HashSet#0:MacroRules[BE8F, 0]@111..118#18432#<#0:MacroRules[BE8F, 0]@118..119#18432#(#0:MacroRules[BE8F, 0]@119..120#18432#)#0:MacroRules[BE8F, 0]@120..121#18432#>#0:MacroRules[BE8F, 0]@121..122#18432#,#0:MacroRules[BE8F, 0]@122..123#18432#
-}#0:MacroRules[BE8F, 0]@132..133#18432#
+struct#0:MacroRules[BE8F, 0]@58..64#17408# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#17408#
+ map#0:MacroRules[BE8F, 0]@86..89#17408#:#0:MacroRules[BE8F, 0]@89..90#17408# #0:MacroRules[BE8F, 0]@89..90#17408#::#0:MacroRules[BE8F, 0]@91..93#17408#std#0:MacroRules[BE8F, 0]@93..96#17408#::#0:MacroRules[BE8F, 0]@96..98#17408#collections#0:MacroRules[BE8F, 0]@98..109#17408#::#0:MacroRules[BE8F, 0]@109..111#17408#HashSet#0:MacroRules[BE8F, 0]@111..118#17408#<#0:MacroRules[BE8F, 0]@118..119#17408#(#0:MacroRules[BE8F, 0]@119..120#17408#)#0:MacroRules[BE8F, 0]@120..121#17408#>#0:MacroRules[BE8F, 0]@121..122#17408#,#0:MacroRules[BE8F, 0]@122..123#17408#
+}#0:MacroRules[BE8F, 0]@132..133#17408#
"#]],
);
}
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
#[macro_use]
mod foo;
-struct#1:MacroRules[DB0C, 0]@59..65#18432# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#18432#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#18432#;#1:MacroRules[DB0C, 0]@75..76#18432#
+struct#1:MacroRules[DB0C, 0]@59..65#17408# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#17408#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#17408#;#1:MacroRules[DB0C, 0]@75..76#17408#
"#]],
);
}
@@ -423,10 +423,10 @@ m! { foo, bar }
macro_rules! m {
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
}
-impl#\18432# Bar#\18432# {#\18432#
- fn#\18432# foo#\ROOT2024#(#\18432#)#\18432# {#\18432#}#\18432#
- fn#\18432# bar#\ROOT2024#(#\18432#)#\18432# {#\18432#}#\18432#
-}#\18432#
+impl#\17408# Bar#\17408# {#\17408#
+ fn#\17408# foo#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408#
+ fn#\17408# bar#\ROOT2024#(#\17408#)#\17408# {#\17408#}#\17408#
+}#\17408#
"#]],
);
}
@@ -1198,7 +1198,7 @@ m! { hello::world }
macro_rules! m {
($m:meta) => ( #[$m] fn bar() {} )
}
-#[cfg(target_os = "windows")] fn bar() {}
+#[cfg (target_os = "windows")] fn bar() {}
#[hello::world] fn bar() {}
"#]],
);
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index ddabb50251..cac248f47f 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -205,7 +205,7 @@ impl Clone for D3DVSHADERCAPS2_0 {
*self
}
}
-#[cfg(feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 {
+#[cfg (feature = "impl-default")] impl Default for D3DVSHADERCAPS2_0 {
#[inline] fn default() -> D3DVSHADERCAPS2_0 {
unsafe {
$crate::_core::mem::zeroed()
@@ -215,7 +215,7 @@ impl Clone for D3DVSHADERCAPS2_0 {
#[repr(C)]
#[derive(Copy)]
-#[cfg_attr(target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS {
+#[cfg_attr (target_arch = "x86", repr(packed))] pub struct D3DCONTENTPROTECTIONCAPS {
pub Caps: u8,
}
impl Clone for D3DCONTENTPROTECTIONCAPS {
@@ -223,7 +223,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS {
*self
}
}
-#[cfg(feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS {
+#[cfg (feature = "impl-default")] impl Default for D3DCONTENTPROTECTIONCAPS {
#[inline] fn default() -> D3DCONTENTPROTECTIONCAPS {
unsafe {
$crate::_core::mem::zeroed()
@@ -1001,8 +1001,8 @@ macro_rules! with_std {
($($i:item)*) => ($(#[cfg(feature = "std")]$i)*)
}
-#[cfg(feature = "std")] mod m;
-#[cfg(feature = "std")] mod f;
+#[cfg (feature = "std")] mod m;
+#[cfg (feature = "std")] mod f;
"#]],
)
}
diff --git a/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs
index 8317c56caf..eabdada67c 100644
--- a/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -16,7 +16,6 @@ mod proc_macros;
use std::{any::TypeId, iter, ops::Range, sync};
-use base_db::RootQueryDb;
use expect_test::Expect;
use hir_expand::{
AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind,
@@ -75,7 +74,7 @@ fn check_errors(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
let editioned_file_id =
ast_id.file_id.file_id().expect("macros inside macros are not supported");
- let ast = db.parse(editioned_file_id).syntax_node();
+ let ast = editioned_file_id.parse(&db).syntax_node();
let ast_id_map = db.ast_id_map(ast_id.file_id);
let node = ast_id_map.get_erased(ast_id.value).to_node(&ast);
Some((node.text_range(), errors))
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index bf04a500a5..8c91cf6793 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -55,8 +55,8 @@ mod foo {
# ![doc = "123..."]
# ![attr2]
# ![attr3]
- #[cfg_attr(true , cfg(false ))] fn foo() {}
- #[cfg(true )] fn bar() {}
+ #[cfg_attr (true , cfg (false ))] fn foo() {}
+ #[cfg (true )] fn bar() {}
}"##]],
);
}
diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs
index 5fda1beab4..56b3f03f7b 100644
--- a/crates/hir-def/src/nameres.rs
+++ b/crates/hir-def/src/nameres.rs
@@ -346,7 +346,7 @@ impl ModuleOrigin {
match self {
&ModuleOrigin::File { definition: editioned_file_id, .. }
| &ModuleOrigin::CrateRoot { definition: editioned_file_id } => {
- let sf = db.parse(editioned_file_id).tree();
+ let sf = editioned_file_id.parse(db).tree();
InFile::new(editioned_file_id.into(), ModuleSource::SourceFile(sf))
}
&ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 9c101c127b..703f070dba 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -22,7 +22,7 @@ use itertools::izip;
use la_arena::Idx;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::SmallVec;
-use span::{Edition, FileAstId, SyntaxContext};
+use span::{Edition, FileAstId, ROOT_ERASED_FILE_AST_ID, SyntaxContext};
use stdx::always;
use syntax::ast;
use triomphe::Arc;
@@ -369,7 +369,14 @@ impl<'db> DefCollector<'db> {
self.inject_prelude();
- if matches!(item_tree.top_level_attrs(), AttrsOrCfg::CfgDisabled(_)) {
+ if let AttrsOrCfg::CfgDisabled(attrs) = item_tree.top_level_attrs() {
+ let (cfg_expr, _) = &**attrs;
+ self.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
+ self.def_map.root,
+ InFile::new(file_id.into(), ROOT_ERASED_FILE_AST_ID),
+ cfg_expr.clone(),
+ self.cfg_options.clone(),
+ ));
return;
}
diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs
index fe55252e25..08d98dff33 100644
--- a/crates/hir-def/src/nameres/tests.rs
+++ b/crates/hir-def/src/nameres/tests.rs
@@ -4,7 +4,6 @@ mod incremental;
mod macros;
mod mod_resolution;
-use base_db::RootQueryDb;
use expect_test::{Expect, expect};
use test_fixture::WithFixture;
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 5b75c078ec..0f1828abce 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -1,6 +1,6 @@
use base_db::{
CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
- DependencyBuilder, Env, RootQueryDb, SourceDatabase,
+ DependencyBuilder, Env, SourceDatabase, all_crates,
};
use expect_test::{Expect, expect};
use intern::Symbol;
@@ -56,11 +56,11 @@ pub const BAZ: u32 = 0;
"#,
);
- for &krate in db.all_crates().iter() {
+ for &krate in all_crates(&db).iter() {
crate_def_map(&db, krate);
}
- let all_crates_before = db.all_crates();
+ let all_crates_before = all_crates(&db);
{
// Add dependencies: c -> b, b -> a.
@@ -100,15 +100,15 @@ pub const BAZ: u32 = 0;
new_crate_graph.set_in_db(&mut db);
}
- let all_crates_after = db.all_crates();
+ let all_crates_after = all_crates(&db);
assert!(
- Arc::ptr_eq(&all_crates_before, &all_crates_after),
+ std::sync::Arc::ptr_eq(&all_crates_before, &all_crates_after),
"the all_crates list should not have been invalidated"
);
execute_assert_events(
&db,
|| {
- for &krate in db.all_crates().iter() {
+ for &krate in all_crates(&db).iter() {
crate_def_map(&db, krate);
}
},
@@ -167,22 +167,22 @@ fn no() {}
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"EnumVariants::of_",
]
"#]],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -225,16 +225,16 @@ pub struct S {}
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"decl_macro_expander_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"macro_def_shim",
"file_item_tree_query",
@@ -245,7 +245,7 @@ pub struct S {}
"#]],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -283,21 +283,21 @@ fn f() { foo }
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"crate_local_def_map",
"proc_macros_for_crate_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"macro_def_shim",
"file_item_tree_query",
@@ -310,7 +310,7 @@ fn f() { foo }
"#]],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -407,22 +407,22 @@ pub struct S {}
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"crate_local_def_map",
"proc_macros_for_crate_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"decl_macro_expander_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"macro_def_shim",
"file_item_tree_query",
@@ -446,7 +446,7 @@ pub struct S {}
"#]],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -524,16 +524,16 @@ m!(Z);
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"decl_macro_expander_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"macro_def_shim",
"file_item_tree_query",
@@ -571,7 +571,7 @@ m!(Z);
&[("file_item_tree_query", 1), ("parse_macro_expansion_shim", 0)],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -611,7 +611,7 @@ pub type Ty = ();
[
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
]
"#]],
@@ -629,7 +629,7 @@ pub type Ty = ();
&[("file_item_tree_query", 1), ("parse", 1)],
expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index a013f8b2bc..f073cf777d 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -1,3 +1,4 @@
+use base_db::all_crates;
use expect_test::expect;
use itertools::Itertools;
@@ -1129,7 +1130,7 @@ pub fn derive_macro_2(_item: TokenStream) -> TokenStream {
}
"#,
);
- let krate = *db.all_crates().last().expect("no crate graph present");
+ let krate = *all_crates(&db).last().expect("no crate graph present");
let def_map = crate_def_map(&db, krate);
assert_eq!(def_map.data.exported_derives.len(), 1);
@@ -1497,7 +1498,7 @@ struct TokenStream;
fn proc_attr(a: TokenStream, b: TokenStream) -> TokenStream { a }
"#,
);
- let krate = *db.all_crates().last().expect("no crate graph present");
+ let krate = *all_crates(&db).last().expect("no crate graph present");
let def_map = crate_def_map(&db, krate);
let root_module = &def_map[def_map.root].scope;
diff --git a/crates/hir-def/src/test_db.rs b/crates/hir-def/src/test_db.rs
index 0d260279f9..b854d2aa21 100644
--- a/crates/hir-def/src/test_db.rs
+++ b/crates/hir-def/src/test_db.rs
@@ -3,8 +3,9 @@
use std::{fmt, panic, sync::Mutex};
use base_db::{
- Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb,
- SourceDatabase, SourceRoot, SourceRootId, SourceRootInput,
+ Crate, CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, SourceDatabase,
+ SourceRoot, SourceRootId, SourceRootInput, all_crates, relevant_crates,
+ set_all_crates_with_durability,
};
use hir_expand::{InFile, files::FilePosition};
use salsa::Durability;
@@ -49,7 +50,7 @@ impl Default for TestDB {
};
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
// This needs to be here otherwise `CrateGraphBuilder` panics.
- this.set_all_crates(Arc::new(Box::new([])));
+ set_all_crates_with_durability(&mut this, std::iter::empty(), Durability::HIGH);
_ = base_db::LibraryRoots::builder(Default::default())
.durability(Durability::MEDIUM)
.new(&this);
@@ -145,7 +146,7 @@ impl SourceDatabase for TestDB {
impl TestDB {
pub(crate) fn fetch_test_crate(&self) -> Crate {
- let all_crates = self.all_crates();
+ let all_crates = all_crates(self);
all_crates
.iter()
.copied()
@@ -157,7 +158,7 @@ impl TestDB {
}
pub(crate) fn module_for_file(&self, file_id: FileId) -> ModuleId {
- for &krate in self.relevant_crates(file_id).iter() {
+ for &krate in relevant_crates(self, file_id).iter() {
let crate_def_map = crate_def_map(self, krate);
for (local_id, data) in crate_def_map.modules() {
if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) {
@@ -288,7 +289,7 @@ impl TestDB {
let source_map = &Body::with_source_map(self, def_with_body).1;
let scopes = ExprScopes::body_expr_scopes(self, def_with_body);
- let root_syntax_node = self.parse(file_id).syntax_node();
+ let root_syntax_node = file_id.parse(self).syntax_node();
let scope_iter =
algo::ancestors_at_offset(&root_syntax_node, position.offset).filter_map(|node| {
let block = ast::BlockExpr::cast(node)?;
diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml
index 4fa476afb6..43b0bea891 100644
--- a/crates/hir-expand/Cargo.toml
+++ b/crates/hir-expand/Cargo.toml
@@ -23,7 +23,6 @@ triomphe.workspace = true
query-group.workspace = true
salsa.workspace = true
salsa-macros.workspace = true
-arrayvec.workspace = true
thin-vec.workspace = true
# local deps
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index e3f10b2129..d1f962f7ff 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -4,20 +4,8 @@
//! [`expand_cfg_attr_with_doc_comments()`]. It is used to implement all attribute lowering
//! in r-a. Its basic job is to list attributes; however, attributes do not necessarily map
//! into [`ast::Attr`], because `cfg_attr` can map to zero, one, or more attributes
-//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). To bridge this gap, this module defines
-//! [`Meta`], which represents a desugared attribute. Various bits of r-a need different
-//! things from [`Meta`], therefore it contains many parts. The basic idea is:
-//!
-//! - There are three kinds of attributes, `path = value`, `path`, and `path(token_tree)`.
-//! - Most bits of rust-analyzer only need to deal with some paths. Therefore, we keep
-//! the path only if it has up to 2 segments, or one segment for `path = value`.
-//! We also only keep the value in `path = value` if it is a literal. However, we always
-//! save the all relevant ranges of attributes (the path range, and the full attribute range)
-//! for parts of r-a (e.g. name resolution) that need a faithful representation of the
-//! attribute.
-//!
-//! [`expand_cfg_attr()`] expands `cfg_attr`s as it goes (as its name implies), to list
-//! all attributes.
+//! (`#[cfg_attr(predicate, attr1, attr2, ...)]`). [`expand_cfg_attr()`] expands `cfg_attr`s
+//! as it goes (as its name implies), to list all attributes.
//!
//! Another thing to note is that we need to be able to map an attribute back to a range
//! (for diagnostic purposes etc.). This is only ever needed for attributes that participate
@@ -26,26 +14,18 @@
//! place (here) and one function ([`is_item_tree_filtered_attr()`]) that decides whether
//! an attribute participate in name resolution.
-use std::{
- borrow::Cow, cell::OnceCell, convert::Infallible, fmt, iter::Peekable, ops::ControlFlow,
-};
+use std::{borrow::Cow, cell::OnceCell, convert::Infallible, fmt, ops::ControlFlow};
-use ::tt::{TextRange, TextSize};
-use arrayvec::ArrayVec;
+use ::tt::TextRange;
use base_db::Crate;
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use intern::Interned;
use itertools::Itertools;
use mbe::{DelimiterKind, Punct};
-use parser::T;
use smallvec::SmallVec;
use span::{RealSpanMap, Span, SyntaxContext};
-use syntax::{
- AstNode, NodeOrToken, SyntaxNode, SyntaxToken,
- ast::{self, TokenTreeChildren},
- unescape,
-};
+use syntax::{AstNode, SmolStr, ast, unescape};
use syntax_bridge::DocCommentDesugarMode;
use crate::{
@@ -56,207 +36,75 @@ use crate::{
tt::{self, TopSubtree},
};
-#[derive(Debug)]
-pub struct AttrPath {
- /// This can be empty if the path is not of 1 or 2 segments exactly.
- pub segments: ArrayVec<SyntaxToken, 2>,
- pub range: TextRange,
- // FIXME: This shouldn't be textual, `#[test]` needs name resolution.
- // And if textual, it shouldn't be here, it should be in hir-def/src/attrs.rs. But some macros
- // fully qualify `test` as `core::prelude::vX::test`, and this is more than 2 segments, so hir-def
- // attrs can't find it. But this will mean we have to push every up-to-4-segments path, which
- // may impact perf. So it was easier to just hack it here.
- pub is_test: bool,
+pub trait AstPathExt {
+ fn is1(&self, segment: &str) -> bool;
+
+ fn as_one_segment(&self) -> Option<SmolStr>;
+
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)>;
}
-impl AttrPath {
- #[inline]
- fn extract(path: &ast::Path) -> Self {
- let mut is_test = false;
- let segments = (|| {
- let mut segments = ArrayVec::new();
- let segment2 = path.segment()?.name_ref()?.syntax().first_token()?;
- if segment2.text() == "test" {
- // `#[test]` or `#[core::prelude::vX::test]`.
- is_test = true;
- }
- let segment1 = path.qualifier();
- if let Some(segment1) = segment1 {
- if segment1.qualifier().is_some() {
- None
- } else {
- let segment1 = segment1.segment()?.name_ref()?.syntax().first_token()?;
- segments.push(segment1);
- segments.push(segment2);
- Some(segments)
- }
- } else {
- segments.push(segment2);
- Some(segments)
- }
- })();
- AttrPath {
- segments: segments.unwrap_or(ArrayVec::new()),
- range: path.syntax().text_range(),
- is_test,
- }
+impl AstPathExt for ast::Path {
+ fn is1(&self, segment: &str) -> bool {
+ self.as_one_segment().is_some_and(|it| it == segment)
}
- #[inline]
- pub fn is1(&self, segment: &str) -> bool {
- self.segments.len() == 1 && self.segments[0].text() == segment
+ fn as_one_segment(&self) -> Option<SmolStr> {
+ Some(self.as_single_name_ref()?.text().into())
}
-}
-#[derive(Debug)]
-pub enum Meta {
- /// `name` is `None` if not a single token. `value` is a literal or `None`.
- NamedKeyValue {
- path_range: TextRange,
- name: Option<SyntaxToken>,
- value: Option<SyntaxToken>,
- },
- TokenTree {
- path: AttrPath,
- tt: ast::TokenTree,
- },
- Path {
- path: AttrPath,
- },
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> {
+ let parent = self.qualifier().as_one_segment();
+ let this = self.segment()?.name_ref()?.text().into();
+ if let Some(parent) = parent { Some((parent, Some(this))) } else { Some((this, None)) }
+ }
}
-impl Meta {
- #[inline]
- pub fn path_range(&self) -> TextRange {
- match self {
- Meta::NamedKeyValue { path_range, .. } => *path_range,
- Meta::TokenTree { path, .. } | Meta::Path { path } => path.range,
- }
+impl AstPathExt for Option<ast::Path> {
+ fn is1(&self, segment: &str) -> bool {
+ self.as_ref().is_some_and(|it| it.is1(segment))
}
- fn extract(iter: &mut Peekable<TokenTreeChildren>) -> Option<(Self, TextSize)> {
- let mut start_offset = None;
- if let Some(NodeOrToken::Token(colon1)) = iter.peek()
- && colon1.kind() == T![:]
- {
- start_offset = Some(colon1.text_range().start());
- iter.next();
- iter.next_if(|it| it.as_token().is_some_and(|it| it.kind() == T![:]));
- }
- let first_segment = iter
- .next_if(|it| it.as_token().is_some_and(|it| it.kind().is_any_identifier()))?
- .into_token()?;
- let mut is_test = first_segment.text() == "test";
- let start_offset = start_offset.unwrap_or_else(|| first_segment.text_range().start());
-
- let mut segments_len = 1;
- let mut second_segment = None;
- let mut path_range = first_segment.text_range();
- while iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
- && let _ = iter.next()
- && iter.peek().and_then(NodeOrToken::as_token).is_some_and(|it| it.kind() == T![:])
- && let _ = iter.next()
- && let Some(NodeOrToken::Token(segment)) = iter.peek()
- && segment.kind().is_any_identifier()
- {
- segments_len += 1;
- is_test = segment.text() == "test";
- second_segment = Some(segment.clone());
- path_range = TextRange::new(path_range.start(), segment.text_range().end());
- iter.next();
- }
+ fn as_one_segment(&self) -> Option<SmolStr> {
+ self.as_ref().and_then(|it| it.as_one_segment())
+ }
- let segments = |first, second| {
- let mut segments = ArrayVec::new();
- if segments_len <= 2 {
- segments.push(first);
- if let Some(second) = second {
- segments.push(second);
- }
- }
- segments
- };
- let meta = match iter.peek() {
- Some(NodeOrToken::Token(eq)) if eq.kind() == T![=] => {
- iter.next();
- let value = match iter.peek() {
- Some(NodeOrToken::Token(token)) if token.kind().is_literal() => {
- // No need to consume it, it will be consumed by `extract_and_eat_comma()`.
- Some(token.clone())
- }
- _ => None,
- };
- let name = if second_segment.is_none() { Some(first_segment) } else { None };
- Meta::NamedKeyValue { path_range, name, value }
- }
- Some(NodeOrToken::Node(tt)) => Meta::TokenTree {
- path: AttrPath {
- segments: segments(first_segment, second_segment),
- range: path_range,
- is_test,
- },
- tt: tt.clone(),
- },
- _ => Meta::Path {
- path: AttrPath {
- segments: segments(first_segment, second_segment),
- range: path_range,
- is_test,
- },
- },
- };
- Some((meta, start_offset))
+ fn as_up_to_two_segment(&self) -> Option<(SmolStr, Option<SmolStr>)> {
+ self.as_ref().and_then(|it| it.as_up_to_two_segment())
}
+}
+
+pub trait AstKeyValueMetaExt {
+ fn value_string(&self) -> Option<SmolStr>;
+}
- fn extract_possibly_unsafe(
- iter: &mut Peekable<TokenTreeChildren>,
- container: &ast::TokenTree,
- ) -> Option<(Self, TextRange)> {
- if iter.peek().is_some_and(|it| it.as_token().is_some_and(|it| it.kind() == T![unsafe])) {
- iter.next();
- let tt = iter.next()?.into_node()?;
- let result = Self::extract(&mut TokenTreeChildren::new(&tt).peekable()).map(
- |(meta, start_offset)| (meta, TextRange::new(start_offset, tt_end_offset(&tt))),
- );
- while iter.next().is_some_and(|it| it.as_token().is_none_or(|it| it.kind() != T![,])) {}
- result
+impl AstKeyValueMetaExt for ast::KeyValueMeta {
+ fn value_string(&self) -> Option<SmolStr> {
+ if let Some(ast::Expr::Literal(value)) = self.expr()
+ && let ast::LiteralKind::String(value) = value.kind()
+ && let Ok(value) = value.value()
+ {
+ Some((*value).into())
} else {
- Self::extract(iter).map(|(meta, start_offset)| {
- let end_offset = 'find_end_offset: {
- for it in iter {
- if let NodeOrToken::Token(it) = it
- && it.kind() == T![,]
- {
- break 'find_end_offset it.text_range().start();
- }
- }
- tt_end_offset(container)
- };
- (meta, TextRange::new(start_offset, end_offset))
- })
+ None
}
}
}
-fn tt_end_offset(tt: &ast::TokenTree) -> TextSize {
- tt.syntax().last_token().unwrap().text_range().start()
-}
-
-/// The callback is passed a desugared form of the attribute ([`Meta`]), a [`SyntaxNode`] fully containing it
-/// (note: it may not be the direct parent), the range within the [`SyntaxNode`] bounding the attribute,
-/// and the outermost `ast::Attr`. Note that one node may map to multiple [`Meta`]s due to `cfg_attr`.
+/// The callback is passed the attribute and the outermost `ast::Attr`.
+/// Note that one node may map to multiple [`ast::Meta`]s due to `cfg_attr`.
+///
+/// `unsafe(attr)` are passed the inner attribute for now.
#[inline]
pub fn expand_cfg_attr<'a, BreakValue>(
attrs: impl Iterator<Item = ast::Attr>,
cfg_options: impl FnMut() -> &'a CfgOptions,
- mut callback: impl FnMut(Meta, &SyntaxNode, TextRange, &ast::Attr) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
expand_cfg_attr_with_doc_comments::<Infallible, _>(
attrs.map(Either::Left),
cfg_options,
- move |Either::Left((meta, container, range, top_attr))| {
- callback(meta, container, range, top_attr)
- },
+ move |Either::Left((meta, top_attr))| callback(meta, top_attr),
)
}
@@ -264,66 +112,47 @@ pub fn expand_cfg_attr<'a, BreakValue>(
pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>(
mut attrs: impl Iterator<Item = Either<ast::Attr, DocComment>>,
mut cfg_options: impl FnMut() -> &'a CfgOptions,
- mut callback: impl FnMut(
- Either<(Meta, &SyntaxNode, TextRange, &ast::Attr), DocComment>,
- ) -> ControlFlow<BreakValue>,
+ mut callback: impl FnMut(Either<(ast::Meta, ast::Attr), DocComment>) -> ControlFlow<BreakValue>,
) -> Option<BreakValue> {
let mut stack = SmallVec::<[_; 1]>::new();
- let result = attrs.try_for_each(|top_attr| {
- let top_attr = match top_attr {
- Either::Left(it) => it,
- Either::Right(comment) => return callback(Either::Right(comment)),
- };
- if let Some((attr_name, tt)) = top_attr.as_simple_call()
- && attr_name == "cfg_attr"
- {
- let mut tt_iter = TokenTreeChildren::new(&tt).peekable();
- let cfg = cfg::CfgExpr::parse_from_ast(&mut tt_iter);
- if cfg_options().check(&cfg) != Some(false) {
- stack.push((tt_iter, tt));
- while let Some((tt_iter, tt)) = stack.last_mut() {
- let Some((attr, range)) = Meta::extract_possibly_unsafe(tt_iter, tt) else {
- stack.pop();
- continue;
- };
- if let Meta::TokenTree { path, tt: nested_tt } = &attr
- && path.is1("cfg_attr")
- {
- let mut nested_tt_iter = TokenTreeChildren::new(nested_tt).peekable();
- let cfg = cfg::CfgExpr::parse_from_ast(&mut nested_tt_iter);
- if cfg_options().check(&cfg) != Some(false) {
- stack.push((nested_tt_iter, nested_tt.clone()));
- }
- } else {
- callback(Either::Left((attr, tt.syntax(), range, &top_attr)))?;
+ loop {
+ let (mut meta, top_attr) = if let Some(it) = stack.pop() {
+ it
+ } else {
+ let attr = attrs.next()?;
+ match attr {
+ Either::Left(attr) => {
+ let Some(meta) = attr.meta() else { continue };
+ stack.push((meta, attr));
+ }
+ Either::Right(doc_comment) => {
+ if let ControlFlow::Break(break_value) = callback(Either::Right(doc_comment)) {
+ return Some(break_value);
}
}
}
- } else if let Some(ast_meta) = top_attr.meta()
- && let Some(path) = ast_meta.path()
- {
- let path = AttrPath::extract(&path);
- let meta = if let Some(tt) = ast_meta.token_tree() {
- Meta::TokenTree { path, tt }
- } else if let Some(value) = ast_meta.expr() {
- let value =
- if let ast::Expr::Literal(value) = value { Some(value.token()) } else { None };
- let name =
- if path.segments.len() == 1 { Some(path.segments[0].clone()) } else { None };
- Meta::NamedKeyValue { name, value, path_range: path.range }
- } else {
- Meta::Path { path }
- };
- callback(Either::Left((
- meta,
- ast_meta.syntax(),
- ast_meta.syntax().text_range(),
- &top_attr,
- )))?;
+ continue;
+ };
+
+ while let ast::Meta::UnsafeMeta(unsafe_meta) = &meta {
+ let Some(inner) = unsafe_meta.meta() else { continue };
+ meta = inner;
}
- ControlFlow::Continue(())
- });
- result.break_value()
+
+ if let ast::Meta::CfgAttrMeta(meta) = meta {
+ let Some(cfg_predicate) = meta.cfg_predicate() else { continue };
+ let cfg_predicate = CfgExpr::parse_from_ast(cfg_predicate);
+ if cfg_options().check(&cfg_predicate) != Some(false) {
+ let prev_stack_len = stack.len();
+ stack.extend(meta.metas().map(|meta| (meta, top_attr.clone())));
+ stack[prev_stack_len..].reverse();
+ }
+ } else {
+ if let ControlFlow::Break(break_value) = callback(Either::Left((meta, top_attr))) {
+ return Some(break_value);
+ }
+ }
+ }
}
#[inline]
@@ -351,39 +180,33 @@ pub(crate) fn is_item_tree_filtered_attr(name: &str) -> bool {
pub fn collect_item_tree_attrs<'a, BreakValue>(
owner: &dyn ast::HasAttrs,
cfg_options: impl Fn() -> &'a CfgOptions,
- mut on_attr: impl FnMut(Meta, &SyntaxNode, &ast::Attr, TextRange) -> ControlFlow<BreakValue>,
+ mut on_attr: impl FnMut(ast::Meta, ast::Attr) -> ControlFlow<BreakValue>,
) -> Option<Either<BreakValue, CfgExpr>> {
let attrs = ast::attrs_including_inner(owner);
expand_cfg_attr(
attrs,
|| cfg_options(),
- |attr, container, range, top_attr| {
+ |attr, top_attr| {
// We filter builtin attributes that we don't need for nameres, because this saves memory.
// I only put the most common attributes, but if some attribute becomes common feel free to add it.
// Notice, however: for an attribute to be filtered out, it *must* not be shadowable with a macro!
let filter = match &attr {
- Meta::NamedKeyValue { name: Some(name), .. } => {
- is_item_tree_filtered_attr(name.text())
- }
- Meta::TokenTree { path, tt } if path.segments.len() == 1 => {
- let name = path.segments[0].text();
- if name == "cfg" {
- let cfg =
- CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(tt).peekable());
- if cfg_options().check(&cfg) == Some(false) {
- return ControlFlow::Break(Either::Right(cfg));
- }
- true
- } else {
- is_item_tree_filtered_attr(name)
+ ast::Meta::CfgMeta(attr) => {
+ let Some(cfg_predicate) = attr.cfg_predicate() else {
+ return ControlFlow::Continue(());
+ };
+ let cfg = CfgExpr::parse_from_ast(cfg_predicate);
+ if cfg_options().check(&cfg) == Some(false) {
+ return ControlFlow::Break(Either::Right(cfg));
}
+ true
}
- Meta::Path { path } => {
- path.segments.len() == 1 && is_item_tree_filtered_attr(path.segments[0].text())
- }
- _ => false,
+ _ => attr
+ .path()
+ .and_then(|path| path.as_one_segment())
+ .is_some_and(|segment| is_item_tree_filtered_attr(&segment)),
};
- if !filter && let ControlFlow::Break(v) = on_attr(attr, container, top_attr, range) {
+ if !filter && let ControlFlow::Break(v) = on_attr(attr, top_attr) {
return ControlFlow::Break(Either::Left(v));
}
ControlFlow::Continue(())
@@ -540,34 +363,32 @@ impl AttrId {
}
/// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
- /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
- /// attribute, and its desugared [`Meta`].
+ /// to `cfg_attr`) and its [`ast::Meta`].
pub fn find_attr_range<N: ast::HasAttrs>(
self,
db: &dyn ExpandDatabase,
krate: Crate,
owner: AstId<N>,
- ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
+ ) -> (ast::Attr, ast::Meta) {
self.find_attr_range_with_source(db, krate, &owner.to_node(db))
}
/// Returns the containing `ast::Attr` (note that it may contain other attributes as well due
- /// to `cfg_attr`), a `SyntaxNode` guaranteed to contain the attribute, the full range of the
- /// attribute, and its desugared [`Meta`].
+ /// to `cfg_attr`) and its [`ast::Meta`].
pub fn find_attr_range_with_source(
self,
db: &dyn ExpandDatabase,
krate: Crate,
owner: &dyn ast::HasAttrs,
- ) -> (ast::Attr, SyntaxNode, TextRange, Meta) {
+ ) -> (ast::Attr, ast::Meta) {
let cfg_options = OnceCell::new();
let mut index = 0;
let result = collect_item_tree_attrs(
owner,
|| cfg_options.get_or_init(|| krate.cfg_options(db)),
- |meta, container, top_attr, range| {
+ |meta, top_attr| {
if index == self.id {
- return ControlFlow::Break((top_attr.clone(), container.clone(), range, meta));
+ return ControlFlow::Break((top_attr, meta));
}
index += 1;
ControlFlow::Continue(())
@@ -588,9 +409,12 @@ impl AttrId {
owner: AstId<ast::Adt>,
derive_index: u32,
) -> TextRange {
- let (_, _, derive_attr_range, derive_attr) = self.find_attr_range(db, krate, owner);
- let Meta::TokenTree { tt, .. } = derive_attr else {
- return derive_attr_range;
+ let (_, derive_attr) = self.find_attr_range(db, krate, owner);
+ let ast::Meta::TokenTreeMeta(derive_attr) = derive_attr else {
+ return derive_attr.syntax().text_range();
+ };
+ let Some(tt) = derive_attr.token_tree() else {
+ return derive_attr.syntax().text_range();
};
// Fake the span map, as we don't really need spans here, just the offsets of the node in the file.
let span_map = RealSpanMap::absolute(span::EditionedFileId::current_edition(
@@ -605,11 +429,11 @@ impl AttrId {
let Some((_, _, derive_tts)) =
parse_path_comma_token_tree(db, &tt).nth(derive_index as usize)
else {
- return derive_attr_range;
+ return derive_attr.syntax().text_range();
};
let (Some(first_span), Some(last_span)) = (derive_tts.first_span(), derive_tts.last_span())
else {
- return derive_attr_range;
+ return derive_attr.syntax().text_range();
};
let start = first_span.range.start();
let end = last_span.range.end();
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs
index f208203c93..8f513a2bcf 100644
--- a/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -22,8 +22,9 @@ use crate::{
use syntax::{
ast::{
self, AstNode, FieldList, HasAttrs, HasGenericArgs, HasGenericParams, HasModuleItem,
- HasName, HasTypeBounds, edit_in_place::GenericParamsOwnerEdit, make,
+ HasName, HasTypeBounds, make,
},
+ syntax_editor::{GetOrCreateWhereClause, SyntaxEditor},
ted,
};
@@ -1150,11 +1151,9 @@ fn coerce_pointee_expand(
const ADDED_PARAM: &str = "__S";
- let where_clause = strukt.get_or_create_where_clause();
+ let mut new_predicates: Vec<ast::WherePred> = Vec::new();
{
- let mut new_predicates = Vec::new();
-
// # Rewrite generic parameter bounds
// For each bound `U: ..` in `struct<U: ..>`, make a new bound with `__S` in place of `#[pointee]`
// Example:
@@ -1196,16 +1195,13 @@ fn coerce_pointee_expand(
} else {
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_bounds,
- )
- .clone_for_update(),
- );
+ new_predicates.push(make::where_pred(
+ Either::Right(make::ty_path(make::path_from_segments(
+ [make::path_segment(new_bounds_target)],
+ false,
+ ))),
+ new_bounds,
+ ));
}
}
@@ -1235,7 +1231,7 @@ 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 where_clause.predicates() {
+ 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 };
@@ -1269,42 +1265,41 @@ fn coerce_pointee_expand(
);
}
}
-
- for new_predicate in new_predicates {
- where_clause.add_predicate(new_predicate);
- }
}
{
// # Add `Unsize<__S>` bound to `#[pointee]` at the generic parameter location
//
// Find the `#[pointee]` parameter and add an `Unsize<__S>` bound to it.
- where_clause.add_predicate(
- 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()],
- ),
- ],
- true,
- )))],
- )
- .clone_for_update(),
- );
+ 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()],
+ ),
+ ],
+ 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;
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index b3572a1cef..9962677a9d 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -828,7 +828,7 @@ fn include_expand(
let span_map = db.real_span_map(editioned_file_id);
// FIXME: Parse errors
ExpandResult::ok(syntax_node_to_token_tree(
- &db.parse(editioned_file_id).syntax_node(),
+ &editioned_file_id.parse(db).syntax_node(),
SpanMap::RealSpanMap(span_map),
span,
syntax_bridge::DocCommentDesugarMode::ProcMacro,
diff --git a/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs
index ccef9168ac..6258fac0e9 100644
--- a/crates/hir-expand/src/cfg_process.rs
+++ b/crates/hir-expand/src/cfg_process.rs
@@ -8,12 +8,12 @@ use parser::T;
use smallvec::SmallVec;
use syntax::{
AstNode, PreorderWithTokens, SyntaxElement, SyntaxNode, SyntaxToken, WalkEvent,
- ast::{self, HasAttrs, TokenTreeChildren},
+ ast::{self, HasAttrs},
};
use syntax_bridge::DocCommentDesugarMode;
use crate::{
- attrs::{AttrId, Meta, expand_cfg_attr, is_item_tree_filtered_attr},
+ attrs::{AstPathExt, AttrId, expand_cfg_attr, is_item_tree_filtered_attr},
db::ExpandDatabase,
fixup::{self, SyntaxFixupUndoInfo},
span_map::SpanMapRef,
@@ -24,7 +24,7 @@ struct ItemIsCfgedOut;
#[derive(Debug)]
struct ExpandedAttrToProcess {
- range: TextRange,
+ attr: ast::Meta,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -143,42 +143,29 @@ fn macro_input_callback(
});
attrs_idx = 0;
- let strip_current_item = expand_cfg_attr(
- node_attrs,
- &cfg_options,
- |attr, _container, range, top_attr| {
+ let strip_current_item =
+ expand_cfg_attr(node_attrs, &cfg_options, |attr, top_attr| {
// Find the attr.
while attrs[attrs_idx].range != top_attr.syntax().text_range() {
attrs_idx += 1;
}
let mut strip_current_attr = false;
- match attr {
- Meta::NamedKeyValue { name, .. } => {
- if name
- .is_none_or(|name| !is_item_tree_filtered_attr(name.text()))
- {
- strip_current_attr = should_strip_attr();
- }
- }
- Meta::TokenTree { path, tt } => {
- if path.is1("cfg") {
- let cfg_expr = CfgExpr::parse_from_ast(
- &mut TokenTreeChildren::new(&tt).peekable(),
- );
+ match &attr {
+ ast::Meta::CfgMeta(attr) => {
+ if let Some(cfg_predicate) = attr.cfg_predicate() {
+ let cfg_expr = CfgExpr::parse_from_ast(cfg_predicate);
if cfg_options().check(&cfg_expr) == Some(false) {
return ControlFlow::Break(ItemIsCfgedOut);
}
strip_current_attr = true;
- } else if path.segments.len() != 1
- || !is_item_tree_filtered_attr(path.segments[0].text())
- {
- strip_current_attr = should_strip_attr();
}
}
- Meta::Path { path } => {
- if path.segments.len() != 1
- || !is_item_tree_filtered_attr(path.segments[0].text())
+ _ => {
+ if attr
+ .path()
+ .as_one_segment()
+ .is_none_or(|name| !is_item_tree_filtered_attr(&name))
{
strip_current_attr = should_strip_attr();
}
@@ -188,12 +175,11 @@ fn macro_input_callback(
if !strip_current_attr {
attrs[attrs_idx]
.expanded_attrs
- .push(ExpandedAttrToProcess { range });
+ .push(ExpandedAttrToProcess { attr });
}
ControlFlow::Continue(())
- },
- );
+ });
attrs_idx = 0;
if strip_current_item.is_some() {
@@ -248,7 +234,7 @@ fn macro_input_callback(
};
match ast_attr.next_expanded_attr {
NextExpandedAttrState::NotStarted => {
- if token_range.start() >= expanded_attr.range.start() {
+ if token_range.start() >= expanded_attr.attr.syntax().text_range().start() {
// We started the next attribute.
let mut insert_tokens = Vec::with_capacity(3);
insert_tokens.push(tt::Leaf::Punct(tt::Punct {
@@ -278,7 +264,7 @@ fn macro_input_callback(
}
}
NextExpandedAttrState::InTheMiddle => {
- if token_range.start() >= expanded_attr.range.end() {
+ if token_range.start() >= expanded_attr.attr.syntax().text_range().end() {
// Finished the current attribute.
let insert_tokens = vec![tt::Leaf::Punct(tt::Punct {
char: ']',
@@ -329,12 +315,3 @@ pub(crate) fn attr_macro_input_to_token_tree(
fixups.undo_info,
)
}
-
-pub fn check_cfg_attr_value(
- db: &dyn ExpandDatabase,
- attr: &ast::TokenTree,
- krate: Crate,
-) -> Option<bool> {
- let cfg_expr = CfgExpr::parse_from_ast(&mut TokenTreeChildren::new(attr).peekable());
- krate.cfg_options(db).check(&cfg_expr)
-}
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index 020731cf9a..8dddddfabb 100644
--- a/crates/hir-expand/src/db.rs
+++ b/crates/hir-expand/src/db.rs
@@ -1,6 +1,6 @@
//! Defines database & queries for macro expansion.
-use base_db::{Crate, RootQueryDb};
+use base_db::{Crate, SourceDatabase};
use mbe::MatchedArmIndex;
use span::{AstIdMap, Edition, Span, SyntaxContext};
use syntax::{AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T, ast};
@@ -11,7 +11,6 @@ use crate::{
AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId,
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
- attrs::Meta,
builtin::pseudo_derive_attr_expansion,
cfg_process::attr_macro_input_to_token_tree,
declarative::DeclarativeMacroExpander,
@@ -48,7 +47,7 @@ pub enum TokenExpander {
}
#[query_group::query_group]
-pub trait ExpandDatabase: RootQueryDb {
+pub trait ExpandDatabase: SourceDatabase {
/// The proc macros. Do not use this! Use `proc_macros_for_crate()` instead.
#[salsa::input]
fn proc_macros(&self) -> Arc<ProcMacros>;
@@ -239,8 +238,15 @@ pub fn expand_speculative(
MacroCallKind::Attr { censored_attr_ids: attr_ids, .. } => {
if loc.def.is_attribute_derive() {
// for pseudo-derive expansion we actually pass the attribute itself only
- ast::Attr::cast(speculative_args.clone()).and_then(|attr| attr.token_tree()).map(
- |token_tree| {
+ ast::Attr::cast(speculative_args.clone())
+ .and_then(|attr| {
+ if let ast::Meta::TokenTreeMeta(meta) = attr.meta()? {
+ meta.token_tree()
+ } else {
+ None
+ }
+ })
+ .map(|token_tree| {
let mut tree = syntax_node_to_token_tree(
token_tree.syntax(),
span_map,
@@ -250,26 +256,26 @@ pub fn expand_speculative(
tree.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
tree.set_top_subtree_delimiter_span(tt::DelimSpan::from_single(span));
tree
- },
- )
+ })
} else {
// Attributes may have an input token tree, build the subtree and map for this as well
// then try finding a token id for our token if it is inside this input subtree.
let item = ast::Item::cast(speculative_args.clone())?;
- let (_, _, _, meta) =
+ let (_, meta) =
attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &item);
- match meta {
- Meta::TokenTree { tt, .. } => {
- let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
- tt.syntax(),
- span_map,
- span,
- DocCommentDesugarMode::ProcMacro,
- );
- attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
- Some(attr_arg)
- }
- _ => None,
+ if let ast::Meta::TokenTreeMeta(meta) = meta
+ && let Some(tt) = meta.token_tree()
+ {
+ let mut attr_arg = syntax_bridge::syntax_node_to_token_tree(
+ tt.syntax(),
+ span_map,
+ span,
+ DocCommentDesugarMode::ProcMacro,
+ );
+ attr_arg.set_top_subtree_delimiter_kind(tt::DelimiterKind::Invisible);
+ Some(attr_arg)
+ } else {
+ None
}
}
}
@@ -343,7 +349,7 @@ fn ast_id_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> AstIdMap {
/// file or a macro expansion.
fn parse_or_expand(db: &dyn ExpandDatabase, file_id: HirFileId) -> SyntaxNode {
match file_id {
- HirFileId::FileId(file_id) => db.parse(file_id).syntax_node(),
+ HirFileId::FileId(file_id) => file_id.parse(db).syntax_node(),
HirFileId::MacroFile(macro_file) => {
db.parse_macro_expansion(macro_file).value.0.syntax_node()
}
@@ -389,7 +395,7 @@ pub(crate) fn parse_with_map(
) -> (Parse<SyntaxNode>, SpanMap) {
match file_id {
HirFileId::FileId(file_id) => {
- (db.parse(file_id).to_syntax(), SpanMap::RealSpanMap(db.real_span_map(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;
@@ -501,11 +507,11 @@ fn macro_arg(db: &dyn ExpandDatabase, id: MacroCallId) -> MacroArgResult {
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
let node = ast_id.to_ptr(db).to_node(&root);
- let range = attr_ids
- .invoc_attr()
- .find_attr_range_with_source(db, loc.krate, &node)
- .3
- .path_range();
+ let (_, attr) = attr_ids.invoc_attr().find_attr_range_with_source(db, loc.krate, &node);
+ let range = attr
+ .path()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| attr.syntax().text_range());
let span = map.span_for_range(range);
let is_derive = matches!(loc.def.kind, MacroDefKind::BuiltInAttr(_, expander) if expander.is_derive());
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index 1726412275..4b2c6e7351 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -6,7 +6,7 @@ use base_db::Crate;
use span::{Edition, Span, SyntaxContext};
use stdx::TupleExt;
use syntax::{
- AstNode, AstToken,
+ AstNode,
ast::{self, HasAttrs},
};
use syntax_bridge::DocCommentDesugarMode;
@@ -15,7 +15,7 @@ use triomphe::Arc;
use crate::{
AstId, ExpandError, ExpandErrorKind, ExpandResult, HirFileId, Lookup, MacroCallId,
MacroCallStyle,
- attrs::{Meta, expand_cfg_attr},
+ attrs::{AstKeyValueMetaExt, AstPathExt, expand_cfg_attr},
db::ExpandDatabase,
hygiene::{Transparency, apply_mark},
tt,
@@ -92,11 +92,10 @@ impl DeclarativeMacroExpander {
expand_cfg_attr(
node.attrs(),
|| cfg_options.get_or_init(|| def_crate.cfg_options(db)),
- |attr, _, _, _| {
- if let Meta::NamedKeyValue { name: Some(name), value, .. } = attr
- && name.text() == "rustc_macro_transparency"
- && let Some(value) = value.and_then(ast::String::cast)
- && let Ok(value) = value.value()
+ |attr, _| {
+ if let ast::Meta::KeyValueMeta(attr) = attr
+ && attr.path().is1("rustc_macro_transparency")
+ && let Some(value) = attr.value_string()
{
match &*value {
"transparent" => ControlFlow::Break(Transparency::Transparent),
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index fce92c8a3e..71da560b15 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -198,7 +198,7 @@ trait FileIdToSyntax: Copy {
impl FileIdToSyntax for EditionedFileId {
fn file_syntax(self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- db.parse(self).syntax_node()
+ self.parse(db).syntax_node()
}
}
impl FileIdToSyntax for MacroCallId {
@@ -333,8 +333,8 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
)?;
let kind = self.kind();
- let value = db
- .parse(editioned_file_id)
+ let value = editioned_file_id
+ .parse(db)
.syntax_node()
.covering_element(range)
.ancestors()
@@ -521,7 +521,7 @@ impl<N: AstNode> InFile<N> {
)?;
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
- let anc = db.parse(editioned_file_id).syntax_node().covering_element(range);
+ let anc = editioned_file_id.parse(db).syntax_node().covering_element(range);
let value = anc.ancestors().find_map(N::cast)?;
Some(InRealFile::new(editioned_file_id, value))
}
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index 4b2c75ed38..8d42a24e2f 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -58,7 +58,6 @@ use crate::{
};
pub use crate::{
- cfg_process::check_cfg_attr_value,
files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
prettify_macro_expansion_::prettify_macro_expansion,
};
@@ -635,14 +634,12 @@ impl MacroCallLoc {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
}
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
- // FIXME: handle `cfg_attr`
- let (attr, _, _, _) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
+ let (_, attr) = derive_attr_index.find_attr_range(db, self.krate, *ast_id);
ast_id.with_value(attr.syntax().clone())
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
if self.def.is_attribute_derive() {
- let (attr, _, _, _) =
- attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
+ let (_, attr) = attr_ids.invoc_attr().find_attr_range(db, self.krate, *ast_id);
ast_id.with_value(attr.syntax().clone())
} else {
ast_id.with_value(ast_id.to_node(db).syntax().clone())
@@ -770,11 +767,11 @@ impl MacroCallKind {
}
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
// FIXME: should be the range of the macro name, not the whole derive
- derive_attr_index.find_attr_range(db, krate, ast_id).2
+ derive_attr_index.find_attr_range(db, krate, ast_id).1.syntax().text_range()
}
// FIXME: handle `cfg_attr`
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
- attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).2
+ attr_ids.invoc_attr().find_attr_range(db, krate, ast_id).1.syntax().text_range()
}
};
diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs
index 71d0b880ca..aa8603341b 100644
--- a/crates/hir-expand/src/span_map.rs
+++ b/crates/hir-expand/src/span_map.rs
@@ -86,7 +86,7 @@ pub(crate) fn real_span_map(
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());
- let tree = db.parse(editioned_file_id).tree();
+ let tree = editioned_file_id.parse(db).tree();
// This is an incrementality layer. Basically we can't use absolute ranges for our spans as that
// would mean we'd invalidate everything whenever we type. So instead we make the text ranges
// relative to some AstIds reducing the risk of invalidation as typing somewhere no longer
diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml
index 238d1b08ae..18426f3095 100644
--- a/crates/hir-ty/Cargo.toml
+++ b/crates/hir-ty/Cargo.toml
@@ -55,7 +55,7 @@ hir-expand.workspace = true
base-db.workspace = true
syntax.workspace = true
span.workspace = true
-thin-vec = "0.2.14"
+thin-vec = "0.2.16"
[dev-dependencies]
expect-test = "1.5.1"
diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs
index 92629b7a05..6a9b1671e7 100644
--- a/crates/hir-ty/src/builtin_derive.rs
+++ b/crates/hir-ty/src/builtin_derive.rs
@@ -20,8 +20,8 @@ use crate::{
GenericPredicates,
db::HirDatabase,
next_solver::{
- Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, StoredEarlyBinder,
- StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
+ AliasTy, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv,
+ StoredEarlyBinder, StoredTy, TraitRef, Ty, TyKind, fold::fold_tys, generics::Generics,
},
};
@@ -174,8 +174,11 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) ->
if matches!(loc.adt, AdtId::EnumId(_)) {
// Enums don't have extra bounds.
GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
- Clauses::new_from_slice(adt_predicates.explicit_predicates().skip_binder())
- .store(),
+ Clauses::new_from_iter(
+ interner,
+ adt_predicates.own_explicit_predicates().skip_binder(),
+ )
+ .store(),
))
} else {
simple_trait_predicates(interner, loc, generic_params, adt_predicates, trait_id)
@@ -191,7 +194,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) ->
));
};
let duplicated_bounds =
- adt_predicates.explicit_predicates().iter_identity_copied().filter_map(|pred| {
+ adt_predicates.explicit_predicates().iter_identity().filter_map(|pred| {
let mentions_pointee =
pred.visit_with(&mut MentionsPointee { pointee_param_idx }).is_break();
if !mentions_pointee {
@@ -212,7 +215,7 @@ pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) ->
interner,
adt_predicates
.explicit_predicates()
- .iter_identity_copied()
+ .iter_identity()
.chain(duplicated_bounds)
.chain(unsize_bound),
)
@@ -313,7 +316,7 @@ fn simple_trait_predicates<'db>(
interner,
adt_predicates
.explicit_predicates()
- .iter_identity_copied()
+ .iter_identity()
.chain(extra_predicates)
.chain(assoc_type_bounds),
)
@@ -339,7 +342,7 @@ fn extend_assoc_type_bounds<'db>(
type Result = ();
fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
- if let TyKind::Alias(AliasTyKind::Projection, _) = t.kind() {
+ if let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) = t.kind() {
self.assoc_type_bounds.push(
TraitRef::new_from_args(
self.interner,
@@ -440,7 +443,7 @@ mod tests {
format_to!(
predicates,
"{}\n\n",
- preds.iter().format_with("\n", |pred, formatter| formatter(&format_args!(
+ preds.format_with("\n", |pred, formatter| formatter(&format_args!(
"{pred:?}"
))),
);
@@ -543,49 +546,49 @@ struct WithGenerics<'a, T: Trait, const N: usize>(&'a [T; N], T::Assoc);
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Debug, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Debug, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Debug, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Clone, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Clone, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Clone, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Copy, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Copy, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Copy, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: PartialEq<[#1]>, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialEq<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): PartialEq<[Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. })]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Eq, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Eq, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Eq, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: PartialOrd<[#1]>, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): PartialOrd<[Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. })]>, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): PartialOrd<[Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. })]>, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Ord, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Ord, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Ord, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Trait, polarity:Positive), bound_vars: [] })
Clause(Binder { value: ConstArgHasType(#2, usize), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Sized, polarity:Positive), bound_vars: [] })
Clause(Binder { value: TraitPredicate(#1: Hash, polarity:Positive), bound_vars: [] })
- Clause(Binder { value: TraitPredicate(Alias(Projection, AliasTy { args: [#1], def_id: TypeAliasId("Assoc"), .. }): Hash, polarity:Positive), bound_vars: [] })
+ Clause(Binder { value: TraitPredicate(Alias(AliasTy { args: [#1], kind: Projection { def_id: TypeAliasId("Assoc") }, .. }): Hash, polarity:Positive), bound_vars: [] })
"#]],
);
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs
index 928396c63a..80e7e05d76 100644
--- a/crates/hir-ty/src/consteval.rs
+++ b/crates/hir-ty/src/consteval.rs
@@ -8,28 +8,30 @@ use hir_def::{
ConstId, EnumVariantId, ExpressionStoreOwnerId, GeneralConstId, GenericDefId, HasModule,
StaticId,
attrs::AttrFlags,
- builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::{Body, ExpressionStore},
hir::{Expr, ExprId, Literal},
};
use hir_expand::Lookup;
+use rustc_abi::Size;
+use rustc_apfloat::Float;
use rustc_type_ir::inherent::IntoKind;
+use stdx::never;
use triomphe::Arc;
use crate::{
- LifetimeElisionKind, MemoryMap, ParamEnvAndCrate, TyLoweringContext,
+ LifetimeElisionKind, ParamEnvAndCrate, TyLoweringContext,
db::HirDatabase,
display::DisplayTarget,
infer::InferenceContext,
- mir::{MirEvalError, MirLowerError},
+ mir::{MirEvalError, MirLowerError, pad16},
next_solver::{
- Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
- StoredConst, StoredGenericArgs, Ty, ValueConst,
+ Allocation, Const, ConstKind, Consts, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
+ ScalarInt, StoredAllocation, StoredGenericArgs, Ty, TyKind, ValTreeKind, default_types,
},
traits::StoredParamEnvAndCrate,
};
-use super::mir::{interpret_mir, lower_body_to_mir, pad16};
+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))
@@ -84,140 +86,87 @@ pub fn intern_const_ref<'a>(
db: &'a dyn HirDatabase,
value: &Literal,
ty: Ty<'a>,
- _krate: Crate,
+ krate: Crate,
) -> Const<'a> {
let interner = DbInterner::new_no_crate(db);
- let kind = match value {
- &Literal::Uint(i, builtin_ty)
- if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
- BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
- BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
- BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
- BuiltinUint::U128 => Box::new((i).to_le_bytes()),
- BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ let Ok(data_layout) = db.target_data_layout(krate) else {
+ return Const::error(interner);
+ };
+ let valtree = match (ty.kind(), value) {
+ (TyKind::Uint(uint), Literal::Uint(value, _)) => {
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(*value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- &Literal::Int(i, None)
- if ty
- .as_builtin()
- .is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
- BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
- BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
- BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
- BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()),
- BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ (TyKind::Uint(uint), Literal::Int(value, _)) => {
+ // `Literal::Int` is the default, so we also need to account for the type being uint.
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(*value as u128, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- &Literal::Int(i, builtin_ty)
- if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) =>
- {
- let memory = match ty.as_builtin() {
- Some(BuiltinType::Int(builtin_int)) => match builtin_int {
- BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>,
- BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()),
- BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()),
- BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()),
- BuiltinInt::I128 => Box::new((i).to_le_bytes()),
- BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()),
- },
- _ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
- };
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
- ))
+ (TyKind::Int(int), Literal::Int(value, _)) => {
+ let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_int(*value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
}
- Literal::Float(float_type_wrapper, builtin_float)
- if builtin_float.is_none()
- || ty.as_builtin() == builtin_float.map(BuiltinType::Float) =>
- {
- let memory = match ty.as_builtin().unwrap() {
- BuiltinType::Float(builtin_float) => match builtin_float {
- // FIXME:
- hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>,
- hir_def::builtin_type::BuiltinFloat::F32 => {
- Box::new(float_type_wrapper.to_f32().to_le_bytes())
- }
- hir_def::builtin_type::BuiltinFloat::F64 => {
- Box::new(float_type_wrapper.to_f64().to_le_bytes())
- }
- // FIXME:
- hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]),
- },
- _ => unreachable!(),
+ (TyKind::Bool, Literal::Bool(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
+ (TyKind::Char, Literal::Char(value)) => ValTreeKind::Leaf(ScalarInt::from(*value)),
+ (TyKind::Float(float), Literal::Float(value, _)) => {
+ let size = Size::from_bits(float.bit_width());
+ let value = match float {
+ rustc_ast_ir::FloatTy::F16 => value.to_f16().to_bits(),
+ rustc_ast_ir::FloatTy::F32 => value.to_f32().to_bits(),
+ rustc_ast_ir::FloatTy::F64 => value.to_f64().to_bits(),
+ rustc_ast_ir::FloatTy::F128 => value.to_f128().to_bits(),
};
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory, memory_map: MemoryMap::default() },
+ let scalar = ScalarInt::try_from_uint(value, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ (_, Literal::String(value)) => {
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]),
))
}
- Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
- )),
- Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes {
- memory: (*c as u32).to_le_bytes().into(),
- memory_map: MemoryMap::default(),
- },
- )),
- Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes {
- memory: symbol.as_str().as_bytes().into(),
- memory_map: MemoryMap::default(),
- },
- )),
- Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => {
- rustc_type_ir::ConstKind::Value(ValueConst::new(
- ty,
- ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() },
+ (_, Literal::ByteString(value)) => {
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ value.iter().map(|&byte| u8_values[usize::from(byte)]),
))
}
- // FIXME
- Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
- _ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
+ (_, Literal::CString(_)) => {
+ // FIXME:
+ return Const::error(interner);
+ }
+ _ => {
+ never!("mismatching type for literal");
+ return Const::error(interner);
+ }
};
- Const::new(interner, kind)
+ Const::new_valtree(interner, ty, valtree)
}
/// Interns a possibly-unknown target usize
pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
- intern_const_ref(
- db,
- &match value {
- Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)),
- None => {
- return Const::new(
- DbInterner::new_no_crate(db),
- rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
- );
- }
- },
- Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize),
- krate,
- )
+ let interner = DbInterner::new_no_crate(db);
+ let value = match value {
+ Some(value) => value,
+ None => {
+ return Const::error(interner);
+ }
+ };
+ let Ok(data_layout) = db.target_data_layout(krate) else {
+ return Const::error(interner);
+ };
+ let usize_ty = interner.default_types().types.usize;
+ let scalar = ScalarInt::try_from_uint(value, data_layout.pointer_size()).unwrap();
+ Const::new_valtree(interner, usize_ty, ValTreeKind::Leaf(scalar))
+}
+
+pub fn allocation_as_usize(ec: Allocation<'_>) -> u128 {
+ u128::from_le_bytes(pad16(&ec.memory, false))
}
pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
@@ -230,20 +179,30 @@ pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u
GeneralConstId::ConstId(id) => {
let subst = unevaluated_const.args;
let ec = db.const_eval(id, subst, None).ok()?;
- try_const_usize(db, ec)
+ Some(allocation_as_usize(ec))
}
GeneralConstId::StaticId(id) => {
let ec = db.const_eval_static(id).ok()?;
- try_const_usize(db, ec)
+ Some(allocation_as_usize(ec))
}
GeneralConstId::AnonConstId(_) => None,
},
- ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))),
+ ConstKind::Value(val) => {
+ if val.ty == default_types(db).types.usize {
+ Some(val.value.inner().to_leaf().to_uint_unchecked())
+ } else {
+ None
+ }
+ }
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
}
+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() {
ConstKind::Param(_) => None,
@@ -254,15 +213,21 @@ pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<
GeneralConstId::ConstId(id) => {
let subst = unevaluated_const.args;
let ec = db.const_eval(id, subst, None).ok()?;
- try_const_isize(db, &ec)
+ Some(allocation_as_isize(ec))
}
GeneralConstId::StaticId(id) => {
let ec = db.const_eval_static(id).ok()?;
- try_const_isize(db, &ec)
+ Some(allocation_as_isize(ec))
}
GeneralConstId::AnonConstId(_) => None,
},
- ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))),
+ ConstKind::Value(val) => {
+ if val.ty == default_types(db).types.isize {
+ Some(val.value.inner().to_leaf().to_int_unchecked())
+ } else {
+ None
+ }
+ }
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
@@ -299,11 +264,7 @@ pub(crate) fn const_eval_discriminant_variant(
.store(),
)?;
let c = interpret_mir(db, mir_body, false, None)?.0?;
- let c = if is_signed {
- try_const_isize(db, &c).unwrap()
- } else {
- try_const_usize(db, c).unwrap() as i128
- };
+ let c = if is_signed { allocation_as_isize(c) } else { allocation_as_usize(c) as i128 };
Ok(c)
}
@@ -341,7 +302,11 @@ pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'_, 'd
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 result;
+ return Const::new_from_allocation(
+ ctx.interner(),
+ &result,
+ ParamEnvAndCrate { param_env: ctx.table.param_env, krate: ctx.resolver.krate() },
+ );
}
Const::error(ctx.interner())
}
@@ -359,7 +324,7 @@ pub(crate) fn const_eval<'db>(
def: ConstId,
subst: GenericArgs<'db>,
trait_env: Option<ParamEnvAndCrate<'db>>,
-) -> Result<Const<'db>, ConstEvalError> {
+) -> Result<Allocation<'db>, ConstEvalError> {
return match const_eval_query(db, def, subst.store(), trait_env.map(|env| env.store())) {
Ok(konst) => Ok(konst.as_ref()),
Err(err) => Err(err.clone()),
@@ -371,7 +336,7 @@ pub(crate) fn const_eval<'db>(
def: ConstId,
subst: StoredGenericArgs,
trait_env: Option<StoredParamEnvAndCrate>,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
let body = db.monomorphized_mir_body(
def.into(),
subst,
@@ -392,7 +357,7 @@ pub(crate) fn const_eval<'db>(
_: ConstId,
_: StoredGenericArgs,
_: Option<StoredParamEnvAndCrate>,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
}
@@ -400,7 +365,7 @@ pub(crate) fn const_eval<'db>(
pub(crate) fn const_eval_static<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
-) -> Result<Const<'db>, ConstEvalError> {
+) -> Result<Allocation<'db>, ConstEvalError> {
return match const_eval_static_query(db, def) {
Ok(konst) => Ok(konst.as_ref()),
Err(err) => Err(err.clone()),
@@ -410,7 +375,7 @@ pub(crate) fn const_eval_static<'db>(
pub(crate) fn const_eval_static_query<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
let interner = DbInterner::new_no_crate(db);
let body = db.monomorphized_mir_body(
def.into(),
@@ -430,7 +395,7 @@ pub(crate) fn const_eval_static<'db>(
_: &dyn HirDatabase,
_: salsa::Id,
_: StaticId,
- ) -> Result<StoredConst, ConstEvalError> {
+ ) -> Result<StoredAllocation, ConstEvalError> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 31cf86476f..723fa0fc68 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -1,21 +1,20 @@
-use base_db::RootQueryDb;
+use base_db::all_crates;
use hir_def::signatures::ConstSignature;
use hir_expand::EditionedFileId;
use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
};
-use rustc_type_ir::inherent::IntoKind;
use test_fixture::WithFixture;
use test_utils::skip_slow_tests;
use crate::{
MemoryMap,
- consteval::try_const_usize,
+ consteval::allocation_as_usize,
db::HirDatabase,
display::DisplayTarget,
mir::pad16,
- next_solver::{Const, ConstBytes, ConstKind, DbInterner, GenericArgs},
+ next_solver::{Allocation, DbInterner, GenericArgs},
setup_tracing,
test_db::TestDB,
};
@@ -45,7 +44,11 @@ fn check_fail(
crate::attach_db(&db, || match eval_goal(&db, file_id) {
Ok(_) => panic!("Expected fail, but it succeeded"),
Err(e) => {
- assert!(error(simplify(e.clone())), "Actual error was: {}", pretty_print_err(e, &db))
+ assert!(
+ error(simplify(e.clone())),
+ "Actual error was: {}\n{e:?}",
+ pretty_print_err(e.clone(), &db)
+ )
}
})
}
@@ -94,13 +97,7 @@ fn check_answer(
panic!("Error in evaluating goal: {err}");
}
};
- match r.kind() {
- ConstKind::Value(value) => {
- let ConstBytes { memory, memory_map } = value.value.inner();
- check(memory, memory_map);
- }
- _ => panic!("Expected number but found {r:?}"),
- }
+ check(&r.memory, &r.memory_map);
});
}
@@ -108,7 +105,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String {
let mut err = String::new();
let span_formatter = |file, range| format!("{file:?} {range:?}");
let display_target =
- DisplayTarget::from_crate(db, *db.all_crates().last().expect("no crate graph present"));
+ DisplayTarget::from_crate(db, *all_crates(db).last().expect("no crate graph present"));
match e {
ConstEvalError::MirLowerError(e) => {
e.pretty_print(&mut err, db, span_formatter, display_target)
@@ -121,7 +118,7 @@ fn pretty_print_err(e: ConstEvalError, db: &TestDB) -> String {
err
}
-fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const<'_>, ConstEvalError> {
+fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Allocation<'_>, ConstEvalError> {
let _tracing = setup_tracing();
let interner = DbInterner::new_no_crate(db);
let module_id = db.module_for_file(file_id.file_id(db));
@@ -1795,14 +1792,14 @@ const GOAL: i32 = {
fn closure_capture_unsized_type() {
check_number(
r#"
- //- minicore: fn, copy, slice, index, coerce_unsized
+ //- minicore: fn, copy, slice, index, coerce_unsized, sized
fn f<T: A>(x: &<T as A>::Ty) -> &<T as A>::Ty {
let c = || &*x;
c()
}
trait A {
- type Ty;
+ type Ty: ?Sized;
}
impl A for i32 {
@@ -1813,7 +1810,7 @@ fn closure_capture_unsized_type() {
let k: &[u8] = &[1, 2, 3];
let k = f::<i32>(k);
k[0] + k[1] + k[2]
- }
+ };
"#,
6,
);
@@ -2524,7 +2521,7 @@ fn enums() {
);
crate::attach_db(&db, || {
let r = eval_goal(&db, file_id).unwrap();
- assert_eq!(try_const_usize(&db, r), Some(1));
+ assert_eq!(allocation_as_usize(r), 1);
})
}
@@ -2537,7 +2534,15 @@ fn const_loop() {
const F2: i32 = 2 * F1;
const GOAL: i32 = F3;
"#,
- |e| e == ConstEvalError::MirLowerError(MirLowerError::Loop),
+ |e| {
+ if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e
+ && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner
+ {
+ true
+ } else {
+ false
+ }
+ },
);
}
@@ -2940,6 +2945,14 @@ fn recursive_adt() {
TAG_TREE
};
"#,
- |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::Loop)),
+ |e| {
+ if let ConstEvalError::MirEvalError(MirEvalError::ConstEvalError(_, inner)) = e
+ && let ConstEvalError::MirLowerError(MirLowerError::Loop) = *inner
+ {
+ true
+ } else {
+ false
+ }
+ },
);
}
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index a0fb75397a..3bf2d9a6a6 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -7,7 +7,7 @@ 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, hir::ExprId, layout::TargetDataLayout,
+ db::DefDatabase, expr_store::ExpressionStore, hir::ExprId, layout::TargetDataLayout,
};
use la_arena::ArenaMap;
use salsa::plumbing::AsId;
@@ -21,8 +21,8 @@ use crate::{
lower::{Diagnostics, GenericDefaults},
mir::{BorrowckResult, MirBody, MirLowerError},
next_solver::{
- Const, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder, StoredGenericArgs,
- StoredTy, TraitRef, Ty, VariancesOf,
+ Allocation, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredEarlyBinder,
+ StoredGenericArgs, StoredTy, TraitRef, Ty, VariancesOf,
},
traits::{ParamEnvAndCrate, StoredParamEnvAndCrate},
};
@@ -68,11 +68,11 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
def: ConstId,
subst: GenericArgs<'db>,
trait_env: Option<ParamEnvAndCrate<'db>>,
- ) -> Result<Const<'db>, ConstEvalError>;
+ ) -> Result<Allocation<'db>, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_static)]
#[salsa::transparent]
- fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Const<'db>, ConstEvalError>;
+ fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Allocation<'db>, ConstEvalError>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)]
@@ -200,12 +200,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::interned]
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
- #[salsa::interned]
- fn intern_closure(&self, id: InternedClosure) -> InternedClosureId;
-
- #[salsa::interned]
- fn intern_coroutine(&self, id: InternedCoroutine) -> InternedCoroutineId;
-
#[salsa::invoke(crate::variance::variances_of)]
#[salsa::transparent]
fn variances_of<'db>(&'db self, def: GenericDefId) -> VariancesOf<'db>;
@@ -238,17 +232,87 @@ pub struct InternedOpaqueTyId {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct InternedClosure(pub ExpressionStoreOwnerId, pub ExprId);
-#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
+#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)]
#[derive(PartialOrd, Ord)]
pub struct InternedClosureId {
pub loc: InternedClosure,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct InternedCoroutine(pub ExpressionStoreOwnerId, pub ExprId);
+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];
+ assert!(
+ matches!(
+ expr,
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::Closure,
+ ..
+ }
+ ),
+ "expected a closure, found {expr:?}"
+ );
+ }
+
+ Self::new_impl(db, loc)
+ }
+}
-#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
+#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)]
#[derive(PartialOrd, Ord)]
pub struct InternedCoroutineId {
- pub loc: InternedCoroutine,
+ pub loc: InternedClosure,
+}
+
+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];
+ assert!(
+ matches!(
+ expr,
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::Coroutine(_)
+ | hir_def::hir::ClosureKind::AsyncBlock { .. },
+ ..
+ }
+ ),
+ "expected a coroutine, found {expr:?}"
+ );
+ }
+
+ Self::new_impl(db, loc)
+ }
+}
+
+#[salsa_macros::interned(constructor = new_impl, no_lifetime, debug, revisions = usize::MAX)]
+#[derive(PartialOrd, Ord)]
+pub struct InternedCoroutineClosureId {
+ pub loc: InternedClosure,
+}
+
+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];
+ assert!(
+ matches!(
+ expr,
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::AsyncClosure,
+ ..
+ }
+ ),
+ "expected a coroutine closure, found {expr:?}"
+ );
+ }
+
+ Self::new_impl(db, loc)
+ }
}
diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs
index 33d9dd538d..068118c705 100644
--- a/crates/hir-ty/src/diagnostics/expr.rs
+++ b/crates/hir-ty/src/diagnostics/expr.rs
@@ -146,7 +146,7 @@ impl<'db> ExprValidator<'db> {
Expr::If { .. } => {
self.check_for_unnecessary_else(id, expr);
}
- Expr::Block { .. } | Expr::Async { .. } | Expr::Unsafe { .. } => {
+ Expr::Block { .. } | Expr::Unsafe { .. } => {
self.validate_block(expr);
}
_ => {}
@@ -238,8 +238,7 @@ impl<'db> ExprValidator<'db> {
if (pat_ty == scrut_ty
|| scrut_ty
.as_reference()
- .map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
- .unwrap_or(false))
+ .is_none_or(|(match_expr_ty, ..)| match_expr_ty == pat_ty))
&& types_of_subpatterns_do_match(arm.pat, self.body, self.infer)
{
// If we had a NotUsefulMatchArm diagnostic, we could
@@ -325,10 +324,7 @@ impl<'db> ExprValidator<'db> {
}
fn validate_block(&mut self, expr: &Expr) {
- let (Expr::Block { statements, .. }
- | Expr::Async { statements, .. }
- | Expr::Unsafe { statements, .. }) = expr
- else {
+ let (Expr::Block { statements, .. } | Expr::Unsafe { statements, .. }) = expr else {
return;
};
let pattern_arena = Arena::new();
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_util.rs b/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
index c6a26cdd1d..0b39692e46 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_util.rs
@@ -38,6 +38,12 @@ pub(crate) trait EnumerateAndAdjustIterator {
}
impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
+ /// When there is a list of items with a gap of an unknown length inside, and another list
+ /// of item it should be zipped against, this operates on the list with the gap and returns,
+ /// for each item, the index it should match in the other list.
+ ///
+ /// When compiling Rust, such situation often occurs for tuple structs/tuples with a rest pattern
+ /// that should be matched against the fields.
fn enumerate_and_adjust(
self,
expected_len: usize,
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 09c648139c..ee33f7d158 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -406,7 +406,7 @@ impl<'db> UnsafeVisitor<'db> {
});
return;
}
- Expr::Block { statements, .. } | Expr::Async { statements, .. } => {
+ Expr::Block { statements, .. } => {
self.walk_pats_top(
statements.iter().filter_map(|statement| match statement {
&Statement::Let { pat, .. } => Some(pat),
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index d680588645..e4a8def442 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -39,8 +39,7 @@ use rustc_apfloat::{
use rustc_ast_ir::FloatTy;
use rustc_hash::FxHashSet;
use rustc_type_ir::{
- AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, CoroutineClosureArgsParts, RegionKind,
- Upcast,
+ AliasTyKind, BoundVarIndexKind, CoroutineArgsParts, RegionKind, Upcast,
inherent::{AdtDef, GenericArgs as _, IntoKind, Term as _, Ty as _, Tys as _},
};
use smallvec::SmallVec;
@@ -48,16 +47,17 @@ use span::Edition;
use stdx::never;
use crate::{
- CallableDefId, FnAbi, ImplTraitId, InferenceResult, MemoryMap, ParamEnvAndCrate, consteval,
- db::{HirDatabase, InternedClosure, InternedCoroutine},
+ CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval,
+ db::{HirDatabase, InternedClosure},
generics::generics,
layout::Layout,
lower::GenericPredicates,
mir::pad16,
next_solver::{
- AliasTy, Clause, ClauseKind, Const, ConstKind, DbInterner, ExistentialPredicate, FnSig,
- GenericArg, GenericArgKind, GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId,
- StoredEarlyBinder, StoredTy, Term, TermKind, TraitRef, Ty, TyKind, TypingMode,
+ 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,
abi::Safety,
infer::{DbInternerInferExt, traits::ObligationCause},
},
@@ -640,14 +640,14 @@ fn write_projection<'db>(
// 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_copied()
+ .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(AliasTyKind::Projection, a) = ty.kind() else {
+ let TyKind::Alias(a) = ty.kind() else {
return false;
};
a == *alias
@@ -658,7 +658,7 @@ fn write_projection<'db>(
write_bounds_like_dyn_trait_with_prefix(
f,
"impl",
- Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)),
+ Either::Left(Ty::new_alias(f.interner, *alias)),
&bounds,
SizedByDefault::NotSized,
needs_parens_if_multi,
@@ -674,7 +674,7 @@ fn write_projection<'db>(
write!(
f,
">::{}",
- TypeAliasSignature::of(f.db, alias.def_id.expect_type_alias())
+ TypeAliasSignature::of(f.db, alias.kind.def_id().expect_type_alias())
.name
.display(f.db, f.edition())
)?;
@@ -692,6 +692,12 @@ impl<'db> HirDisplay<'db> for GenericArg<'db> {
}
}
+impl<'db> HirDisplay<'db> for Allocation<'db> {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
+ render_const_scalar(f, &self.memory, &self.memory_map, self.ty)
+ }
+}
+
impl<'db> HirDisplay<'db> for Const<'db> {
fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
match self.kind() {
@@ -711,12 +717,7 @@ impl<'db> HirDisplay<'db> for Const<'db> {
f.end_location_link();
Ok(())
}
- ConstKind::Value(const_bytes) => render_const_scalar(
- f,
- &const_bytes.value.inner().memory,
- &const_bytes.value.inner().memory_map,
- const_bytes.ty,
- ),
+ 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))?;
@@ -1006,7 +1007,7 @@ fn render_const_scalar_inner<'db>(
TyKind::Pat(_, _) => f.write_str("<pat>"),
TyKind::Error(..)
| TyKind::Placeholder(_)
- | TyKind::Alias(_, _)
+ | TyKind::Alias(..)
| TyKind::Param(_)
| TyKind::Bound(_, _)
| TyKind::Infer(_) => f.write_str("<placeholder-or-unknown-type>"),
@@ -1015,6 +1016,151 @@ fn render_const_scalar_inner<'db>(
}
}
+fn render_const_scalar_from_valtree<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ ty: Ty<'db>,
+ valtree: 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);
+ render_const_scalar_from_valtree_inner(f, ty, valtree, param_env)
+}
+
+fn render_const_scalar_from_valtree_inner<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ ty: Ty<'db>,
+ valtree: ValTree<'db>,
+ _param_env: ParamEnv<'db>,
+) -> Result {
+ use TyKind;
+ match ty.kind() {
+ TyKind::Bool => write!(f, "{}", valtree.inner().to_leaf().try_to_bool().unwrap()),
+ TyKind::Char => {
+ let it = valtree.inner().to_leaf().to_u32();
+ let Ok(c) = char::try_from(it) else {
+ return f.write_str("<unicode-error>");
+ };
+ write!(f, "{c:?}")
+ }
+ TyKind::Int(_) => {
+ let it = valtree.inner().to_leaf().to_int_unchecked();
+ write!(f, "{it}")
+ }
+ TyKind::Uint(_) => {
+ let it = valtree.inner().to_leaf().to_uint_unchecked();
+ write!(f, "{it}")
+ }
+ TyKind::Float(fl) => match fl {
+ FloatTy::F16 => {
+ // FIXME(#17451): Replace with builtins once they are stabilised.
+ let it = f16::from_bits(valtree.inner().to_leaf().to_u16() as u128);
+ let s = it.to_string();
+ if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
+ // Match Rust debug formatting
+ write!(f, "{s}.0")
+ } else {
+ write!(f, "{s}")
+ }
+ }
+ FloatTy::F32 => {
+ let it = f32::from_bits(valtree.inner().to_leaf().to_u32());
+ write!(f, "{it:?}")
+ }
+ FloatTy::F64 => {
+ let it = f64::from_bits(valtree.inner().to_leaf().to_u64());
+ write!(f, "{it:?}")
+ }
+ FloatTy::F128 => {
+ // FIXME(#17451): Replace with builtins once they are stabilised.
+ let it = f128::from_bits(valtree.inner().to_leaf().to_u128());
+ let s = it.to_string();
+ if s.strip_prefix('-').unwrap_or(&s).chars().all(|c| c.is_ascii_digit()) {
+ // Match Rust debug formatting
+ write!(f, "{s}.0")
+ } else {
+ write!(f, "{s}")
+ }
+ }
+ },
+ TyKind::Ref(_, inner_ty, _) => {
+ render_const_scalar_from_valtree_inner(f, inner_ty, valtree, _param_env)
+ }
+ TyKind::Str => {
+ let bytes = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => Some(value.value.inner().to_leaf().to_u8()),
+ _ => None,
+ })
+ .collect::<Option<Vec<_>>>();
+ let Some(bytes) = bytes else { return f.write_str("<invalid-str>") };
+ let s = std::str::from_utf8(&bytes).unwrap_or("<utf8-error>");
+ write!(f, "{s:?}")
+ }
+ TyKind::Slice(inner_ty) | TyKind::Array(inner_ty, _) => {
+ let mut first = true;
+ write!(f, "[")?;
+ for item in valtree.inner().to_branch() {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+ let ConstKind::Value(value) = item.kind() else {
+ return f.write_str("<invalid-const>");
+ };
+ render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;
+ }
+ write!(f, "]")
+ }
+ TyKind::Tuple(tys) => {
+ let mut first = true;
+ write!(f, "(")?;
+ for (inner_ty, item) in std::iter::zip(tys, valtree.inner().to_branch()) {
+ if !first {
+ write!(f, ", ")?;
+ } else {
+ first = false;
+ }
+ let ConstKind::Value(value) = item.kind() else {
+ return f.write_str("<invalid-const>");
+ };
+ render_const_scalar_from_valtree_inner(f, inner_ty, value.value, _param_env)?;
+ }
+ write!(f, ")")
+ }
+ TyKind::Adt(..) => {
+ // FIXME: ADTs, requires `adt_const_params`.
+ f.write_str("<adt>")
+ }
+ TyKind::FnDef(..) => ty.hir_fmt(f),
+ TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {
+ let it = valtree.inner().to_leaf().to_uint_unchecked();
+ write!(f, "{it:#X} as ")?;
+ ty.hir_fmt(f)
+ }
+ TyKind::Never => f.write_str("!"),
+ TyKind::Closure(_, _) => f.write_str("<closure>"),
+ TyKind::Coroutine(_, _) => f.write_str("<coroutine>"),
+ TyKind::CoroutineWitness(_, _) => f.write_str("<coroutine-witness>"),
+ TyKind::CoroutineClosure(_, _) => f.write_str("<coroutine-closure>"),
+ TyKind::UnsafeBinder(_) => f.write_str("<unsafe-binder>"),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => f.write_str("<extern-type>"),
+ TyKind::Pat(_, _) => f.write_str("<pat>"),
+ TyKind::Error(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(_, _)
+ | TyKind::Infer(_) => f.write_str("<placeholder-or-unknown-type>"),
+ TyKind::Dynamic(_, _) => f.write_str("<dyn-trait>"),
+ }
+}
+
fn render_variant_after_name<'db>(
data: &VariantFields,
f: &mut HirFormatter<'_, 'db>,
@@ -1281,7 +1427,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?;
}
- TyKind::Alias(AliasTyKind::Projection, alias_ty) => {
+ TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => {
write_projection(f, &alias_ty, trait_bounds_need_parens)?
}
TyKind::Foreign(alias) => {
@@ -1290,8 +1436,8 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
write!(f, "{}", type_alias.name.display(f.db, f.edition()))?;
f.end_location_link();
}
- TyKind::Alias(AliasTyKind::Opaque, alias_ty) => {
- let opaque_ty_id = match alias_ty.def_id {
+ TyKind::Alias(alias_ty @ AliasTy { kind: AliasTyKind::Opaque { def_id }, .. }) => {
+ let opaque_ty_id = match def_id {
SolverDefId::InternedOpaqueTyId(id) => id,
_ => unreachable!(),
};
@@ -1349,9 +1495,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe);
let sig = sig.skip_binder();
- let InternedClosure(owner, _) = db.lookup_intern_closure(id);
- let infer = InferenceResult::of(db, owner);
- let (_, kind) = infer.closure_info(id);
+ let kind = substs.as_closure().kind();
match f.closure_style {
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
ClosureStyle::RANotation => write!(f, "|")?,
@@ -1403,26 +1547,16 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
_ => (),
}
- let CoroutineClosureArgsParts { closure_kind_ty, signature_parts_ty, .. } =
- args.split_coroutine_closure_args();
- let kind = closure_kind_ty.to_opt_closure_kind().unwrap();
+ 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 TyKind::FnPtr(coroutine_sig, _) = signature_parts_ty.kind() else {
- unreachable!("invalid coroutine closure signature");
- };
+ let coroutine_sig = args.as_coroutine_closure().coroutine_closure_sig();
let coroutine_sig = coroutine_sig.skip_binder();
- let coroutine_inputs = coroutine_sig.inputs();
- let TyKind::Tuple(coroutine_inputs) = coroutine_inputs[1].kind() else {
- unreachable!("invalid coroutine closure signature");
- };
- let TyKind::Tuple(coroutine_output) = coroutine_sig.output().kind() else {
- unreachable!("invalid coroutine closure signature");
- };
- let coroutine_output = coroutine_output.as_slice()[1];
+ let coroutine_inputs = coroutine_sig.tupled_inputs_ty.tuple_fields();
+ let coroutine_output = coroutine_sig.return_ty;
match f.closure_style {
ClosureStyle::ImplFn => write!(f, "impl {kind}(")?,
ClosureStyle::RANotation => write!(f, "async |")?,
@@ -1466,7 +1600,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
TypeParamProvenance::ArgumentImplTrait => {
let bounds = GenericPredicates::query_all(f.db, param.id.parent())
- .iter_identity_copied()
+ .iter_identity()
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == *self,
ClauseKind::Projection(proj) => proj.self_ty() == *self,
@@ -1536,17 +1670,16 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
}
TyKind::Infer(..) => write!(f, "_")?,
TyKind::Coroutine(coroutine_id, subst) => {
- let InternedCoroutine(owner, expr_id) = coroutine_id.0.loc(db);
+ let InternedClosure(owner, expr_id) = coroutine_id.0.loc(db);
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::Async,
+ closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. },
..
- }
- | hir_def::hir::Expr::Async { .. } => {
+ } => {
let future_trait = f.lang_items().Future;
let output = future_trait.and_then(|t| {
t.trait_items(db)
@@ -1597,7 +1730,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
TyKind::CoroutineWitness(..) => write!(f, "{{coroutine witness}}")?,
TyKind::Pat(_, _) => write!(f, "{{pat}}")?,
TyKind::UnsafeBinder(_) => write!(f, "{{unsafe binder}}")?,
- TyKind::Alias(_, _) => write!(f, "{{alias}}")?,
+ TyKind::Alias(..) => write!(f, "{{alias}}")?,
}
Ok(())
}
diff --git a/crates/hir-ty/src/drop.rs b/crates/hir-ty/src/drop.rs
index ddc4e4ce85..0d25d7dbd1 100644
--- a/crates/hir-ty/src/drop.rs
+++ b/crates/hir-ty/src/drop.rs
@@ -1,15 +1,16 @@
//! Utilities for computing drop info about types.
use hir_def::{
- AdtId,
+ AdtId, ImplId,
signatures::{StructFlags, StructSignature},
};
use rustc_hash::FxHashSet;
-use rustc_type_ir::inherent::{AdtDef, IntoKind};
+use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind};
use stdx::never;
use crate::{
- InferenceResult, consteval,
+ consteval,
+ db::HirDatabase,
method_resolution::TraitImpls,
next_solver::{
DbInterner, ParamEnv, SimplifiedType, Ty, TyKind,
@@ -18,24 +19,23 @@ use crate::{
},
};
-fn has_destructor(interner: DbInterner<'_>, adt: AdtId) -> bool {
- let db = interner.db;
+#[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,
};
- let Some(drop_trait) = interner.lang_items().Drop else {
- return false;
- };
+ let interner = DbInterner::new_with(db, module.krate(db));
+ let drop_trait = interner.lang_items().Drop?;
let impls = match module.block(db) {
Some(block) => match TraitImpls::for_block(db, block) {
Some(it) => &**it,
- None => return false,
+ None => return None,
},
None => TraitImpls::for_crate(db, module.krate(db)),
};
- !impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).0.is_empty()
+ impls.for_trait_and_self_ty(drop_trait, &SimplifiedType::Adt(adt.into())).0.first().copied()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -71,7 +71,7 @@ fn has_drop_glue_impl<'db>(
match ty.kind() {
TyKind::Adt(adt_def, subst) => {
let adt_id = adt_def.def_id().0;
- if has_destructor(infcx.interner, adt_id) {
+ if adt_def.destructor(infcx.interner).is_some() {
return DropGlue::HasDropGlue;
}
match adt_id {
@@ -132,21 +132,17 @@ fn has_drop_glue_impl<'db>(
has_drop_glue_impl(infcx, ty, env, visited)
}
TyKind::Slice(ty) => has_drop_glue_impl(infcx, ty, env, visited),
- TyKind::Closure(closure_id, subst) => {
- let owner = db.lookup_intern_closure(closure_id.0).0;
- let infer = InferenceResult::of(db, owner);
- let (captures, _) = infer.closure_info(closure_id.0);
- let env = db.trait_environment(owner);
- captures
- .iter()
- .map(|capture| has_drop_glue_impl(infcx, capture.ty(db, subst), env, visited))
- .max()
- .unwrap_or(DropGlue::None)
+ TyKind::Closure(_, args) => {
+ has_drop_glue_impl(infcx, args.as_closure().tupled_upvars_ty(), env, visited)
}
- // FIXME: Handle coroutines.
- TyKind::Coroutine(..) | TyKind::CoroutineWitness(..) | TyKind::CoroutineClosure(..) => {
- DropGlue::None
+ TyKind::Coroutine(_, args) => {
+ has_drop_glue_impl(infcx, args.as_coroutine().tupled_upvars_ty(), env, visited)
+ }
+ TyKind::CoroutineClosure(_, args) => {
+ has_drop_glue_impl(infcx, args.as_coroutine_closure().tupled_upvars_ty(), env, visited)
}
+ // FIXME: Coroutine witness.
+ TyKind::CoroutineWitness(..) => DropGlue::None,
TyKind::Ref(..)
| TyKind::RawPtr(..)
| TyKind::FnDef(..)
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index 4c300affd8..ba63343d49 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -21,8 +21,9 @@ use crate::{
db::{HirDatabase, InternedOpaqueTyId},
lower::{GenericPredicates, associated_ty_item_bounds},
next_solver::{
- Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, ParamTy,
- SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, mk_param,
+ AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv,
+ ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt,
+ mk_param,
},
traits::next_trait_solve_in_ctxt,
};
@@ -141,7 +142,7 @@ 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_copied()).any(|pred| {
+ elaborate::elaborate(interner, predicates.iter_identity()).any(|pred| {
match pred.kind().skip_binder() {
ClauseKind::Trait(trait_pred) => {
if sized == trait_pred.def_id().0
@@ -164,7 +165,7 @@ pub fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> b
// 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_copied()
+ .iter_identity()
.any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No))
}
@@ -239,30 +240,30 @@ fn contains_illegal_self_type_reference<'db, T: rustc_type_ir::TypeVisitable<DbI
match ty.kind() {
rustc_type_ir::TyKind::Param(param) if param.index == 0 => ControlFlow::Break(()),
rustc_type_ir::TyKind::Param(_) => ControlFlow::Continue(()),
- rustc_type_ir::TyKind::Alias(AliasTyKind::Projection, proj) => {
- match self.allow_self_projection {
- AllowSelfProjection::Yes => {
- let trait_ = proj.trait_def_id(interner);
- let trait_ = match trait_ {
- SolverDefId::TraitId(id) => id,
- _ => unreachable!(),
- };
- if self.super_traits.is_none() {
- self.super_traits = Some(
- elaborate::supertrait_def_ids(interner, self.trait_.into())
- .map(|super_trait| super_trait.0)
- .collect(),
- )
- }
- if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) {
- ControlFlow::Continue(())
- } else {
- ty.super_visit_with(self)
- }
+ rustc_type_ir::TyKind::Alias(
+ 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!(),
+ };
+ if self.super_traits.is_none() {
+ self.super_traits = Some(
+ elaborate::supertrait_def_ids(interner, self.trait_.into())
+ .map(|super_trait| super_trait.0)
+ .collect(),
+ )
+ }
+ if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) {
+ ControlFlow::Continue(())
+ } else {
+ ty.super_visit_with(self)
}
- AllowSelfProjection::No => ty.super_visit_with(self),
}
- }
+ AllowSelfProjection::No => ty.super_visit_with(self),
+ },
_ => ty.super_visit_with(self),
}
}
@@ -360,8 +361,8 @@ where
cb(MethodViolationCode::UndispatchableReceiver)?;
}
- let predicates = GenericPredicates::query_own(db, func.into());
- for pred in predicates.iter_identity_copied() {
+ let predicates = GenericPredicates::query_own_explicit(db, func.into());
+ for pred in predicates.iter_identity() {
let pred = pred.kind().skip_binder();
if matches!(pred, ClauseKind::TypeOutlives(_)) {
@@ -459,7 +460,7 @@ fn receiver_is_dispatchable<'db>(
clauses: Clauses::new_from_iter(
interner,
generic_predicates
- .iter_identity_copied()
+ .iter_identity()
.chain([unsize_predicate.upcast(interner), trait_predicate.upcast(interner)])
.chain(meta_sized_predicate),
),
@@ -503,8 +504,12 @@ fn contains_illegal_impl_trait_in_trait<'db>(
&mut self,
ty: <DbInterner<'db> as rustc_type_ir::Interner>::Ty,
) -> Self::Result {
- if let rustc_type_ir::TyKind::Alias(AliasTyKind::Opaque, op) = ty.kind() {
- let id = match op.def_id {
+ if let rustc_type_ir::TyKind::Alias(AliasTy {
+ kind: AliasTyKind::Opaque { def_id },
+ ..
+ }) = ty.kind()
+ {
+ let id = match def_id {
SolverDefId::InternedOpaqueTyId(id) => id,
_ => unreachable!(),
};
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index d14e9d6526..339ce7933a 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -14,6 +14,7 @@
//! the `ena` crate, which is extracted from rustc.
mod autoderef;
+mod callee;
pub(crate) mod cast;
pub(crate) mod closure;
mod coerce;
@@ -28,9 +29,9 @@ mod path;
mod place_op;
pub(crate) mod unify;
-use std::{cell::OnceCell, convert::identity, iter};
+use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref};
-use base_db::Crate;
+use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
AdtId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, FieldId,
@@ -54,15 +55,22 @@ use rustc_type_ir::{
AliasTyKind, TypeFoldable,
inherent::{AdtDef, IntoKind, Ty as _},
};
+use smallvec::SmallVec;
use span::Edition;
use stdx::never;
use thin_vec::ThinVec;
use crate::{
ImplTraitId, IncorrectGenericsLenKind, PathLoweringDiagnostic, TargetFeatures,
+ closure_analysis::PlaceBase,
collect_type_inference_vars,
- db::{HirDatabase, InternedClosureId, InternedOpaqueTyId},
+ db::{HirDatabase, InternedOpaqueTyId},
infer::{
+ callee::DeferredCallResolution,
+ closure::analysis::{
+ BorrowKind,
+ expr_use_visitor::{FakeReadCause, Place},
+ },
coerce::{CoerceMany, DynamicCoerceMany},
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
expr::ExprIsRead,
@@ -71,14 +79,12 @@ use crate::{
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
},
method_resolution::{CandidateId, MethodResolutionUnstableFeatures},
- mir::MirSpan,
next_solver::{
AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region,
StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys,
abi::Safety,
infer::{InferCtxt, ObligationInspector, traits::ObligationCause},
},
- traits::FnTrait,
utils::TargetFeatureIsSafeInTarget,
};
@@ -91,7 +97,6 @@ pub use coerce::could_coerce;
pub use unify::{could_unify, could_unify_deeply};
use cast::{CastCheck, CastError};
-pub(crate) use closure::analysis::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
/// The entry point of type inference.
fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult {
@@ -266,7 +271,10 @@ fn infer_finalize(mut ctx: InferenceContext<'_, '_>) -> InferenceResult {
ctx.table.select_obligations_where_possible();
- ctx.infer_closures();
+ // Closure and coroutine analysis may run after fallback
+ // because they don't constrain other type variables.
+ ctx.closure_analyze();
+ assert!(ctx.deferred_call_resolutions.is_empty());
ctx.table.select_obligations_where_possible();
@@ -498,7 +506,7 @@ pub enum Adjust {
/// The target type is `U` in both cases, with the region and mutability
/// being those shared by both the receiver and the returned reference.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct OverloadedDeref(pub Option<Mutability>);
+pub struct OverloadedDeref(pub Mutability);
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum AutoBorrowMutability {
@@ -535,15 +543,6 @@ pub enum AutoBorrow {
RawPtr(Mutability),
}
-impl AutoBorrow {
- fn mutability(self) -> Mutability {
- match self {
- AutoBorrow::Ref(mutbl) => mutbl.into(),
- AutoBorrow::RawPtr(mutbl) => mutbl,
- }
- }
-}
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum PointerCast {
/// Go from a fn-item type to a fn-pointer type.
@@ -637,11 +636,226 @@ 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>,
- pub(crate) closure_info: FxHashMap<InternedClosureId, (Vec<CapturedItem>, FnTrait)>,
- // FIXME: remove this field
- pub mutated_bindings_in_closure: FxHashSet<BindingId>,
-
pub(crate) coercion_casts: FxHashSet<ExprId>,
+
+ pub closures_data: FxHashMap<ExprId, ClosureData>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Default)]
+pub struct ClosureData {
+ /// Tracks the minimum captures required for a closure;
+ /// see `MinCaptureInformationMap` for more details.
+ pub min_captures: RootVariableMinCaptureList,
+
+ /// Tracks the fake reads required for a closure and the reason for the fake read.
+ /// When performing pattern matching for closures, there are times we don't end up
+ /// reading places that are mentioned in a closure (because of _ patterns). However,
+ /// to ensure the places are initialized, we introduce fake reads.
+ /// Consider these two examples:
+ /// ```ignore (discriminant matching with only wildcard arm)
+ /// let x: u8;
+ /// let c = || match x { _ => () };
+ /// ```
+ /// In this example, we don't need to actually read/borrow `x` in `c`, and so we don't
+ /// want to capture it. However, we do still want an error here, because `x` should have
+ /// to be initialized at the point where c is created. Therefore, we add a "fake read"
+ /// instead.
+ /// ```ignore (destructured assignments)
+ /// let c = || {
+ /// let (t1, t2) = t;
+ /// }
+ /// ```
+ /// In the second example, we capture the disjoint fields of `t` (`t.0` & `t.1`), but
+ /// we never capture `t`. This becomes an issue when we build MIR as we require
+ /// information on `t` in order to create place `t.0` and `t.1`. We can solve this
+ /// issue by fake reading `t`.
+ pub fake_reads: Box<[(Place, FakeReadCause, SmallVec<[CaptureSourceStack; 2]>)]>,
+}
+
+/// Part of `MinCaptureInformationMap`; Maps a root variable to the list of `CapturedPlace`.
+/// Used to track the minimum set of `Place`s that need to be captured to support all
+/// Places captured by the closure starting at a given root variable.
+///
+/// This provides a convenient and quick way of checking if a variable being used within
+/// a closure is a capture of a local variable.
+pub(crate) type RootVariableMinCaptureList = FxIndexMap<BindingId, MinCaptureList>;
+
+/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
+pub(crate) type MinCaptureList = Vec<CapturedPlace>;
+
+/// A composite describing a `Place` that is captured by a closure.
+#[derive(Eq, PartialEq, Clone, Debug, Hash)]
+pub struct CapturedPlace {
+ /// The `Place` that is captured.
+ pub place: Place,
+
+ /// `CaptureKind` and expression(s) that resulted in such capture of `place`.
+ pub info: CaptureInfo,
+
+ /// Represents if `place` can be mutated or not.
+ pub mutability: Mutability,
+}
+
+impl CapturedPlace {
+ pub fn is_by_ref(&self) -> bool {
+ match self.info.capture_kind {
+ UpvarCapture::ByValue | UpvarCapture::ByUse => false,
+ UpvarCapture::ByRef(..) => true,
+ }
+ }
+
+ pub fn captured_local(&self) -> BindingId {
+ match self.place.base {
+ PlaceBase::Upvar { var_id: local, .. } | PlaceBase::Local(local) => local,
+ PlaceBase::Rvalue | PlaceBase::StaticItem => {
+ unreachable!("only locals can be captured")
+ }
+ }
+ }
+
+ /// The type of the capture stored in the closure, which is different from the type of the captured place
+ /// if we capture by reference.
+ pub fn captured_ty<'db>(&self, db: &'db dyn HirDatabase) -> Ty<'db> {
+ let place_ty = self.place.ty();
+ let make_ref = |mutbl| {
+ let interner = DbInterner::new_no_crate(db);
+ let region = Region::new_erased(interner);
+ Ty::new_ref(interner, region, place_ty, mutbl)
+ };
+ match self.info.capture_kind {
+ UpvarCapture::ByUse | UpvarCapture::ByValue => place_ty,
+ UpvarCapture::ByRef(kind) => make_ref(kind.to_mutbl_lossy()),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct CaptureSourceStack(CaptureSourceStackRepr);
+
+#[derive(Clone)]
+enum CaptureSourceStackRepr {
+ One(ExprOrPatId),
+ Two([ExprOrPatId; 2]),
+ Many(ThinVec<ExprOrPatId>),
+}
+
+impl PartialEq for CaptureSourceStack {
+ fn eq(&self, other: &Self) -> bool {
+ **self == **other
+ }
+}
+
+impl Eq for CaptureSourceStack {}
+
+impl std::hash::Hash for CaptureSourceStack {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ (**self).hash(state);
+ }
+}
+
+const _: () = assert!(size_of::<CaptureSourceStack>() == 16);
+
+impl Deref for CaptureSourceStack {
+ type Target = [ExprOrPatId];
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ match &self.0 {
+ CaptureSourceStackRepr::One(it) => std::slice::from_ref(it),
+ CaptureSourceStackRepr::Two(it) => it,
+ CaptureSourceStackRepr::Many(it) => it,
+ }
+ }
+}
+
+impl fmt::Debug for CaptureSourceStack {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("CaptureSourceStack").field(&&**self).finish()
+ }
+}
+
+impl CaptureSourceStack {
+ #[inline]
+ pub fn len(&self) -> usize {
+ match &self.0 {
+ CaptureSourceStackRepr::One(_) => 1,
+ CaptureSourceStackRepr::Two(_) => 2,
+ CaptureSourceStackRepr::Many(it) => it.len(),
+ }
+ }
+
+ #[inline]
+ pub(crate) fn from_single(id: ExprOrPatId) -> Self {
+ Self(CaptureSourceStackRepr::One(id))
+ }
+
+ #[inline]
+ pub fn final_source(&self) -> ExprOrPatId {
+ *self.last().expect("should always have a final source")
+ }
+
+ pub fn push(&mut self, new_id: ExprOrPatId) {
+ match &mut self.0 {
+ CaptureSourceStackRepr::One(old_id) => {
+ self.0 = CaptureSourceStackRepr::Two([*old_id, new_id])
+ }
+ CaptureSourceStackRepr::Two([old_id1, old_id2]) => {
+ self.0 = CaptureSourceStackRepr::Many(ThinVec::from([*old_id1, *old_id2, new_id]));
+ }
+ CaptureSourceStackRepr::Many(old_ids) => old_ids.push(new_id),
+ }
+ }
+
+ pub fn truncate(&mut self, new_len: usize) {
+ debug_assert!(new_len > 0);
+ match &mut self.0 {
+ CaptureSourceStackRepr::One(_) => {}
+ CaptureSourceStackRepr::Two([first, _]) => {
+ if new_len == 1 {
+ self.0 = CaptureSourceStackRepr::One(*first)
+ }
+ }
+ CaptureSourceStackRepr::Many(ids) => ids.truncate(new_len),
+ }
+ }
+
+ pub fn shrink_to_fit(&mut self) {
+ match &mut self.0 {
+ CaptureSourceStackRepr::One(_) | CaptureSourceStackRepr::Two(_) => {}
+ CaptureSourceStackRepr::Many(ids) => match **ids {
+ [one] => self.0 = CaptureSourceStackRepr::One(one),
+ [first, second] => self.0 = CaptureSourceStackRepr::Two([first, second]),
+ _ => ids.shrink_to_fit(),
+ },
+ }
+ }
+}
+
+/// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move)
+/// for a particular capture as well as identifying the part of the source code
+/// that triggered this capture to occur.
+#[derive(Eq, PartialEq, Clone, Debug, Hash)]
+pub struct CaptureInfo {
+ pub sources: SmallVec<[CaptureSourceStack; 2]>,
+
+ /// Capture mode that was selected
+ pub capture_kind: UpvarCapture,
+}
+
+/// Information describing the capture of an upvar. This is computed
+/// during `typeck`, specifically by `regionck`.
+#[derive(Eq, PartialEq, Clone, Debug, Copy, Hash)]
+pub enum UpvarCapture {
+ /// Upvar is captured by value. This is always true when the
+ /// closure is labeled `move`, but can also be true in other cases
+ /// depending on inference.
+ ByValue,
+
+ /// Upvar is captured by use. This is true when the closure is labeled `use`.
+ ByUse,
+
+ /// Upvar is captured by reference.
+ ByRef(BorrowKind),
}
#[salsa::tracked]
@@ -699,9 +913,8 @@ impl InferenceResult {
pat_adjustments: Default::default(),
binding_modes: Default::default(),
expr_adjustments: Default::default(),
- closure_info: Default::default(),
- mutated_bindings_in_closure: Default::default(),
coercion_casts: Default::default(),
+ closures_data: Default::default(),
}
}
@@ -771,9 +984,6 @@ impl InferenceResult {
pub fn type_of_type_placeholder<'db>(&self, type_ref: TypeRefId) -> Option<Ty<'db>> {
self.type_of_type_placeholder.get(&type_ref).map(|ty| ty.as_ref())
}
- pub fn closure_info(&self, closure: InternedClosureId) -> &(Vec<CapturedItem>, FnTrait) {
- self.closure_info.get(&closure).unwrap()
- }
pub fn type_of_expr_or_pat<'db>(&self, id: ExprOrPatId) -> Option<Ty<'db>> {
match id {
ExprOrPatId::ExprId(id) => self.type_of_expr.get(id).map(|it| it.as_ref()),
@@ -870,6 +1080,26 @@ impl InferenceResult {
pub fn binding_ty<'db>(&self, id: BindingId) -> Ty<'db> {
self.type_of_binding.get(id).map_or(self.error_ty.as_ref(), |it| it.as_ref())
}
+
+ /// This does not deduplicate, which means you'll get the types once per capture.
+ pub fn closure_captures_tys<'db>(&self, closure: ExprId) -> impl Iterator<Item = Ty<'db>> {
+ self.closures_data[&closure]
+ .min_captures
+ .values()
+ .flat_map(|captures| captures.iter().map(|capture| capture.place.ty()))
+ }
+
+ /// Like [`Self::closure_captures_tys()`], but using [`CapturedPlace::captured_ty()`].
+ pub fn closure_captures_captured_tys<'db>(
+ &self,
+ db: &'db dyn HirDatabase,
+ closure: ExprId,
+ ) -> impl Iterator<Item = Ty<'db>> {
+ self.closures_data[&closure]
+ .min_captures
+ .values()
+ .flat_map(|captures| captures.iter().map(|capture| capture.captured_ty(db)))
+ }
}
/// The inference context contains all information needed during type inference.
@@ -913,19 +1143,8 @@ pub(crate) struct InferenceContext<'body, 'db> {
deferred_cast_checks: Vec<CastCheck<'db>>,
- // fields related to closure capture
- current_captures: Vec<CapturedItemWithoutTy>,
- /// A stack that has an entry for each projection in the current capture.
- ///
- /// For example, in `a.b.c`, we capture the spans of `a`, `a.b`, and `a.b.c`.
- /// We do that because sometimes we truncate projections (when a closure captures
- /// both `a.b` and `a.b.c`), and we want to provide accurate spans in this case.
- current_capture_span_stack: Vec<MirSpan>,
- current_closure: Option<InternedClosureId>,
- /// Stores the list of closure ids that need to be analyzed before this closure. See the
- /// comment on `InferenceContext::sort_closures`
- closure_dependencies: FxHashMap<InternedClosureId, Vec<InternedClosureId>>,
- deferred_closures: FxHashMap<InternedClosureId, Vec<(Ty<'db>, Ty<'db>, Vec<Ty<'db>>, ExprId)>>,
+ /// The key is an expression defining a closure or a coroutine closure.
+ deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>,
diagnostics: Diagnostics,
}
@@ -1017,13 +1236,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
diverges: Diverges::Maybe,
breakables: Vec::new(),
deferred_cast_checks: Vec::new(),
- current_captures: Vec::new(),
- current_capture_span_stack: Vec::new(),
- current_closure: None,
- deferred_closures: FxHashMap::default(),
- closure_dependencies: FxHashMap::default(),
inside_assignment: false,
diagnostics: Diagnostics::default(),
+ deferred_call_resolutions: FxHashMap::default(),
}
}
@@ -1082,7 +1297,12 @@ 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, mut result, tuple_field_accesses_rev, diagnostics, ..
+ mut table,
+ mut result,
+ tuple_field_accesses_rev,
+ diagnostics,
+ types,
+ ..
} = self;
let mut diagnostics = diagnostics.finish();
// Destructure every single field so whenever new fields are added to `InferenceResult` we
@@ -1098,16 +1318,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
type_of_type_placeholder,
type_of_opaque,
type_mismatches,
+ closures_data,
has_errors,
error_ty: _,
pat_adjustments,
binding_modes: _,
expr_adjustments,
- // Types in `closure_info` have already been `resolve_completely()`'d during
- // `InferenceContext::infer_closures()` (in `HirPlace::ty()` specifically), so no need
- // to resolve them here.
- closure_info: _,
- mutated_bindings_in_closure: _,
tuple_field_access_types: _,
coercion_casts: _,
diagnostics: _,
@@ -1194,6 +1410,38 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
*has_errors = *has_errors || adjustment.as_ref().references_non_lt_error();
}
pat_adjustments.shrink_to_fit();
+ for closure_data in closures_data.values_mut() {
+ let ClosureData { min_captures, fake_reads } = closure_data;
+ let dummy_place = || Place {
+ base_ty: types.types.error.store(),
+ base: closure::analysis::expr_use_visitor::PlaceBase::Rvalue,
+ projections: Vec::new(),
+ };
+
+ for (place, _, sources) in fake_reads {
+ *place = table.resolve_completely(std::mem::replace(place, dummy_place()));
+ place.projections.shrink_to_fit();
+ for source in &mut *sources {
+ source.shrink_to_fit();
+ }
+ sources.shrink_to_fit();
+ }
+
+ 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()));
+ let CaptureInfo { sources, capture_kind: _ } = info;
+ for source in &mut *sources {
+ source.shrink_to_fit();
+ }
+ sources.shrink_to_fit();
+ }
+ min_capture.shrink_to_fit();
+ }
+ min_captures.shrink_to_fit();
+ }
+ closures_data.shrink_to_fit();
result.tuple_field_access_types = tuple_field_accesses_rev
.into_iter()
.map(|subst| table.resolve_completely(subst).store())
@@ -1387,6 +1635,21 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.diagnostics.push(diagnostic);
}
+ fn record_deferred_call_resolution(
+ &mut self,
+ closure_def_id: ExprId,
+ r: DeferredCallResolution<'db>,
+ ) {
+ self.deferred_call_resolutions.entry(closure_def_id).or_default().push(r);
+ }
+
+ fn remove_deferred_call_resolutions(
+ &mut self,
+ closure_def_id: ExprId,
+ ) -> Vec<DeferredCallResolution<'db>> {
+ self.deferred_call_resolutions.remove(&closure_def_id).unwrap_or_default()
+ }
+
fn with_ty_lowering<R>(
&mut self,
store: &ExpressionStore,
@@ -1440,9 +1703,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
});
if placeholder_ids.len() == type_variables.len() {
- for (placeholder_id, type_variable) in
- placeholder_ids.into_iter().zip(type_variables.into_iter())
- {
+ for (placeholder_id, type_variable) in placeholder_ids.into_iter().zip(type_variables) {
self.write_type_placeholder_ty(placeholder_id, type_variable);
}
}
@@ -1648,6 +1909,23 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
result.unwrap_or(self.types.types.error)
}
+ pub(crate) fn type_must_be_known_at_this_point(
+ &self,
+ _id: ExprOrPatId,
+ _ty: Ty<'db>,
+ ) -> Ty<'db> {
+ // FIXME: Emit an diagnostic.
+ self.types.types.error
+ }
+
+ pub(crate) fn require_type_is_sized(&mut self, ty: Ty<'db>) {
+ if !ty.references_non_lt_error()
+ && let Some(sized_trait) = self.lang_items.Sized
+ {
+ self.table.register_bound(ty, sized_trait, ObligationCause::new());
+ }
+ }
+
fn expr_ty(&self, expr: ExprId) -> Ty<'db> {
self.result.expr_ty(expr)
}
@@ -1674,10 +1952,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
Some(res_assoc_ty) => {
let alias = Ty::new_alias(
self.interner(),
- AliasTyKind::Projection,
AliasTy::new(
self.interner(),
- res_assoc_ty.into(),
+ AliasTyKind::Projection { def_id: res_assoc_ty.into() },
iter::once(inner_ty.into()).chain(params.iter().copied()),
),
);
@@ -1706,6 +1983,64 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.generic_def,
LifetimeElisionKind::Infer,
);
+
+ if let Some(type_anchor) = path.type_anchor() {
+ let mut segments = path.segments();
+ if segments.is_empty() {
+ return (self.err_ty(), None);
+ }
+ let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor);
+ ty = self.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 Some(assoc_type) = trait_ref
+ .def_id
+ .0
+ .trait_items(self.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 alias = Ty::new_alias(
+ self.interner(),
+ AliasTy::new_from_args(
+ self.interner(),
+ AliasTyKind::Projection { def_id: assoc_type.into() },
+ args,
+ ),
+ );
+ ty = self.table.try_structurally_resolve_type(alias);
+ segments = segments.skip(1);
+ }
+
+ let variant = match ty.as_adt() {
+ Some((AdtId::StructId(id), _)) => id.into(),
+ Some((AdtId::UnionId(id), _)) => id.into(),
+ Some((AdtId::EnumId(id), _)) => {
+ if let Some(segment) = segments.first()
+ && let enum_data = id.enum_variants(self.db)
+ && let Some(variant) = enum_data.variant(segment.name)
+ {
+ // FIXME: Report error if there are generics on the variant.
+ segments = segments.skip(1);
+ variant.into()
+ } else {
+ return (self.err_ty(), None);
+ }
+ }
+ None => return (self.err_ty(), None),
+ };
+
+ if !segments.is_empty() {
+ // FIXME: Report an error.
+ return (self.err_ty(), None);
+ } else {
+ return (ty, Some(variant));
+ }
+ }
+
let mut path_ctx = ctx.at_path(path, node);
let interner = DbInterner::conjure();
let (resolution, unresolved) = if value_ns {
@@ -1838,6 +2173,46 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
});
(ty, variant)
}
+ TypeNs::TraitId(_) => {
+ let Some(remaining_idx) = unresolved else {
+ return (self.err_ty(), None);
+ };
+
+ let remaining_segments = path.segments().skip(remaining_idx);
+
+ if remaining_segments.len() >= 2 {
+ path_ctx.ignore_last_segment();
+ }
+
+ let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
+ ty = self.table.process_user_written_ty(ty);
+
+ if let Some(segment) = remaining_segments.get(1)
+ && let Some((AdtId::EnumId(id), _)) = ty.as_adt()
+ {
+ let enum_data = id.enum_variants(self.db);
+ if let Some(variant) = enum_data.variant(segment.name) {
+ return if remaining_segments.len() == 2 {
+ (ty, Some(variant.into()))
+ } else {
+ // We still have unresolved paths, but enum variants never have
+ // associated types!
+ // FIXME: Report an error.
+ (self.err_ty(), None)
+ };
+ }
+ }
+
+ let variant = ty.as_adt().and_then(|(id, _)| match id {
+ AdtId::StructId(s) => Some(VariantId::StructId(s)),
+ AdtId::UnionId(u) => Some(VariantId::UnionId(u)),
+ AdtId::EnumId(_) => {
+ // FIXME Error E0071, expected struct, variant or union type, found enum `Foo`
+ None
+ }
+ });
+ (ty, variant)
+ }
TypeNs::TypeAliasId(it) => {
let Some(mod_path) = path.mod_path() else {
never!("resolver should always resolve lang item paths");
@@ -1859,10 +2234,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// FIXME potentially resolve assoc type
(self.err_ty(), None)
}
- TypeNs::AdtId(AdtId::EnumId(_))
- | TypeNs::BuiltinType(_)
- | TypeNs::TraitId(_)
- | TypeNs::ModuleId(_) => {
+ TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::ModuleId(_) => {
// FIXME diagnostic
(self.err_ty(), None)
}
diff --git a/crates/hir-ty/src/infer/autoderef.rs b/crates/hir-ty/src/infer/autoderef.rs
index d748c89e67..a6c7b2dbb9 100644
--- a/crates/hir-ty/src/infer/autoderef.rs
+++ b/crates/hir-ty/src/infer/autoderef.rs
@@ -36,7 +36,7 @@ impl<'db, Ctx: AutoderefCtx<'db>> GeneralAutoderef<'db, Ctx> {
.iter()
.map(|&(_source, kind)| {
if let AutoderefKind::Overloaded = kind {
- Some(OverloadedDeref(Some(Mutability::Not)))
+ Some(OverloadedDeref(Mutability::Not))
} else {
None
}
diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs
new file mode 100644
index 0000000000..3d478912a3
--- /dev/null
+++ b/crates/hir-ty/src/infer/callee.rs
@@ -0,0 +1,543 @@
+//! Inference of calls.
+
+use std::iter;
+
+use intern::sym;
+use tracing::debug;
+
+use hir_def::{CallableDefId, hir::ExprId, signatures::FunctionSignature};
+use rustc_type_ir::{
+ InferTy, Interner,
+ inherent::{GenericArgs as _, IntoKind, Ty as _},
+};
+
+use crate::{
+ Adjust, Adjustment, AutoBorrow, FnAbi,
+ autoderef::{GeneralAutoderef, InferenceContextAutoderef},
+ infer::{
+ AllowTwoPhase, AutoBorrowMutability, Expectation, InferenceContext, InferenceDiagnostic,
+ expr::{ExprIsRead, TupleArgumentsFlag},
+ },
+ method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
+ next_solver::{
+ FnSig, Ty, TyKind,
+ infer::{BoundRegionConversionTime, traits::ObligationCause},
+ },
+};
+
+#[derive(Debug)]
+enum CallStep<'db> {
+ Builtin(Ty<'db>),
+ DeferredClosure(ExprId, FnSig<'db>),
+ /// Call overloading when callee implements one of the Fn* traits.
+ Overloaded(MethodCallee<'db>),
+}
+
+impl<'db> InferenceContext<'_, 'db> {
+ pub(crate) fn infer_call(
+ &mut self,
+ call_expr: ExprId,
+ callee_expr: ExprId,
+ arg_exprs: &[ExprId],
+ expected: &Expectation<'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 mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty);
+ let mut result = None;
+ while result.is_none() && autoderef.next().is_some() {
+ result =
+ Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef);
+ }
+
+ // FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate.
+
+ let obligations = autoderef.take_obligations();
+ self.table.register_predicates(obligations);
+
+ let output = match result {
+ None => {
+ // Check all of the arg expressions, but with no expectations
+ // since we don't have a signature to compare them to.
+ for &arg in arg_exprs {
+ self.infer_expr_no_expect(arg, ExprIsRead::Yes);
+ }
+
+ 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)
+ }
+
+ Some(CallStep::DeferredClosure(_def_id, fn_sig)) => {
+ self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig)
+ }
+
+ Some(CallStep::Overloaded(method_callee)) => {
+ self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee)
+ }
+ };
+
+ // we must check that return type of called functions is WF:
+ self.table.register_wf_obligation(output.into(), ObligationCause::new());
+
+ output
+ }
+
+ fn try_overloaded_call_step(
+ call_expr: ExprId,
+ callee_expr: ExprId,
+ arg_exprs: &[ExprId],
+ autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>,
+ ) -> Option<CallStep<'db>> {
+ let final_ty = autoderef.final_ty();
+ let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty);
+
+ // If the callee is a function pointer or a closure, then we're all set.
+ match adjusted_ty.kind() {
+ TyKind::FnDef(..) | TyKind::FnPtr(..) => {
+ let adjust_steps = autoderef.adjust_steps_as_infer_ok();
+ let adjustments =
+ autoderef.ctx().table.register_infer_ok(adjust_steps).into_boxed_slice();
+ autoderef.ctx().write_expr_adj(callee_expr, adjustments);
+ return Some(CallStep::Builtin(adjusted_ty));
+ }
+
+ // Check whether this is a call to a closure where we
+ // haven't yet decided on whether the closure is fn vs
+ // fnmut vs fnonce. If so, we have to defer further processing.
+ TyKind::Closure(def_id, args)
+ if autoderef.ctx().infcx().closure_kind(adjusted_ty).is_none() =>
+ {
+ let closure_sig = args.as_closure().sig();
+ let closure_sig = autoderef.ctx().infcx().instantiate_binder_with_fresh_vars(
+ 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;
+ autoderef.ctx().record_deferred_call_resolution(
+ def_id,
+ DeferredCallResolution {
+ call_expr,
+ callee_expr,
+ closure_ty: adjusted_ty,
+ adjustments,
+ fn_sig: closure_sig,
+ },
+ );
+ return Some(CallStep::DeferredClosure(def_id, closure_sig));
+ }
+
+ // When calling a `CoroutineClosure` that is local to the body, we will
+ // not know what its `closure_kind` is yet. Instead, just fill in the
+ // signature with an infer var for the `tupled_upvars_ty` of the coroutine,
+ // and record a deferred call resolution which will constrain that var
+ // as part of `AsyncFn*` trait confirmation.
+ TyKind::CoroutineClosure(def_id, args)
+ if autoderef.ctx().infcx().closure_kind(adjusted_ty).is_none() =>
+ {
+ let closure_args = args.as_coroutine_closure();
+ let coroutine_closure_sig =
+ autoderef.ctx().infcx().instantiate_binder_with_fresh_vars(
+ BoundRegionConversionTime::FnCall,
+ closure_args.coroutine_closure_sig(),
+ );
+ let tupled_upvars_ty = autoderef.ctx().table.next_ty_var();
+ // 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 interner = autoderef.ctx().interner();
+ let call_sig = interner.mk_fn_sig(
+ [coroutine_closure_sig.tupled_inputs_ty],
+ coroutine_closure_sig.to_coroutine(
+ interner,
+ closure_args.parent_args(),
+ kind_ty,
+ interner.coroutine_for_closure(def_id),
+ tupled_upvars_ty,
+ ),
+ coroutine_closure_sig.c_variadic,
+ coroutine_closure_sig.safety,
+ coroutine_closure_sig.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;
+ autoderef.ctx().record_deferred_call_resolution(
+ def_id,
+ DeferredCallResolution {
+ call_expr,
+ callee_expr,
+ closure_ty: adjusted_ty,
+ adjustments,
+ fn_sig: call_sig,
+ },
+ );
+ return Some(CallStep::DeferredClosure(def_id, call_sig));
+ }
+
+ // Hack: we know that there are traits implementing Fn for &F
+ // where F:Fn and so forth. In the particular case of types
+ // like `f: &mut FnMut()`, if there is a call `f()`, we would
+ // normally translate to `FnMut::call_mut(&mut f, ())`, but
+ // that winds up potentially requiring the user to mark their
+ // variable as `mut` which feels unnecessary and unexpected.
+ //
+ // fn foo(f: &mut impl FnMut()) { f() }
+ // ^ without this hack `f` would have to be declared as mutable
+ //
+ // The simplest fix by far is to just ignore this case and deref again,
+ // so we wind up with `FnMut::call_mut(&mut *f, ())`.
+ TyKind::Ref(..) if autoderef.step_count() == 0 => {
+ return None;
+ }
+
+ TyKind::Infer(InferTy::TyVar(vid))
+ // If we end up with an inference variable which is not the hidden type of
+ // an opaque, emit an error.
+ if !autoderef.ctx().infcx().has_opaques_with_sub_unified_hidden_type(vid) => {
+ autoderef
+ .ctx()
+ .type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty);
+ return None;
+ }
+
+ TyKind::Error(_) => {
+ return None;
+ }
+
+ _ => {}
+ }
+
+ // Now, we look for the implementation of a Fn trait on the object's type.
+ // We first do it with the explicit instruction to look for an impl of
+ // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding
+ // to the number of call parameters.
+ // If that fails (or_else branch), we try again without specifying the
+ // shape of the tuple (hence the None). This allows to detect an Fn trait
+ // 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))
+ .map(|(autoref, method)| {
+ let adjustments = autoderef.adjust_steps_as_infer_ok();
+ let mut adjustments = autoderef.ctx().table.register_infer_ok(adjustments);
+ adjustments.extend(autoref);
+ autoderef.ctx().write_expr_adj(callee_expr, adjustments.into_boxed_slice());
+ CallStep::Overloaded(method)
+ })
+ }
+
+ fn try_overloaded_call_traits(
+ &mut self,
+ adjusted_ty: Ty<'db>,
+ opt_arg_exprs: Option<&[ExprId]>,
+ ) -> Option<(Option<Adjustment>, MethodCallee<'db>)> {
+ // HACK(async_closures): For async closures, prefer `AsyncFn*`
+ // over `Fn*`, since all async closures implement `FnOnce`, but
+ // choosing that over `AsyncFn`/`AsyncFnMut` would be more restrictive.
+ // For other callables, just prefer `Fn*` for perf reasons.
+ //
+ // The order of trait choices here is not that big of a deal,
+ // since it just guides inference (and our choice of autoref).
+ // Though in the future, I'd like typeck to choose:
+ // `Fn > AsyncFn > FnMut > AsyncFnMut > FnOnce > AsyncFnOnce`
+ // ...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),
+ ]
+ } 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),
+ ]
+ };
+
+ // 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 {
+ 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()),
+ )
+ });
+
+ // We use `TreatNotYetDefinedOpaques::AsRigid` here so that if the `adjusted_ty`
+ // is `Box<impl FnOnce()>` we choose `FnOnce` instead of `Fn`.
+ //
+ // We try all the different call traits in order and choose the first
+ // 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,
+ trait_def_id,
+ adjusted_ty,
+ opt_input_type,
+ TreatNotYetDefinedOpaques::AsRigid,
+ ) {
+ let method = self.table.register_infer_ok(ok);
+ let mut autoref = None;
+ if borrow {
+ // Check for &self vs &mut self in the method signature. Since this is either
+ // the Fn or FnMut trait, it should be one of those.
+ let TyKind::Ref(_, _, mutbl) = method.sig.inputs_and_output.inputs()[0].kind()
+ else {
+ panic!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
+ };
+
+ // For initial two-phase borrow
+ // deployment, conservatively omit
+ // overloaded function call ops.
+ let mutbl = AutoBorrowMutability::new(mutbl, AllowTwoPhase::No);
+
+ autoref = Some(Adjustment {
+ kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)),
+ target: method.sig.inputs_and_output.inputs()[0].store(),
+ });
+ }
+
+ return Some((autoref, method));
+ }
+ }
+
+ None
+ }
+
+ /// Returns the argument indices to skip.
+ fn check_legacy_const_generics(
+ &mut self,
+ callee: Option<CallableDefId>,
+ args: &[ExprId],
+ ) -> Box<[u32]> {
+ let func = match callee {
+ Some(CallableDefId::FunctionId(func)) => func,
+ _ => return Default::default(),
+ };
+
+ let data = FunctionSignature::of(self.db, func);
+ let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
+ else {
+ return Default::default();
+ };
+ let mut legacy_const_generics_indices = Box::<[u32]>::from(legacy_const_generics_indices);
+
+ // only use legacy const generics if the param count matches with them
+ if data.params.len() + legacy_const_generics_indices.len() != args.len() {
+ if args.len() <= data.params.len() {
+ return Default::default();
+ } else {
+ // there are more parameters than there should be without legacy
+ // const params; use them
+ legacy_const_generics_indices.sort_unstable();
+ return legacy_const_generics_indices;
+ }
+ }
+
+ // check legacy const parameters
+ for arg_idx in legacy_const_generics_indices.iter().copied() {
+ if arg_idx >= args.len() as u32 {
+ continue;
+ }
+ let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
+ self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
+ // FIXME: evaluate and unify with the const
+ }
+ legacy_const_generics_indices.sort_unstable();
+ legacy_const_generics_indices
+ }
+
+ fn confirm_builtin_call(
+ &mut self,
+ call_expr: ExprId,
+ callee_ty: Ty<'db>,
+ arg_exprs: &[ExprId],
+ expected: &Expectation<'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);
+ (fn_sig, Some(def_id.0))
+ }
+
+ // FIXME(const_trait_impl): these arms should error because we can't enforce them
+ TyKind::FnPtr(sig_tys, hdr) => (sig_tys.with(hdr), None),
+
+ _ => unreachable!(),
+ };
+
+ // Replace any late-bound regions that appear in the function
+ // signature with region variables. We also have to
+ // 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 indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs);
+ self.check_call_arguments(
+ call_expr,
+ fn_sig.inputs(),
+ fn_sig.output(),
+ expected,
+ arg_exprs,
+ &indices_to_skip,
+ fn_sig.c_variadic,
+ TupleArgumentsFlag::DontTupleArguments,
+ );
+
+ if fn_sig.abi == FnAbi::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);
+ }
+
+ fn_sig.output()
+ }
+
+ fn confirm_deferred_closure_call(
+ &mut self,
+ call_expr: ExprId,
+ arg_exprs: &[ExprId],
+ expected: &Expectation<'db>,
+ fn_sig: FnSig<'db>,
+ ) -> Ty<'db> {
+ // `fn_sig` is the *signature* of the closure being called. We
+ // don't know the full details yet (`Fn` vs `FnMut` etc), but we
+ // do know the types expected for each argument and the return
+ // type.
+ self.check_call_arguments(
+ call_expr,
+ fn_sig.inputs(),
+ fn_sig.output(),
+ expected,
+ arg_exprs,
+ &[],
+ fn_sig.c_variadic,
+ TupleArgumentsFlag::TupleArguments,
+ );
+
+ fn_sig.output()
+ }
+
+ fn confirm_overloaded_call(
+ &mut self,
+ call_expr: ExprId,
+ arg_exprs: &[ExprId],
+ expected: &Expectation<'db>,
+ method: MethodCallee<'db>,
+ ) -> Ty<'db> {
+ self.check_call_arguments(
+ call_expr,
+ &method.sig.inputs()[1..],
+ method.sig.output(),
+ expected,
+ arg_exprs,
+ &[],
+ method.sig.c_variadic,
+ TupleArgumentsFlag::TupleArguments,
+ );
+
+ self.write_method_resolution(call_expr, method.def_id, method.args);
+
+ method.sig.output()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub(crate) struct DeferredCallResolution<'db> {
+ call_expr: ExprId,
+ callee_expr: ExprId,
+ closure_ty: Ty<'db>,
+ adjustments: Vec<Adjustment>,
+ fn_sig: FnSig<'db>,
+}
+
+impl<'a, 'db> DeferredCallResolution<'db> {
+ pub(crate) fn resolve(self, ctx: &mut InferenceContext<'a, 'db>) {
+ debug!("DeferredCallResolution::resolve() {:?}", self);
+
+ // we should not be invoked until the closure kind has been
+ // determined by upvar inference
+ 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) {
+ Some((autoref, method_callee)) => {
+ // One problem is that when we get here, we are going
+ // to have a newly instantiated function signature
+ // from the call trait. This has to be reconciled with
+ // the older function signature we had before. In
+ // principle we *should* be able to fn_sigs(), but we
+ // can't because of the annoying need for a TypeTrace.
+ // (This always bites me, should find a way to
+ // refactor it.)
+ let method_sig = method_callee.sig;
+
+ debug!("attempt_resolution: method_callee={:?}", method_callee);
+
+ for (method_arg_ty, self_arg_ty) in
+ iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs())
+ {
+ _ = ctx.demand_eqtype(self.call_expr.into(), *self_arg_ty, *method_arg_ty);
+ }
+
+ _ = ctx.demand_eqtype(
+ self.call_expr.into(),
+ method_sig.output(),
+ self.fn_sig.output(),
+ );
+
+ let mut adjustments = self.adjustments;
+ adjustments.extend(autoref);
+ ctx.write_expr_adj(self.callee_expr, adjustments.into_boxed_slice());
+
+ ctx.write_method_resolution(
+ self.call_expr,
+ method_callee.def_id,
+ method_callee.args,
+ );
+ }
+ None => {
+ assert!(
+ ctx.lang_items.FnOnce.is_none(),
+ "Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{:?}`",
+ self.closure_ty
+ )
+ }
+ }
+ }
+}
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index ce99016470..2207bc37e8 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -6,7 +6,7 @@ use std::{iter, mem, ops::ControlFlow};
use hir_def::{
TraitId,
- hir::{ClosureKind, ExprId, PatId},
+ hir::{ClosureKind, CoroutineSource, ExprId, PatId},
type_ref::TypeRefId,
};
use rustc_type_ir::{
@@ -19,7 +19,7 @@ use tracing::debug;
use crate::{
FnAbi,
- db::{InternedClosure, InternedCoroutine},
+ db::{InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId},
infer::{BreakableKind, Diverges, coerce::CoerceMany},
next_solver::{
AliasTy, Binder, ClauseKind, DbInterner, ErrorGuaranteed, FnSig, GenericArgs, PolyFnSig,
@@ -30,7 +30,6 @@ use crate::{
traits::{ObligationCause, PredicateObligations},
},
},
- traits::FnTrait,
};
use super::{Expectation, InferenceContext};
@@ -54,68 +53,40 @@ impl<'db> InferenceContext<'_, 'db> {
ret_type: Option<TypeRefId>,
arg_types: &[Option<TypeRefId>],
closure_kind: ClosureKind,
- tgt_expr: ExprId,
+ closure_expr: ExprId,
expected: &Expectation<'db>,
) -> Ty<'db> {
assert_eq!(args.len(), arg_types.len());
let interner = self.interner();
+ // 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) {
- Some(expected_ty) => self.deduce_closure_signature(expected_ty, closure_kind),
+ Some(ty) => {
+ let ty = self.table.try_structurally_resolve_type(ty);
+ self.deduce_closure_signature(ty, closure_kind)
+ }
None => (None, None),
};
- let ClosureSignatures { bound_sig, liberated_sig } =
+ let ClosureSignatures { bound_sig, mut liberated_sig } =
self.sig_of_closure(arg_types, ret_type, expected_sig);
- let body_ret_ty = bound_sig.output().skip_binder();
- let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
- // FIXME: Make this an infer var and infer it later.
- let tupled_upvars_ty = self.types.types.unit;
- let (id, ty, resume_yield_tys) = match closure_kind {
- ClosureKind::Coroutine(_) => {
- let yield_ty = self.table.next_ty_var();
- let resume_ty =
- liberated_sig.inputs().first().copied().unwrap_or(self.types.types.unit);
+ debug!(?bound_sig, ?liberated_sig);
- // FIXME: Infer the upvars later.
- let parts = CoroutineArgsParts {
- parent_args: parent_args.as_slice(),
- kind_ty: self.types.types.unit,
- resume_ty,
- yield_ty,
- return_ty: body_ret_ty,
- tupled_upvars_ty,
- };
+ let parent_args = GenericArgs::identity_for_item(interner, self.generic_def.into());
- let coroutine_id =
- self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
- let coroutine_ty = Ty::new_coroutine(
- interner,
- coroutine_id,
- CoroutineArgs::new(interner, parts).args,
- );
+ let tupled_upvars_ty = self.table.next_ty_var();
- (None, coroutine_ty, Some((resume_ty, yield_ty)))
- }
+ // 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 { .. } }`,
+ // similar to how `ty::GenSig` is a distinct data structure.
+ let (closure_ty, resume_yield_tys) = match closure_kind {
ClosureKind::Closure => {
- let closure_id = self.db.intern_closure(InternedClosure(self.owner, tgt_expr));
- match expected_kind {
- Some(kind) => {
- self.result.closure_info.insert(
- closure_id,
- (
- Vec::new(),
- match kind {
- rustc_type_ir::ClosureKind::Fn => FnTrait::Fn,
- rustc_type_ir::ClosureKind::FnMut => FnTrait::FnMut,
- rustc_type_ir::ClosureKind::FnOnce => FnTrait::FnOnce,
- },
- ),
- );
- }
- None => {}
- };
+ // Tuple up the arguments and insert the resulting function type into
+ // the `closures` table.
let sig = bound_sig.map_bound(|sig| {
interner.mk_fn_sig(
[Ty::new_tup(interner, sig.inputs())],
@@ -125,55 +96,91 @@ impl<'db> InferenceContext<'_, 'db> {
sig.abi,
)
});
- let sig_ty = Ty::new_fn_ptr(interner, sig);
- // FIXME: Infer the kind later if needed.
- let parts = ClosureArgsParts {
- parent_args: parent_args.as_slice(),
- closure_kind_ty: Ty::from_closure_kind(
- interner,
- expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
- ),
- closure_sig_as_fn_ptr_ty: sig_ty,
- tupled_upvars_ty,
+
+ debug!(?sig, ?expected_kind);
+
+ 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(),
};
- let closure_ty = Ty::new_closure(
+
+ let closure_args = ClosureArgs::new(
interner,
- closure_id.into(),
- ClosureArgs::new(interner, parts).args,
+ ClosureArgsParts {
+ parent_args: parent_args.as_slice(),
+ closure_kind_ty,
+ closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(interner, sig),
+ tupled_upvars_ty,
+ },
);
- self.deferred_closures.entry(closure_id).or_default();
- self.add_current_closure_dependency(closure_id);
- (Some(closure_id), closure_ty, None)
+
+ let closure_id =
+ InternedClosureId::new(self.db, InternedClosure(self.owner, closure_expr));
+
+ (Ty::new_closure(interner, closure_id.into(), closure_args.args), None)
}
- ClosureKind::Async => {
- // async closures always return the type ascribed after the `->` (if present),
- // and yield `()`.
- let bound_return_ty = bound_sig.skip_binder().output();
- let bound_yield_ty = self.types.types.unit;
- // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
- let resume_ty = self.types.types.unit;
+ ClosureKind::Coroutine(_) | ClosureKind::AsyncBlock { .. } => {
+ let yield_ty = match closure_kind {
+ ClosureKind::Coroutine(_) => self.table.next_ty_var(),
+ ClosureKind::AsyncBlock { .. } => self.types.types.unit,
+ _ => unreachable!(),
+ };
- // FIXME: Infer the kind later if needed.
- let closure_kind_ty = Ty::from_closure_kind(
- interner,
- expected_kind.unwrap_or(rustc_type_ir::ClosureKind::Fn),
- );
+ // Resume type defaults to `()` if the coroutine has no argument.
+ let resume_ty =
+ liberated_sig.inputs().first().copied().unwrap_or(self.types.types.unit);
- // FIXME: Infer captures later.
- // `for<'env> fn() -> ()`, for no captures.
- let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
+ // Coroutines that come from coroutine closures have not yet determined
+ // their kind ty, so make a fresh infer var which will be constrained
+ // 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()
+ }
+ _ => self.types.types.unit,
+ };
+
+ let coroutine_args = CoroutineArgs::new(
interner,
- Binder::bind_with_vars(
- interner.mk_fn_sig(
- [],
- self.types.types.unit,
- false,
- Safety::Safe,
- FnAbi::Rust,
- ),
- self.types.coroutine_captures_by_ref_bound_var_kinds,
- ),
+ CoroutineArgsParts {
+ parent_args: parent_args.as_slice(),
+ kind_ty,
+ resume_ty,
+ yield_ty,
+ return_ty: liberated_sig.output(),
+ tupled_upvars_ty,
+ },
);
+
+ let coroutine_id =
+ InternedCoroutineId::new(self.db, InternedClosure(self.owner, closure_expr));
+
+ (
+ 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);
+ // Compute all of the variables that will be used to populate the coroutine.
+ let resume_ty = self.table.next_ty_var();
+
+ 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(),
+ };
+
+ let coroutine_captures_by_ref_ty = self.table.next_ty_var();
+
let closure_args = CoroutineClosureArgs::new(
interner,
CoroutineClosureArgsParts {
@@ -183,7 +190,13 @@ impl<'db> InferenceContext<'_, 'db> {
interner,
bound_sig.map_bound(|sig| {
interner.mk_fn_sig(
- [resume_ty, Ty::new_tup(interner, sig.inputs())],
+ [
+ resume_ty,
+ Ty::new_tup_from_iter(
+ interner,
+ sig.inputs().iter().copied(),
+ ),
+ ],
Ty::new_tup(interner, &[bound_yield_ty, bound_return_ty]),
sig.c_variadic,
sig.safety,
@@ -196,9 +209,55 @@ impl<'db> InferenceContext<'_, 'db> {
},
);
- let coroutine_id =
- self.db.intern_coroutine(InternedCoroutine(self.owner, tgt_expr)).into();
- (None, Ty::new_coroutine_closure(interner, coroutine_id, closure_args.args), None)
+ let coroutine_kind_ty = match expected_kind {
+ Some(kind) => Ty::from_coroutine_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(),
+ };
+
+ let coroutine_upvars_ty = self.table.next_ty_var();
+
+ let coroutine_closure_id = InternedCoroutineClosureId::new(
+ self.db,
+ InternedClosure(self.owner, closure_expr),
+ );
+
+ // We need to turn the liberated signature that we got from HIR, which
+ // looks something like `|Args...| -> T`, into a signature that is suitable
+ // for type checking the inner body of the closure, which always returns a
+ // coroutine. To do so, we use the `CoroutineClosureSignature` to compute
+ // the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer
+ // vars which will get constrained during upvar analysis.
+ let coroutine_output_ty = closure_args
+ .coroutine_closure_sig()
+ .map_bound(|sig| {
+ sig.to_coroutine(
+ interner,
+ parent_args.as_slice(),
+ coroutine_kind_ty,
+ interner.coroutine_for_closure(coroutine_closure_id.into()),
+ coroutine_upvars_ty,
+ )
+ })
+ .skip_binder();
+ liberated_sig = interner.mk_fn_sig(
+ liberated_sig.inputs().iter().copied(),
+ coroutine_output_ty,
+ liberated_sig.c_variadic,
+ liberated_sig.safety,
+ liberated_sig.abi,
+ );
+
+ (
+ Ty::new_coroutine_closure(
+ interner,
+ coroutine_closure_id.into(),
+ closure_args.args,
+ ),
+ None,
+ )
}
};
@@ -209,9 +268,9 @@ impl<'db> InferenceContext<'_, 'db> {
// FIXME: lift these out into a struct
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let prev_closure = mem::replace(&mut self.current_closure, id);
- let prev_ret_ty = mem::replace(&mut self.return_ty, body_ret_ty);
- let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(body_ret_ty));
+ let prev_ret_ty = mem::replace(&mut self.return_ty, liberated_sig.output());
+ let prev_ret_coercion =
+ self.return_coercion.replace(CoerceMany::new(liberated_sig.output()));
let prev_resume_yield_tys = mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
@@ -221,10 +280,9 @@ impl<'db> InferenceContext<'_, 'db> {
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
self.return_coercion = prev_ret_coercion;
- self.current_closure = prev_closure;
self.resume_yield_tys = prev_resume_yield_tys;
- ty
+ closure_ty
}
fn fn_trait_kind_from_def_id(&self, trait_id: TraitId) -> Option<rustc_type_ir::ClosureKind> {
@@ -262,7 +320,7 @@ impl<'db> InferenceContext<'_, 'db> {
closure_kind: ClosureKind,
) -> (Option<PolyFnSig<'db>>, Option<rustc_type_ir::ClosureKind>) {
match expected_ty.kind() {
- TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id, args, .. }) => self
+ TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id }, args, .. }) => self
.deduce_closure_signature_from_predicates(
expected_ty,
closure_kind,
@@ -293,7 +351,9 @@ impl<'db> InferenceContext<'_, 'db> {
let expected_sig = sig_tys.with(hdr);
(Some(expected_sig), Some(rustc_type_ir::ClosureKind::Fn))
}
- ClosureKind::Coroutine(_) | ClosureKind::Async => (None, None),
+ ClosureKind::Coroutine(_)
+ | ClosureKind::AsyncClosure
+ | ClosureKind::AsyncBlock { .. } => (None, None),
},
_ => (None, None),
}
@@ -406,7 +466,7 @@ 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::Async => self
+ ClosureKind::AsyncClosure => self
.async_fn_trait_kind_from_def_id(trait_def_id)
.or_else(|| self.fn_trait_kind_from_def_id(trait_def_id)),
_ => None,
@@ -452,13 +512,13 @@ impl<'db> InferenceContext<'_, 'db> {
ClosureKind::Closure if Some(def_id) == self.lang_items.FnOnceOutput => {
self.extract_sig_from_projection(projection)
}
- ClosureKind::Async if Some(def_id) == self.lang_items.AsyncFnOnceOutput => {
+ ClosureKind::AsyncClosure 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::Async if Some(def_id) == self.lang_items.FnOnceOutput => {
+ ClosureKind::AsyncClosure if Some(def_id) == self.lang_items.FnOnceOutput => {
self.extract_sig_from_projection_and_future_bound(projection)
}
_ => None,
@@ -484,13 +544,8 @@ impl<'db> InferenceContext<'_, 'db> {
let ret_param_ty = projection.skip_binder().term.expect_type();
debug!(?ret_param_ty);
- let sig = projection.rebind(self.interner().mk_fn_sig(
- input_tys,
- ret_param_ty,
- false,
- Safety::Safe,
- FnAbi::Rust,
- ));
+ let sig =
+ projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, ret_param_ty));
Some(sig)
}
@@ -572,13 +627,7 @@ impl<'db> InferenceContext<'_, 'db> {
// 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 sig = projection.rebind(self.interner().mk_fn_sig(
- input_tys,
- return_ty,
- false,
- Safety::Safe,
- FnAbi::Rust,
- ));
+ let sig = projection.rebind(self.interner().mk_fn_sig_safe_rust_abi(input_tys, return_ty));
Some(sig)
}
diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs
index ce0ccfe82f..668d7496cd 100644
--- a/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/crates/hir-ty/src/infer/closure/analysis.rs
@@ -1,1310 +1,1676 @@
-//! Post-inference closure analysis: captures and closure kind.
+//! ### Inferring borrow kinds for upvars
+//!
+//! Whenever there is a closure expression, we need to determine how each
+//! upvar is used. We do this by initially assigning each upvar an
+//! immutable "borrow kind" (see `BorrowKind` for details) and then
+//! "escalating" the kind as needed. The borrow kind proceeds according to
+//! the following lattice:
+//! ```ignore (not-rust)
+//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow
+//! ```
+//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we
+//! will promote its borrow kind to mutable borrow. If we see an `&mut x`
+//! we'll do the same. Naturally, this applies not just to the upvar, but
+//! to everything owned by `x`, so the result is the same for something
+//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a
+//! struct). These adjustments are performed in
+//! `adjust_for_non_move_closure` (you can trace backwards through the code
+//! from there).
+//!
+//! The fact that we are inferring borrow kinds as we go results in a
+//! semi-hacky interaction with the way `ExprUseVisitor` is computing
+//! `Place`s. In particular, it will query the current borrow kind as it
+//! goes, and we'll return the *current* value, but this may get
+//! adjusted later. Therefore, in this module, we generally ignore the
+//! borrow kind (and derived mutabilities) that `ExprUseVisitor` returns
+//! within `Place`s, since they may be inaccurate. (Another option
+//! would be to use a unification scheme, where instead of returning a
+//! concrete borrow kind like `ty::ImmBorrow`, we return a
+//! `ty::InferBorrow(upvar_id)` or something like that, but this would
+//! then mean that all later passes would have to check for these figments
+//! and report an error, and it just seems like more mess in the end.)
-use std::{cmp, mem};
+use std::{iter, mem};
-use base_db::Crate;
use hir_def::{
- ExpressionStoreOwnerId, FieldId, HasModule, VariantId,
- expr_store::{Body, ExpressionStore, path::Path},
+ expr_store::ExpressionStore,
hir::{
- Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
- RecordSpread, Statement, UnaryOp,
+ BindingAnnotation, BindingId, CaptureBy, CoroutineSource, Expr, ExprId, ExprOrPatId, Pat,
+ PatId, Statement,
},
- item_tree::FieldsShape,
resolver::ValueNs,
- signatures::VariantFields,
};
+use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::Mutability;
-use rustc_hash::{FxHashMap, FxHashSet};
-use rustc_type_ir::inherent::{GenericArgs as _, IntoKind, Ty as _};
+use rustc_hash::{FxBuildHasher, FxHashMap};
+use rustc_type_ir::{
+ BoundVar, ClosureKind, TypeVisitableExt as _,
+ inherent::{AdtDef as _, GenericArgs as _, IntoKind as _, Ty as _},
+};
use smallvec::{SmallVec, smallvec};
-use stdx::{format_to, never};
-use syntax::utils::is_raw_identifier;
+use span::Edition;
+use tracing::{debug, instrument};
use crate::{
- Adjust, Adjustment, BindingMode,
- db::{HirDatabase, InternedClosure, InternedClosureId},
- display::{DisplayTarget, HirDisplay as _},
- infer::InferenceContext,
- mir::{BorrowKind, MirSpan, MutBorrowKind},
+ FnAbi,
+ infer::{
+ CaptureInfo, CaptureSourceStack, CapturedPlace, InferenceContext, UpvarCapture,
+ closure::analysis::expr_use_visitor::{
+ self as euv, FakeReadCause, Place, PlaceBase, PlaceWithOrigin, Projection,
+ ProjectionKind,
+ },
+ },
next_solver::{
- DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredEarlyBinder, StoredTy, Ty,
- TyKind,
- infer::{InferCtxt, traits::ObligationCause},
- obligation_ctxt::ObligationCtxt,
+ Binder, BoundRegion, BoundRegionKind, DbInterner, GenericArgs, Region, Ty, TyKind,
+ abi::Safety, infer::traits::ObligationCause, normalize,
},
- traits::FnTrait,
+ upvars::{Upvars, UpvarsRef},
};
-// The below functions handle capture and closure kind (Fn, FnMut, ..)
+pub(crate) mod expr_use_visitor;
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub(crate) enum HirPlaceProjection {
- Deref,
- Field(FieldId),
- TupleField(u32),
+#[derive(Debug, Copy, Clone, TypeVisitable, TypeFoldable)]
+enum UpvarArgs<'db> {
+ Closure(GenericArgs<'db>),
+ Coroutine(GenericArgs<'db>),
+ CoroutineClosure(GenericArgs<'db>),
}
-impl HirPlaceProjection {
- fn projected_ty<'db>(
- self,
- infcx: &InferCtxt<'db>,
- env: ParamEnv<'db>,
- mut base: Ty<'db>,
- krate: Crate,
- ) -> Ty<'db> {
- let interner = infcx.interner;
- let db = interner.db;
- if base.is_ty_error() {
- return Ty::new_error(interner, ErrorGuaranteed);
- }
-
- if matches!(base.kind(), TyKind::Alias(..)) {
- let mut ocx = ObligationCtxt::new(infcx);
- match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
- Ok(it) => base = it,
- Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
- }
- }
+impl<'db> UpvarArgs<'db> {
+ #[inline]
+ fn tupled_upvars_ty(self) -> Ty<'db> {
match self {
- HirPlaceProjection::Deref => match base.kind() {
- TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
- TyKind::Adt(adt_def, subst) if adt_def.is_box() => subst.type_at(0),
- _ => {
- never!(
- "Overloaded deref on type {} is not a projection",
- base.display(db, DisplayTarget::from_crate(db, krate))
- );
- Ty::new_error(interner, ErrorGuaranteed)
- }
- },
- HirPlaceProjection::Field(f) => match base.kind() {
- TyKind::Adt(_, subst) => {
- db.field_types(f.parent)[f.local_id].get().instantiate(interner, subst)
- }
- ty => {
- never!("Only adt has field, found {:?}", ty);
- Ty::new_error(interner, ErrorGuaranteed)
- }
- },
- HirPlaceProjection::TupleField(idx) => match base.kind() {
- TyKind::Tuple(subst) => {
- subst.as_slice().get(idx as usize).copied().unwrap_or_else(|| {
- never!("Out of bound tuple field");
- Ty::new_error(interner, ErrorGuaranteed)
- })
- }
- ty => {
- never!("Only tuple has tuple field: {:?}", ty);
- Ty::new_error(interner, ErrorGuaranteed)
- }
- },
+ UpvarArgs::Closure(args) => args.as_closure().tupled_upvars_ty(),
+ UpvarArgs::Coroutine(args) => args.as_coroutine().tupled_upvars_ty(),
+ UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().tupled_upvars_ty(),
}
}
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash, salsa::Update)]
-pub(crate) struct HirPlace {
- pub(crate) local: BindingId,
- pub(crate) projections: Vec<HirPlaceProjection>,
+#[derive(Eq, Clone, PartialEq, Debug, Copy, Hash)]
+pub enum BorrowKind {
+ /// Data must be immutable and is aliasable.
+ Immutable,
+
+ /// Data must be immutable but not aliasable. This kind of borrow
+ /// cannot currently be expressed by the user and is used only in
+ /// implicit closure bindings. It is needed when the closure
+ /// is borrowing or mutating a mutable referent, e.g.:
+ ///
+ /// ```
+ /// let mut z = 3;
+ /// let x: &mut isize = &mut z;
+ /// let y = || *x += 5;
+ /// ```
+ ///
+ /// If we were to try to translate this closure into a more explicit
+ /// form, we'd encounter an error with the code as written:
+ ///
+ /// ```compile_fail,E0594
+ /// struct Env<'a> { x: &'a &'a mut isize }
+ /// let mut z = 3;
+ /// let x: &mut isize = &mut z;
+ /// let y = (&mut Env { x: &x }, fn_ptr); // Closure is pair of env and fn
+ /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+ /// ```
+ ///
+ /// This is then illegal because you cannot mutate a `&mut` found
+ /// in an aliasable location. To solve, you'd have to translate with
+ /// an `&mut` borrow:
+ ///
+ /// ```compile_fail,E0596
+ /// struct Env<'a> { x: &'a mut &'a mut isize }
+ /// let mut z = 3;
+ /// let x: &mut isize = &mut z;
+ /// let y = (&mut Env { x: &mut x }, fn_ptr); // changed from &x to &mut x
+ /// fn fn_ptr(env: &mut Env) { **env.x += 5; }
+ /// ```
+ ///
+ /// Now the assignment to `**env.x` is legal, but creating a
+ /// mutable pointer to `x` is not because `x` is not mutable. We
+ /// could fix this by declaring `x` as `let mut x`. This is ok in
+ /// user code, if awkward, but extra weird for closures, since the
+ /// borrow is hidden.
+ ///
+ /// So we introduce a "unique imm" borrow -- the referent is
+ /// immutable, but not aliasable. This solves the problem. For
+ /// simplicity, we don't give users the way to express this
+ /// borrow, it's just used when translating closures.
+ ///
+ /// FIXME: Rename this to indicate the borrow is actually not immutable.
+ UniqueImmutable,
+
+ /// Data is mutable and not aliasable.
+ Mutable,
}
-impl HirPlace {
- fn ty<'db>(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> {
- let krate = ctx.krate();
- let mut ty = ctx.table.resolve_completely(ctx.result.binding_ty(self.local));
- for p in &self.projections {
- ty = p.projected_ty(ctx.infcx(), ctx.table.param_env, ty, krate);
+impl BorrowKind {
+ pub fn from_hir_mutbl(m: hir_def::hir::type_ref::Mutability) -> BorrowKind {
+ match m {
+ hir_def::hir::type_ref::Mutability::Mut => BorrowKind::Mutable,
+ hir_def::hir::type_ref::Mutability::Shared => BorrowKind::Immutable,
}
- ty
}
- fn capture_kind_of_truncated_place(
- &self,
- mut current_capture: CaptureKind,
- len: usize,
- ) -> CaptureKind {
- if let CaptureKind::ByRef(BorrowKind::Mut {
- kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
- }) = current_capture
- && self.projections[len..].contains(&HirPlaceProjection::Deref)
- {
- current_capture =
- CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture });
+ pub fn from_mutbl(m: Mutability) -> BorrowKind {
+ match m {
+ Mutability::Mut => BorrowKind::Mutable,
+ Mutability::Not => BorrowKind::Immutable,
}
- current_capture
}
-}
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum CaptureKind {
- ByRef(BorrowKind),
- ByValue,
-}
+ /// Returns a mutability `m` such that an `&m T` pointer could be used to obtain this borrow
+ /// kind. Because borrow kinds are richer than mutabilities, we sometimes have to pick a
+ /// mutability that is stronger than necessary so that it at least *would permit* the borrow in
+ /// question.
+ pub fn to_mutbl_lossy(self) -> Mutability {
+ match self {
+ BorrowKind::Mutable => Mutability::Mut,
+ BorrowKind::Immutable => Mutability::Not,
-#[derive(Debug, Clone, PartialEq, Eq, salsa::Update)]
-pub struct CapturedItem {
- pub(crate) place: HirPlace,
- pub(crate) kind: CaptureKind,
- /// The inner vec is the stacks; the outer vec is for each capture reference.
- ///
- /// Even though we always report only the last span (i.e. the most inclusive span),
- /// we need to keep them all, since when a closure occurs inside a closure, we
- /// copy all captures of the inner closure to the outer closure, and then we may
- /// truncate them, and we want the correct span to be reported.
- span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>,
- pub(crate) ty: StoredEarlyBinder<StoredTy>,
+ // We have no type corresponding to a unique imm borrow, so
+ // use `&mut`. It gives all the capabilities of a `&uniq`
+ // and hence is a safe "over approximation".
+ BorrowKind::UniqueImmutable => Mutability::Mut,
+ }
+ }
}
-impl CapturedItem {
- pub fn local(&self) -> BindingId {
- self.place.local
- }
+/// Describe the relationship between the paths of two places
+/// eg:
+/// - `foo` is ancestor of `foo.bar.baz`
+/// - `foo.bar.baz` is an descendant of `foo.bar`
+/// - `foo.bar` and `foo.baz` are divergent
+enum PlaceAncestryRelation {
+ Ancestor,
+ Descendant,
+ SamePlace,
+ Divergent,
+}
- /// Returns whether this place has any field (aka. non-deref) projections.
- pub fn has_field_projections(&self) -> bool {
- self.place.projections.iter().any(|it| !matches!(it, HirPlaceProjection::Deref))
- }
+/// Intermediate format to store a captured `Place` and associated `CaptureInfo`
+/// during capture analysis. Information in this map feeds into the minimum capture
+/// analysis pass.
+type InferredCaptureInformation = Vec<(Place, CaptureInfo)>;
- pub fn ty<'db>(&self, db: &'db dyn HirDatabase, subst: GenericArgs<'db>) -> Ty<'db> {
- let interner = DbInterner::new_no_crate(db);
- self.ty.get().instantiate(interner, subst.as_closure().parent_args())
- }
+impl<'a, 'db> InferenceContext<'a, 'db> {
+ pub(crate) fn closure_analyze(&mut self) {
+ let upvars = crate::upvars::upvars_mentioned(self.db, self.owner)
+ .unwrap_or(const { &FxHashMap::with_hasher(FxBuildHasher) });
+ for root_expr in self.store.expr_roots() {
+ self.analyze_closures_in_expr(root_expr, upvars);
+ }
- pub fn kind(&self) -> CaptureKind {
- self.kind
+ // it's our job to process these.
+ assert!(self.deferred_call_resolutions.is_empty());
}
- pub fn spans(&self) -> SmallVec<[MirSpan; 3]> {
- self.span_stacks.iter().map(|stack| *stack.last().expect("empty span stack")).collect()
- }
+ fn analyze_closures_in_expr(&mut self, expr: ExprId, upvars: &'db FxHashMap<ExprId, Upvars>) {
+ self.store.walk_child_exprs(expr, |expr| self.analyze_closures_in_expr(expr, upvars));
- /// Converts the place to a name that can be inserted into source code.
- pub fn place_to_name(&self, owner: ExpressionStoreOwnerId, db: &dyn HirDatabase) -> String {
- let krate = owner.krate(db);
- let edition = krate.data(db).edition;
- let mut result = match owner {
- ExpressionStoreOwnerId::Signature(generic_def_id) => {
- ExpressionStore::of(db, generic_def_id.into())[self.place.local]
- .name
- .display(db, edition)
- .to_string()
- }
- ExpressionStoreOwnerId::Body(def_with_body_id) => Body::of(db, def_with_body_id)
- [self.place.local]
- .name
- .display(db, edition)
- .to_string(),
- ExpressionStoreOwnerId::VariantFields(variant_id) => {
- let fields = VariantFields::of(db, variant_id);
- fields.store[self.place.local].name.display(db, edition).to_string()
- }
- };
- for proj in &self.place.projections {
- match proj {
- HirPlaceProjection::Deref => {}
- HirPlaceProjection::Field(f) => {
- let variant_data = f.parent.fields(db);
- match variant_data.shape {
- FieldsShape::Record => {
- result.push('_');
- result.push_str(variant_data.fields()[f.local_id].name.as_str())
- }
- FieldsShape::Tuple => {
- let index =
- variant_data.fields().iter().position(|it| it.0 == f.local_id);
- if let Some(index) = index {
- format_to!(result, "_{index}");
- }
- }
- FieldsShape::Unit => {}
- }
- }
- HirPlaceProjection::TupleField(idx) => {
- format_to!(result, "_{idx}")
- }
+ match &self.store[expr] {
+ Expr::Closure { args, body, closure_kind, capture_by, .. } => {
+ self.analyze_closure(
+ expr,
+ args,
+ *body,
+ *capture_by,
+ *closure_kind,
+ upvars.get(&expr).map(|upvars| upvars.as_ref()).unwrap_or_default(),
+ );
}
+ _ => {}
}
- if is_raw_identifier(&result, owner.module(db).krate(db).data(db).edition) {
- result.insert_str(0, "r#");
- }
- result
}
- pub fn display_place_source_code(
- &self,
- owner: ExpressionStoreOwnerId,
- db: &dyn HirDatabase,
- ) -> String {
- let krate = owner.krate(db);
- let edition = krate.data(db).edition;
- let mut result = match owner {
- ExpressionStoreOwnerId::Signature(generic_def_id) => {
- ExpressionStore::of(db, generic_def_id.into())[self.place.local]
- .name
- .display(db, edition)
- .to_string()
+ /// Analysis starting point.
+ #[instrument(skip(self, body), level = "debug")]
+ fn analyze_closure(
+ &mut self,
+ closure_expr_id: ExprId,
+ params: &[PatId],
+ body: ExprId,
+ mut capture_clause: CaptureBy,
+ closure_kind: hir_def::hir::ClosureKind,
+ upvars: UpvarsRef<'db>,
+ ) {
+ // Extract the type of the closure.
+ let ty = self.expr_ty(closure_expr_id);
+ let (args, infer_kind) = match ty.kind() {
+ TyKind::Closure(_def_id, args) => {
+ (UpvarArgs::Closure(args), self.infcx().closure_kind(ty).is_none())
}
- ExpressionStoreOwnerId::Body(def_with_body_id) => Body::of(db, def_with_body_id)
- [self.place.local]
- .name
- .display(db, edition)
- .to_string(),
- ExpressionStoreOwnerId::VariantFields(variant_id) => {
- let fields = VariantFields::of(db, variant_id);
- fields.store[self.place.local].name.display(db, edition).to_string()
+ TyKind::CoroutineClosure(_def_id, args) => {
+ (UpvarArgs::CoroutineClosure(args), self.infcx().closure_kind(ty).is_none())
}
- };
- for proj in &self.place.projections {
- match proj {
- // In source code autoderef kicks in.
- HirPlaceProjection::Deref => {}
- HirPlaceProjection::Field(f) => {
- let variant_data = f.parent.fields(db);
- match variant_data.shape {
- FieldsShape::Record => format_to!(
- result,
- ".{}",
- variant_data.fields()[f.local_id].name.display(db, edition)
- ),
- FieldsShape::Tuple => format_to!(
- result,
- ".{}",
- variant_data
- .fields()
- .iter()
- .position(|it| it.0 == f.local_id)
- .unwrap_or_default()
- ),
- FieldsShape::Unit => {}
- }
- }
- HirPlaceProjection::TupleField(idx) => {
- format_to!(result, ".{idx}")
- }
+ TyKind::Coroutine(_def_id, args) => (UpvarArgs::Coroutine(args), false),
+ TyKind::Error(_) => {
+ // #51714: skip analysis when we have already encountered type errors
+ return;
}
- }
- let final_derefs_count = self
- .place
- .projections
- .iter()
- .rev()
- .take_while(|proj| matches!(proj, HirPlaceProjection::Deref))
- .count();
- result.insert_str(0, &"*".repeat(final_derefs_count));
- result
- }
-
- pub fn display_place(&self, owner: ExpressionStoreOwnerId, db: &dyn HirDatabase) -> String {
- let krate = owner.krate(db);
- let edition = krate.data(db).edition;
- let mut result = match owner {
- ExpressionStoreOwnerId::Signature(generic_def_id) => {
- ExpressionStore::of(db, generic_def_id.into())[self.place.local]
- .name
- .display(db, edition)
- .to_string()
- }
- ExpressionStoreOwnerId::Body(def_with_body_id) => Body::of(db, def_with_body_id)
- [self.place.local]
- .name
- .display(db, edition)
- .to_string(),
- ExpressionStoreOwnerId::VariantFields(variant_id) => {
- let fields = VariantFields::of(db, variant_id);
- fields.store[self.place.local].name.display(db, edition).to_string()
+ _ => {
+ panic!("type of closure expr {:?} is not a closure {:?}", closure_expr_id, ty);
}
};
- let mut field_need_paren = false;
- for proj in &self.place.projections {
- match proj {
- HirPlaceProjection::Deref => {
- result = format!("*{result}");
- field_need_paren = true;
- }
- HirPlaceProjection::Field(f) => {
- if field_need_paren {
- result = format!("({result})");
- }
- let variant_data = f.parent.fields(db);
- let field = match variant_data.shape {
- FieldsShape::Record => {
- variant_data.fields()[f.local_id].name.as_str().to_owned()
- }
- FieldsShape::Tuple => variant_data
- .fields()
- .iter()
- .position(|it| it.0 == f.local_id)
- .unwrap_or_default()
- .to_string(),
- FieldsShape::Unit => "[missing field]".to_owned(),
- };
- result = format!("{result}.{field}");
- field_need_paren = false;
- }
- HirPlaceProjection::TupleField(idx) => {
- if field_need_paren {
- result = format!("({result})");
- }
- result = format!("{result}.{idx}");
- field_need_paren = false;
- }
+ let args = self.infcx().resolve_vars_if_possible(args);
+
+ let mut delegate = InferBorrowKind {
+ closure_def_id: closure_expr_id,
+ capture_information: Default::default(),
+ fake_reads: Default::default(),
+ };
+
+ let _ = euv::ExprUseVisitor::new(self, closure_expr_id, upvars, &mut delegate)
+ .consume_closure_body(params, body);
+
+ // There are several curious situations with coroutine-closures where
+ // analysis is too aggressive with borrows when the coroutine-closure is
+ // marked `move`. Specifically:
+ //
+ // 1. If the coroutine-closure was inferred to be `FnOnce` during signature
+ // inference, then it's still possible that we try to borrow upvars from
+ // the coroutine-closure because they are not used by the coroutine body
+ // in a way that forces a move. See the test:
+ // `async-await/async-closures/force-move-due-to-inferred-kind.rs`.
+ //
+ // 2. If the coroutine-closure is forced to be `FnOnce` due to the way it
+ // uses its upvars (e.g. it consumes a non-copy value), but not *all* upvars
+ // would force the closure to `FnOnce`.
+ // See the test: `async-await/async-closures/force-move-due-to-actually-fnonce.rs`.
+ //
+ // This would lead to an impossible to satisfy situation, since `AsyncFnOnce`
+ // coroutine bodies can't borrow from their parent closure. To fix this,
+ // we force the inner coroutine to also be `move`. This only matters for
+ // 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 } =
+ closure_kind
+ && let parent_hir_id = ExpressionStore::closure_for_coroutine(closure_expr_id)
+ && let parent_ty = self.result.expr_ty(parent_hir_id)
+ && let Expr::Closure { capture_by: CaptureBy::Value, .. } = self.store[parent_hir_id]
+ {
+ // (1.) Closure signature inference forced this closure to `FnOnce`.
+ if let Some(ClosureKind::FnOnce) = self.infcx().closure_kind(parent_ty) {
+ capture_clause = CaptureBy::Value;
+ }
+ // (2.) The way that the closure uses its upvars means it's `FnOnce`.
+ else if self.coroutine_body_consumes_upvars(closure_expr_id, body, upvars) {
+ capture_clause = CaptureBy::Value;
}
}
- result
- }
-}
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct CapturedItemWithoutTy {
- pub(crate) place: HirPlace,
- pub(crate) kind: CaptureKind,
- /// The inner vec is the stacks; the outer vec is for each capture reference.
- pub(crate) span_stacks: SmallVec<[SmallVec<[MirSpan; 3]>; 3]>,
-}
-
-impl CapturedItemWithoutTy {
- fn with_ty(self, ctx: &mut InferenceContext<'_, '_>) -> CapturedItem {
- let ty = self.place.ty(ctx);
- let ty = match &self.kind {
- CaptureKind::ByValue => ty,
- CaptureKind::ByRef(bk) => {
- let m = match bk {
- BorrowKind::Mut { .. } => Mutability::Mut,
- _ => Mutability::Not,
+ // As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
+ // to `ByRef` for the `async {}` block internal to async fns/closure. This means
+ // that we would *not* be moving all of the parameters into the async block in all cases.
+ // For example, when one of the arguments is `Copy`, we turn a consuming use into a copy of
+ // a reference, so for `async fn x(t: i32) {}`, we'd only take a reference to `t`.
+ //
+ // We force all of these arguments to be captured by move before we do expr use analysis.
+ //
+ // 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 {
+ source: CoroutineSource::Fn | CoroutineSource::Closure,
+ } = closure_kind
+ {
+ let Expr::Block { statements, .. } = &self.store[body] else {
+ panic!();
+ };
+ for stmt in statements {
+ let Statement::Let { pat, initializer: Some(init), .. } = *stmt else {
+ panic!();
+ };
+ let Pat::Bind { .. } = self.store[pat] else {
+ // Complex pattern, skip the non-upvar local.
+ continue;
+ };
+ let Expr::Path(path) = &self.store[init] else {
+ panic!();
+ };
+ let update_guard = self.resolver.update_to_inner_scope(self.db, self.owner, init);
+ let Some(ValueNs::LocalBinding(local_id)) =
+ self.resolver.resolve_path_in_value_ns_fully(
+ self.db,
+ path,
+ self.store.expr_path_hygiene(init),
+ )
+ else {
+ panic!();
};
- Ty::new_ref(ctx.interner(), ctx.types.regions.error, ty, m)
+ self.resolver.reset_to_guard(update_guard);
+ let place = self.place_for_root_variable(closure_expr_id, local_id);
+ delegate.capture_information.push((
+ place,
+ CaptureInfo {
+ sources: smallvec![CaptureSourceStack::from_single(init.into())],
+ capture_kind: UpvarCapture::ByValue,
+ },
+ ));
}
- };
- CapturedItem {
- place: self.place,
- kind: self.kind,
- span_stacks: self.span_stacks,
- ty: StoredEarlyBinder::bind(ty.store()),
}
- }
-}
-impl<'db> InferenceContext<'_, 'db> {
- fn place_of_expr(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
- let r = self.place_of_expr_without_adjust(tgt_expr)?;
- let adjustments =
- self.result.expr_adjustments.get(&tgt_expr).map(|it| &**it).unwrap_or_default();
- apply_adjusts_to_place(&mut self.current_capture_span_stack, r, adjustments)
- }
+ debug!(
+ "For closure={:?}, capture_information={:#?}",
+ closure_expr_id, delegate.capture_information
+ );
- /// Pushes the span into `current_capture_span_stack`, *without clearing it first*.
- fn path_place(&mut self, path: &Path, id: ExprOrPatId) -> Option<HirPlace> {
- if path.type_anchor().is_some() {
- return None;
- }
- let hygiene = self.store.expr_or_pat_path_hygiene(id);
- self.resolver.resolve_path_in_value_ns_fully(self.db, path, hygiene).and_then(|result| {
- match result {
- ValueNs::LocalBinding(binding) => {
- let mir_span = match id {
- ExprOrPatId::ExprId(id) => MirSpan::ExprId(id),
- ExprOrPatId::PatId(id) => MirSpan::PatId(id),
- };
- self.current_capture_span_stack.push(mir_span);
- Some(HirPlace { local: binding, projections: Vec::new() })
- }
- _ => None,
- }
- })
- }
+ let (capture_information, closure_kind, _origin) = self
+ .process_collected_capture_information(capture_clause, &delegate.capture_information);
- /// Changes `current_capture_span_stack` to contain the stack of spans for this expr.
- fn place_of_expr_without_adjust(&mut self, tgt_expr: ExprId) -> Option<HirPlace> {
- self.current_capture_span_stack.clear();
- match &self.store[tgt_expr] {
- Expr::Path(p) => {
- let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
- let result = self.path_place(p, tgt_expr.into());
- self.resolver.reset_to_guard(resolver_guard);
- return result;
- }
- Expr::Field { expr, name: _ } => {
- let mut place = self.place_of_expr(*expr)?;
- let field = self.result.field_resolution(tgt_expr)?;
- self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
- place.projections.push(field.either(HirPlaceProjection::Field, |f| {
- HirPlaceProjection::TupleField(f.index)
- }));
- return Some(place);
- }
- Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
- let is_builtin_deref = match self.expr_ty(*expr).kind() {
- TyKind::Ref(..) | TyKind::RawPtr(..) => true,
- TyKind::Adt(adt_def, _) if adt_def.is_box() => true,
- _ => false,
+ self.compute_min_captures(closure_expr_id, capture_information);
+
+ // We now fake capture information for all variables that are mentioned within the closure
+ // We do this after handling migrations so that min_captures computes before
+ if !enable_precise_capture(self.edition) {
+ let mut capture_information: InferredCaptureInformation = Default::default();
+
+ for var_hir_id in upvars.iter() {
+ let place = Place {
+ base_ty: self.result.binding_ty(var_hir_id).store(),
+ base: PlaceBase::Upvar { closure: closure_expr_id, var_id: var_hir_id },
+ projections: Vec::new(),
};
- if is_builtin_deref {
- let mut place = self.place_of_expr(*expr)?;
- self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
- place.projections.push(HirPlaceProjection::Deref);
- return Some(place);
- }
- }
- _ => (),
- }
- None
- }
- fn push_capture(&mut self, place: HirPlace, kind: CaptureKind) {
- self.current_captures.push(CapturedItemWithoutTy {
- place,
- kind,
- span_stacks: smallvec![self.current_capture_span_stack.iter().copied().collect()],
- });
- }
+ debug!("seed place {:?}", place);
- fn truncate_capture_spans(&self, capture: &mut CapturedItemWithoutTy, mut truncate_to: usize) {
- // The first span is the identifier, and it must always remain.
- truncate_to += 1;
- for span_stack in &mut capture.span_stacks {
- let mut remained = truncate_to;
- let mut actual_truncate_to = 0;
- for &span in &*span_stack {
- actual_truncate_to += 1;
- if !span.is_ref_span(self.store) {
- remained -= 1;
- if remained == 0 {
- break;
- }
- }
- }
- if actual_truncate_to < span_stack.len()
- && span_stack[actual_truncate_to].is_ref_span(self.store)
- {
- // Include the ref operator if there is one, we will fix it later (in `strip_captures_ref_span()`) if it's incorrect.
- actual_truncate_to += 1;
+ let capture_kind = self.init_capture_kind_for_place(&place, capture_clause);
+ let fake_info = CaptureInfo { sources: SmallVec::new(), capture_kind };
+
+ capture_information.push((place, fake_info));
}
- span_stack.truncate(actual_truncate_to);
- }
- }
- fn ref_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
- if let Some(place) = place {
- self.add_capture(place, CaptureKind::ByRef(BorrowKind::Shared));
+ // This will update the min captures based on this new fake information.
+ self.compute_min_captures(closure_expr_id, capture_information);
}
- self.walk_expr(expr);
- }
- fn add_capture(&mut self, place: HirPlace, kind: CaptureKind) {
- if self.is_upvar(&place) {
- self.push_capture(place, kind);
+ if infer_kind {
+ // Unify the (as yet unbound) type variable in the closure
+ // args with the kind we inferred.
+ let closure_kind_ty = match args {
+ UpvarArgs::Closure(args) => args.as_closure().kind_ty(),
+ UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().kind_ty(),
+ UpvarArgs::Coroutine(_) => unreachable!("coroutines don't have an inferred kind"),
+ };
+ _ = self.demand_eqtype(
+ closure_expr_id.into(),
+ Ty::from_closure_kind(self.interner(), closure_kind),
+ closure_kind_ty,
+ );
}
- }
- fn mutate_path_pat(&mut self, path: &Path, id: PatId) {
- if let Some(place) = self.path_place(path, id.into()) {
- self.add_capture(
- place,
- CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
+ // 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()
+ {
+ let closure_env_region: Region<'_> = Region::new_bound(
+ self.interner(),
+ rustc_type_ir::INNERMOST,
+ BoundRegion { var: BoundVar::ZERO, kind: BoundRegionKind::ClosureEnv },
);
- self.current_capture_span_stack.pop(); // Remove the pattern span.
- }
- }
- fn mutate_expr(&mut self, expr: ExprId, place: Option<HirPlace>) {
- if let Some(place) = place {
- self.add_capture(
- place,
- CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
+ let num_args = args
+ .as_coroutine_closure()
+ .coroutine_closure_sig()
+ .skip_binder()
+ .tupled_inputs_ty
+ .tuple_fields()
+ .len();
+
+ let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter(
+ self.interner(),
+ analyze_coroutine_closure_captures(
+ self.closure_min_captures_flattened(closure_expr_id),
+ self.closure_min_captures_flattened(ExpressionStore::coroutine_for_closure(
+ closure_expr_id,
+ ))
+ // Skip the captures that are just moving the closure's args
+ // into the coroutine. These are always by move, and we append
+ // those later in the `CoroutineClosureSignature` helper functions.
+ .skip(num_args),
+ |(_, parent_capture), (_, child_capture)| {
+ // This is subtle. See documentation on function.
+ let needs_ref = should_reborrow_from_env_of_parent_coroutine_closure(
+ parent_capture,
+ child_capture,
+ );
+
+ let upvar_ty = child_capture.place.ty();
+ let capture = child_capture.info.capture_kind;
+ // Not all upvars are captured by ref, so use
+ // `apply_capture_kind_on_capture_ty` to ensure that we
+ // compute the right captured type.
+ apply_capture_kind_on_capture_ty(
+ self.interner(),
+ upvar_ty,
+ capture,
+ if needs_ref { closure_env_region } else { self.types.regions.erased },
+ )
+ },
+ ),
);
+ let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
+ self.interner(),
+ Binder::bind_with_vars(
+ self.interner().mk_fn_sig(
+ [],
+ tupled_upvars_ty_for_borrow,
+ false,
+ Safety::Safe,
+ FnAbi::Rust,
+ ),
+ self.types.coroutine_captures_by_ref_bound_var_kinds,
+ ),
+ );
+ _ = self.demand_eqtype(
+ closure_expr_id.into(),
+ args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
+ coroutine_captures_by_ref_ty,
+ );
+
+ // Additionally, we can now constrain the coroutine's kind type.
+ //
+ // We only do this if `infer_kind`, because if we have constrained
+ // the kind from closure signature inference, the kind inferred
+ // for the inner coroutine may actually be more restrictive.
+ if infer_kind {
+ let TyKind::Coroutine(_, coroutine_args) = self.result.expr_ty(body).kind() else {
+ panic!();
+ };
+ _ = self.demand_eqtype(
+ closure_expr_id.into(),
+ coroutine_args.as_coroutine().kind_ty(),
+ Ty::from_coroutine_closure_kind(self.interner(), closure_kind),
+ );
+ }
}
- self.walk_expr(expr);
- }
- fn consume_expr(&mut self, expr: ExprId) {
- if let Some(place) = self.place_of_expr(expr) {
- self.consume_place(place);
+ // Now that we've analyzed the closure, we know how each
+ // variable is borrowed, and we know what traits the closure
+ // implements (Fn vs FnMut etc). We now have some updates to do
+ // with that information.
+ //
+ // Note that no closure type C may have an upvar of type C
+ // (though it may reference itself via a trait object). This
+ // results from the desugaring of closures to a struct like
+ // `Foo<..., UV0...UVn>`. If one of those upvars referenced
+ // C, then the type would have infinite size (and the
+ // inference algorithm will reject it).
+
+ // Equate the type variables for the upvars with the actual types.
+ let final_upvar_tys = self.final_upvar_tys(closure_expr_id);
+ debug!(?closure_expr_id, ?args, ?final_upvar_tys);
+
+ // 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);
+
+ let fake_reads = delegate.fake_reads;
+
+ self.result.closures_data.entry(closure_expr_id).or_default().fake_reads =
+ fake_reads.into_boxed_slice();
+
+ // If we are also inferred the closure kind here,
+ // process any deferred resolutions.
+ let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_expr_id);
+ for deferred_call_resolution in deferred_call_resolutions {
+ deferred_call_resolution.resolve(self);
}
- self.walk_expr(expr);
}
- fn consume_place(&mut self, place: HirPlace) {
- if self.is_upvar(&place) {
- let ty = place.ty(self);
- let kind = if self.is_ty_copy(ty) {
- CaptureKind::ByRef(BorrowKind::Shared)
- } else {
- CaptureKind::ByValue
- };
- self.push_capture(place, kind);
- }
+ /// Determines whether the body of the coroutine uses its upvars in a way that
+ /// consumes (i.e. moves) the value, which would force the coroutine to `FnOnce`.
+ /// In a more detailed comment above, we care whether this happens, since if
+ /// this happens, we want to force the coroutine to move all of the upvars it
+ /// would've borrowed from the parent coroutine-closure.
+ ///
+ /// This only really makes sense to be called on the child coroutine of a
+ /// coroutine-closure.
+ fn coroutine_body_consumes_upvars(
+ &mut self,
+ coroutine_def_id: ExprId,
+ body: ExprId,
+ upvars: UpvarsRef<'db>,
+ ) -> bool {
+ let mut delegate = InferBorrowKind {
+ closure_def_id: coroutine_def_id,
+ capture_information: Default::default(),
+ fake_reads: Default::default(),
+ };
+
+ let _ = euv::ExprUseVisitor::new(self, coroutine_def_id, upvars, &mut delegate)
+ .consume_expr(body);
+
+ let (_, kind, _) = self
+ .process_collected_capture_information(CaptureBy::Ref, &delegate.capture_information);
+
+ matches!(kind, ClosureKind::FnOnce)
}
- fn walk_expr_with_adjust(&mut self, tgt_expr: ExprId, adjustment: &[Adjustment]) {
- if let Some((last, rest)) = adjustment.split_last() {
- match &last.kind {
- Adjust::NeverToAny | Adjust::Deref(None) | Adjust::Pointer(_) => {
- self.walk_expr_with_adjust(tgt_expr, rest)
- }
- Adjust::Deref(Some(m)) => match m.0 {
- Some(m) => {
- self.ref_capture_with_adjusts(m, tgt_expr, rest);
- }
- None => unreachable!(),
- },
- Adjust::Borrow(b) => {
- self.ref_capture_with_adjusts(b.mutability(), tgt_expr, rest);
- }
- }
- } else {
- self.walk_expr_without_adjust(tgt_expr);
- }
+ // Returns a list of `Ty`s for each upvar.
+ fn final_upvar_tys(&self, closure_id: ExprId) -> Vec<Ty<'db>> {
+ self.closure_min_captures_flattened(closure_id)
+ .map(|captured_place| {
+ let upvar_ty = captured_place.place.ty();
+ let capture = captured_place.info.capture_kind;
+
+ debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability);
+
+ apply_capture_kind_on_capture_ty(
+ self.interner(),
+ upvar_ty,
+ capture,
+ self.types.regions.erased,
+ )
+ })
+ .collect()
}
- fn ref_capture_with_adjusts(&mut self, m: Mutability, tgt_expr: ExprId, rest: &[Adjustment]) {
- let capture_kind = match m {
- Mutability::Mut => CaptureKind::ByRef(BorrowKind::Mut { kind: MutBorrowKind::Default }),
- Mutability::Not => CaptureKind::ByRef(BorrowKind::Shared),
- };
- if let Some(place) = self.place_of_expr_without_adjust(tgt_expr)
- && let Some(place) =
- apply_adjusts_to_place(&mut self.current_capture_span_stack, place, rest)
- {
- self.add_capture(place, capture_kind);
- }
- self.walk_expr_with_adjust(tgt_expr, rest);
+ /// Adjusts the closure capture information to ensure that the operations aren't unsafe,
+ /// and that the path can be captured with required capture kind (depending on use in closure,
+ /// move closure etc.)
+ ///
+ /// Returns the set of adjusted information along with the inferred closure kind and span
+ /// associated with the closure kind inference.
+ ///
+ /// Note that we *always* infer a minimal kind, even if
+ /// we don't always *use* that in the final result (i.e., sometimes
+ /// we've taken the closure kind from the expectations instead, and
+ /// for coroutines we don't even implement the closure traits
+ /// really).
+ ///
+ /// If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple
+ /// contains a `Some()` with the `Place` that caused us to do so.
+ fn process_collected_capture_information(
+ &mut self,
+ capture_clause: CaptureBy,
+ capture_information: &InferredCaptureInformation,
+ ) -> (InferredCaptureInformation, ClosureKind, Option<Place>) {
+ let mut closure_kind = ClosureKind::LATTICE_BOTTOM;
+ let mut origin: Option<Place> = None;
+
+ let processed = capture_information
+ .iter()
+ .cloned()
+ .map(|(place, mut capture_info)| {
+ // Apply rules for safety before inferring closure kind
+ let place = restrict_capture_precision(place, &mut capture_info);
+
+ let place = truncate_capture_for_optimization(place, &mut capture_info);
+
+ let updated = match capture_info.capture_kind {
+ UpvarCapture::ByValue => match closure_kind {
+ ClosureKind::Fn | ClosureKind::FnMut => {
+ (ClosureKind::FnOnce, Some(place.clone()))
+ }
+ // If closure is already FnOnce, don't update
+ ClosureKind::FnOnce => (closure_kind, origin.take()),
+ },
+
+ UpvarCapture::ByRef(BorrowKind::Mutable | BorrowKind::UniqueImmutable) => {
+ match closure_kind {
+ ClosureKind::Fn => (ClosureKind::FnMut, Some(place.clone())),
+ // Don't update the origin
+ ClosureKind::FnMut | ClosureKind::FnOnce => {
+ (closure_kind, origin.take())
+ }
+ }
+ }
+
+ _ => (closure_kind, origin.take()),
+ };
+
+ closure_kind = updated.0;
+ origin = updated.1;
+
+ let place = match capture_clause {
+ CaptureBy::Value => adjust_for_move_closure(place, &mut capture_info),
+ CaptureBy::Ref => adjust_for_non_move_closure(place, &mut capture_info),
+ };
+
+ // This restriction needs to be applied after we have handled adjustments for `move`
+ // closures. We want to make sure any adjustment that might make us move the place into
+ // the closure gets handled.
+ let place = restrict_precision_for_drop_types(self, place, &mut capture_info);
+
+ (place, capture_info)
+ })
+ .collect();
+
+ (processed, closure_kind, origin)
}
- fn walk_expr(&mut self, tgt_expr: ExprId) {
- if let Some(it) = self.result.expr_adjustments.get_mut(&tgt_expr) {
- // FIXME: this take is completely unneeded, and just is here to make borrow checker
- // happy. Remove it if you can.
- let x_taken = mem::take(it);
- self.walk_expr_with_adjust(tgt_expr, &x_taken);
- *self.result.expr_adjustments.get_mut(&tgt_expr).unwrap() = x_taken;
- } else {
- self.walk_expr_without_adjust(tgt_expr);
+ /// Analyzes the information collected by `InferBorrowKind` to compute the min number of
+ /// Places (and corresponding capture kind) that we need to keep track of to support all
+ /// the required captured paths.
+ ///
+ ///
+ /// Note: If this function is called multiple times for the same closure, it will update
+ /// the existing min_capture map that is stored in TypeckResults.
+ ///
+ /// Eg:
+ /// ```
+ /// #[derive(Debug)]
+ /// struct Point { x: i32, y: i32 }
+ ///
+ /// let s = String::from("s"); // hir_id_s
+ /// let mut p = Point { x: 2, y: -2 }; // his_id_p
+ /// let c = || {
+ /// println!("{s:?}"); // L1
+ /// p.x += 10; // L2
+ /// println!("{}" , p.y); // L3
+ /// println!("{p:?}"); // L4
+ /// drop(s); // L5
+ /// };
+ /// ```
+ /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on
+ /// the lines L1..5 respectively.
+ ///
+ /// InferBorrowKind results in a structure like this:
+ ///
+ /// ```ignore (illustrative)
+ /// {
+ /// Place(base: hir_id_s, projections: [], ....) -> {
+ /// capture_kind_expr: hir_id_L5,
+ /// path_expr_id: hir_id_L5,
+ /// capture_kind: ByValue
+ /// },
+ /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> {
+ /// capture_kind_expr: hir_id_L2,
+ /// path_expr_id: hir_id_L2,
+ /// capture_kind: ByValue
+ /// },
+ /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> {
+ /// capture_kind_expr: hir_id_L3,
+ /// path_expr_id: hir_id_L3,
+ /// capture_kind: ByValue
+ /// },
+ /// Place(base: hir_id_p, projections: [], ...) -> {
+ /// capture_kind_expr: hir_id_L4,
+ /// path_expr_id: hir_id_L4,
+ /// capture_kind: ByValue
+ /// },
+ /// }
+ /// ```
+ ///
+ /// After the min capture analysis, we get:
+ /// ```ignore (illustrative)
+ /// {
+ /// hir_id_s -> [
+ /// Place(base: hir_id_s, projections: [], ....) -> {
+ /// capture_kind_expr: hir_id_L5,
+ /// path_expr_id: hir_id_L5,
+ /// capture_kind: ByValue
+ /// },
+ /// ],
+ /// hir_id_p -> [
+ /// Place(base: hir_id_p, projections: [], ...) -> {
+ /// capture_kind_expr: hir_id_L2,
+ /// path_expr_id: hir_id_L4,
+ /// capture_kind: ByValue
+ /// },
+ /// ],
+ /// }
+ /// ```
+ #[instrument(level = "debug", skip(self))]
+ fn compute_min_captures(
+ &mut self,
+ closure_def_id: ExprId,
+ capture_information: InferredCaptureInformation,
+ ) {
+ if capture_information.is_empty() {
+ return;
}
- }
- fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
- match &self.store[tgt_expr] {
- Expr::OffsetOf(_) => (),
- Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op {
- AsmOperand::In { expr, .. }
- | AsmOperand::Out { expr: Some(expr), .. }
- | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr),
- AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- self.walk_expr_without_adjust(*in_expr);
- if let Some(out_expr) = out_expr {
- self.walk_expr_without_adjust(*out_expr);
+ let mut closure_data =
+ self.result.closures_data.remove(&closure_def_id).unwrap_or_default();
+ let root_var_min_capture_list = &mut closure_data.min_captures;
+ let mut dedup_sources_scratch = FxHashMap::default();
+
+ for (mut place, capture_info) in capture_information.into_iter() {
+ let var_hir_id = match place.base {
+ PlaceBase::Upvar { var_id, .. } => var_id,
+ base => panic!("Expected upvar, found={:?}", base),
+ };
+
+ let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else {
+ let mutability = self.determine_capture_mutability(&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;
+ };
+
+ // Go through each entry in the current list of min_captures
+ // - if ancestor is found, update its capture kind to account for current place's
+ // capture information.
+ //
+ // - if descendant is found, remove it from the list, and update the current place's
+ // capture information to account for the descendant's capture kind.
+ //
+ // We can never be in a case where the list contains both an ancestor and a descendant
+ // Also there can only be ancestor but in case of descendants there might be
+ // multiple.
+
+ let mut descendant_found = false;
+ let mut updated_capture_info = capture_info;
+ min_cap_list.retain(|possible_descendant| {
+ match determine_place_ancestry_relation(&place, &possible_descendant.place) {
+ // current place is ancestor of possible_descendant
+ PlaceAncestryRelation::Ancestor => {
+ descendant_found = true;
+
+ let mut possible_descendant = possible_descendant.clone();
+
+ // Truncate the descendant (already in min_captures) to be same as the ancestor to handle any
+ // possible change in capture mode.
+ truncate_place_to_len_and_update_capture_kind(
+ &mut possible_descendant.place,
+ &mut possible_descendant.info,
+ place.projections.len(),
+ );
+
+ let backup_path_sources = determine_capture_sources(
+ &mut updated_capture_info,
+ &mut possible_descendant.info,
+ &mut dedup_sources_scratch,
+ );
+ determine_capture_info(
+ &mut updated_capture_info,
+ &mut possible_descendant.info,
+ );
+
+ // we need to keep the ancestor's `path_expr_id`
+ updated_capture_info.sources = backup_path_sources;
+ false
}
+
+ _ => true,
}
- AsmOperand::Out { expr: None, .. }
- | AsmOperand::Const(_)
- | AsmOperand::Label(_)
- | AsmOperand::Sym(_) => (),
- }),
- Expr::If { condition, then_branch, else_branch } => {
- self.consume_expr(*condition);
- self.consume_expr(*then_branch);
- if let &Some(expr) = else_branch {
- self.consume_expr(expr);
- }
- }
- Expr::Async { statements, tail, .. }
- | Expr::Unsafe { statements, tail, .. }
- | Expr::Block { statements, tail, .. } => {
- for s in statements.iter() {
- match s {
- Statement::Let { pat, type_ref: _, initializer, else_branch } => {
- if let Some(else_branch) = else_branch {
- self.consume_expr(*else_branch);
- }
- if let Some(initializer) = initializer {
- if else_branch.is_some() {
- self.consume_expr(*initializer);
- } else {
- self.walk_expr(*initializer);
- }
- if let Some(place) = self.place_of_expr(*initializer) {
- self.consume_with_pat(place, *pat);
- }
- }
+ });
+
+ let mut ancestor_found = false;
+ if !descendant_found {
+ for possible_ancestor in min_cap_list.iter_mut() {
+ match determine_place_ancestry_relation(&place, &possible_ancestor.place) {
+ PlaceAncestryRelation::SamePlace => {
+ ancestor_found = true;
+ let backup_path_sources = determine_capture_sources(
+ &mut updated_capture_info,
+ &mut possible_ancestor.info,
+ &mut dedup_sources_scratch,
+ );
+ determine_capture_info(
+ &mut possible_ancestor.info,
+ &mut updated_capture_info,
+ );
+ possible_ancestor.info.sources = backup_path_sources;
+
+ // Only one related place will be in the list.
+ break;
}
- Statement::Expr { expr, has_semi: _ } => {
- self.consume_expr(*expr);
+ // current place is descendant of possible_ancestor
+ PlaceAncestryRelation::Descendant => {
+ ancestor_found = true;
+
+ // Truncate the descendant (current place) to be same as the ancestor to handle any
+ // possible change in capture mode.
+ truncate_place_to_len_and_update_capture_kind(
+ &mut place,
+ &mut updated_capture_info,
+ possible_ancestor.place.projections.len(),
+ );
+
+ let backup_path_sources = determine_capture_sources(
+ &mut updated_capture_info,
+ &mut possible_ancestor.info,
+ &mut dedup_sources_scratch,
+ );
+ determine_capture_info(
+ &mut possible_ancestor.info,
+ &mut updated_capture_info,
+ );
+
+ // we need to keep the ancestor's `sources`
+ possible_ancestor.info.sources = backup_path_sources;
+
+ // Only one related place will be in the list.
+ break;
}
- Statement::Item(_) => (),
+ _ => {}
}
}
- if let Some(tail) = tail {
- self.consume_expr(*tail);
- }
- }
- Expr::Call { callee, args } => {
- self.consume_expr(*callee);
- self.consume_exprs(args.iter().copied());
}
- Expr::MethodCall { receiver, args, .. } => {
- self.consume_expr(*receiver);
- self.consume_exprs(args.iter().copied());
+
+ // 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 captured_place =
+ CapturedPlace { place, info: updated_capture_info, mutability };
+ min_cap_list.push(captured_place);
}
- Expr::Match { expr, arms } => {
- for arm in arms.iter() {
- self.consume_expr(arm.expr);
- if let Some(guard) = arm.guard {
- self.consume_expr(guard);
- }
- }
- self.walk_expr(*expr);
- if let Some(discr_place) = self.place_of_expr(*expr)
- && self.is_upvar(&discr_place)
- {
- let mut capture_mode = None;
- for arm in arms.iter() {
- self.walk_pat(&mut capture_mode, arm.pat);
- }
- if let Some(c) = capture_mode {
- self.push_capture(discr_place, c);
+ }
+
+ debug!(
+ "For closure={:?}, min_captures before sorting={:?}",
+ closure_def_id, root_var_min_capture_list
+ );
+
+ // Now that we have the minimized list of captures, sort the captures by field id.
+ // This causes the closure to capture the upvars in the same order as the fields are
+ // declared which is also the drop order. Thus, in situations where we capture all the
+ // fields of some type, the observable drop order will remain the same as it previously
+ // was even though we're dropping each capture individually.
+ // See https://github.com/rust-lang/project-rfc-2229/issues/42 and
+ // `tests/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`.
+ for (_, captures) in &mut *root_var_min_capture_list {
+ captures.sort_by(|capture1, capture2| {
+ fn is_field(p: &&Projection) -> bool {
+ match p.kind {
+ ProjectionKind::Field { .. } => true,
+ ProjectionKind::Deref | ProjectionKind::UnwrapUnsafeBinder => false,
+ p @ (ProjectionKind::Subslice | ProjectionKind::Index) => {
+ panic!("ProjectionKind {:?} was unexpected", p)
+ }
}
}
- }
- Expr::Break { expr, label: _ }
- | Expr::Return { expr }
- | Expr::Yield { expr }
- | Expr::Yeet { expr } => {
- if let &Some(expr) = expr {
- self.consume_expr(expr);
- }
- }
- &Expr::Become { expr } => {
- self.consume_expr(expr);
- }
- Expr::RecordLit { fields, spread, .. } => {
- if let RecordSpread::Expr(expr) = *spread {
- self.consume_expr(expr);
- }
- self.consume_exprs(fields.iter().map(|it| it.expr));
- }
- Expr::Field { expr, name: _ } => self.select_from_expr(*expr),
- Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
- if self.result.method_resolution(tgt_expr).is_some() {
- // Overloaded deref.
- match self.expr_ty_after_adjustments(*expr).kind() {
- TyKind::Ref(_, _, mutability) => {
- let place = self.place_of_expr(*expr);
- match mutability {
- Mutability::Mut => self.mutate_expr(*expr, place),
- Mutability::Not => self.ref_expr(*expr, place),
+
+ // Need to sort only by Field projections, so filter away others.
+ // A previous implementation considered other projection types too
+ // but that caused ICE #118144
+ let capture1_field_projections = capture1.place.projections.iter().filter(is_field);
+ let capture2_field_projections = capture2.place.projections.iter().filter(is_field);
+
+ for (p1, p2) in capture1_field_projections.zip(capture2_field_projections) {
+ // We do not need to look at the `Projection.ty` fields here because at each
+ // step of the iteration, the projections will either be the same and therefore
+ // the types must be as well or the current projection will be different and
+ // we will return the result of comparing the field indexes.
+ match (p1.kind, p2.kind) {
+ (
+ ProjectionKind::Field { field_idx: i1, .. },
+ ProjectionKind::Field { field_idx: i2, .. },
+ ) => {
+ // Compare only if paths are different.
+ // Otherwise continue to the next iteration
+ if i1 != i2 {
+ return i1.cmp(&i2);
}
}
- // FIXME: Is this correct wrt. raw pointer derefs?
- TyKind::RawPtr(..) => self.select_from_expr(*expr),
- _ => never!("deref adjustments should include taking a mutable reference"),
+ // Given the filter above, this arm should never be hit
+ (l, r) => panic!("ProjectionKinds {:?} or {:?} were unexpected", l, r),
}
- } else {
- self.select_from_expr(*expr);
- }
- }
- Expr::Let { pat, expr } => {
- self.walk_expr(*expr);
- if let Some(place) = self.place_of_expr(*expr) {
- self.consume_with_pat(place, *pat);
- }
- }
- Expr::UnaryOp { expr, op: _ }
- | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
- | Expr::Await { expr }
- | Expr::Loop { body: expr, label: _ }
- | Expr::Box { expr }
- | Expr::Cast { expr, type_ref: _ } => {
- self.consume_expr(*expr);
- }
- Expr::Ref { expr, rawness: _, mutability } => {
- // We need to do this before we push the span so the order will be correct.
- let place = self.place_of_expr(*expr);
- self.current_capture_span_stack.push(MirSpan::ExprId(tgt_expr));
- match mutability {
- hir_def::type_ref::Mutability::Shared => self.ref_expr(*expr, place),
- hir_def::type_ref::Mutability::Mut => self.mutate_expr(*expr, place),
}
- }
- Expr::BinaryOp { lhs, rhs, op } => {
- let Some(op) = op else {
- return;
- };
- if matches!(op, BinaryOp::Assignment { .. }) {
- let place = self.place_of_expr(*lhs);
- self.mutate_expr(*lhs, place);
- self.consume_expr(*rhs);
- return;
- }
- self.consume_expr(*lhs);
- self.consume_expr(*rhs);
- }
- Expr::Range { lhs, rhs, range_type: _ } => {
- if let &Some(expr) = lhs {
- self.consume_expr(expr);
- }
- if let &Some(expr) = rhs {
- self.consume_expr(expr);
+
+ std::cmp::Ordering::Equal
+ });
+ }
+
+ debug!(
+ "For closure={:?}, min_captures after sorting={:#?}",
+ closure_def_id, root_var_min_capture_list
+ );
+ 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);
+
+ // 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 at = self.table.at(&cause);
+ match normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
+ at,
+ place.clone(),
+ vec![],
+ ) {
+ Ok((normalized, goals)) => {
+ if !goals.is_empty() {
+ // FIXME: Insert coroutine stalled predicates, this matters for MIR.
+ // let mut typeck_results = self.typeck_results.borrow_mut();
+ // typeck_results.coroutine_stalled_predicates.extend(
+ // goals
+ // .into_iter()
+ // // FIXME: throwing away the param-env :(
+ // .map(|goal| (goal.predicate, self.misc(span))),
+ // );
}
+ normalized
}
- Expr::Index { base, index } => {
- self.select_from_expr(*base);
- self.consume_expr(*index);
- }
- Expr::Closure { .. } => {
- let ty = self.expr_ty(tgt_expr);
- let TyKind::Closure(id, _) = ty.kind() else {
- never!("closure type is always closure");
- return;
- };
- let (captures, _) =
- self.result.closure_info.get(&id.0).expect(
- "We sort closures, so we should always have data for inner closures",
- );
- let mut cc = mem::take(&mut self.current_captures);
- cc.extend(captures.iter().filter(|it| self.is_upvar(&it.place)).map(|it| {
- CapturedItemWithoutTy {
- place: it.place.clone(),
- kind: it.kind,
- span_stacks: it.span_stacks.clone(),
- }
- }));
- self.current_captures = cc;
- }
- Expr::Array(Array::ElementList { elements: exprs }) | Expr::Tuple { exprs } => {
- self.consume_exprs(exprs.iter().copied())
- }
- &Expr::Assignment { target, value } => {
- self.walk_expr(value);
- let resolver_guard =
- self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
- match self.place_of_expr(value) {
- Some(rhs_place) => {
- self.inside_assignment = true;
- self.consume_with_pat(rhs_place, target);
- self.inside_assignment = false;
- }
- None => self.store.walk_pats(target, &mut |pat| match &self.store[pat] {
- Pat::Path(path) => self.mutate_path_pat(path, pat),
- &Pat::Expr(expr) => {
- let place = self.place_of_expr(expr);
- self.mutate_expr(expr, place);
- }
- _ => {}
- }),
+ Err(_errors) => {
+ place.base_ty = self.types.types.error.store();
+ for proj in &mut place.projections {
+ proj.ty = self.types.types.error.store();
}
- self.resolver.reset_to_guard(resolver_guard);
+ place
}
+ }
+ }
+
+ fn closure_min_captures_flattened(
+ &self,
+ closure_expr_id: ExprId,
+ ) -> impl Iterator<Item = &CapturedPlace> {
+ self.result
+ .closures_data
+ .get(&closure_expr_id)
+ .map(|closure_data| closure_data.min_captures.values().flatten())
+ .into_iter()
+ .flatten()
+ }
- Expr::Missing
- | Expr::Continue { .. }
- | Expr::Path(_)
- | Expr::Literal(_)
- | Expr::Const(_)
- | Expr::Underscore => (),
+ fn init_capture_kind_for_place(
+ &self,
+ place: &Place,
+ capture_clause: CaptureBy,
+ ) -> UpvarCapture {
+ match capture_clause {
+ // In case of a move closure if the data is accessed through a reference we
+ // want to capture by ref to allow precise capture using reborrows.
+ //
+ // If the data will be moved out of this place, then the place will be truncated
+ // at the first Deref in `adjust_for_move_closure` and then moved into the closure.
+ //
+ // For example:
+ //
+ // struct Buffer<'a> {
+ // x: &'a String,
+ // y: Vec<u8>,
+ // }
+ //
+ // fn get<'a>(b: Buffer<'a>) -> impl Sized + 'a {
+ // let c = move || b.x;
+ // drop(b);
+ // c
+ // }
+ //
+ // Even though the closure is declared as move, when we are capturing borrowed data (in
+ // this case, *b.x) we prefer to capture by reference.
+ // Otherwise you'd get an error in 2021 immediately because you'd be trying to take
+ // ownership of the (borrowed) String or else you'd take ownership of b, as in 2018 and
+ // before, which is also an error.
+ CaptureBy::Value if !place.deref_tys().any(Ty::is_ref) => UpvarCapture::ByValue,
+ CaptureBy::Value | CaptureBy::Ref => UpvarCapture::ByRef(BorrowKind::Immutable),
}
}
- fn walk_pat(&mut self, result: &mut Option<CaptureKind>, pat: PatId) {
- let mut update_result = |ck: CaptureKind| match result {
- Some(r) => {
- *r = cmp::max(*r, ck);
- }
- None => *result = Some(ck),
+ fn place_for_root_variable(&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 },
+ projections: Default::default(),
};
- self.walk_pat_inner(
- pat,
- &mut update_result,
- BorrowKind::Mut { kind: MutBorrowKind::Default },
- );
+ // Normalize eagerly when inserting into `capture_information`, so all downstream
+ // capture analysis can assume a normalized `Place`.
+ self.normalize_capture_place(place)
}
- fn walk_pat_inner(
- &mut self,
- p: PatId,
- update_result: &mut impl FnMut(CaptureKind),
- mut for_mut: BorrowKind,
- ) {
- match &self.store[p] {
- Pat::Ref { .. }
- | Pat::Box { .. }
- | Pat::Missing
- | Pat::Wild
- | Pat::Tuple { .. }
- | Pat::Expr(_)
- | Pat::Or(_) => (),
- Pat::TupleStruct { .. } | Pat::Record { .. } => {
- if let Some(variant) = self.result.variant_resolution_for_pat(p) {
- let adt = variant.adt_id(self.db);
- let is_multivariant = match adt {
- hir_def::AdtId::EnumId(e) => e.enum_variants(self.db).variants.len() != 1,
- _ => false,
- };
- if is_multivariant {
- update_result(CaptureKind::ByRef(BorrowKind::Shared));
- }
- }
- }
- Pat::Slice { .. }
- | Pat::ConstBlock(_)
- | Pat::Path(_)
- | Pat::Lit(_)
- | Pat::Range { .. } => {
- update_result(CaptureKind::ByRef(BorrowKind::Shared));
+ /// 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 {
+ let var_hir_id = match place.base {
+ PlaceBase::Upvar { var_id, .. } => var_id,
+ _ => unreachable!(),
+ };
+
+ let mut is_mutbl = if self.store[var_hir_id].mode == BindingAnnotation::Mutable {
+ Mutability::Mut
+ } else {
+ Mutability::Not
+ };
+
+ for pointer_ty in place.deref_tys() {
+ match self.table.structurally_resolve_type(pointer_ty).kind() {
+ // We don't capture derefs of raw ptrs
+ TyKind::RawPtr(_, _) => unreachable!(),
+
+ // Dereferencing a mut-ref allows us to mut the Place if we don't deref
+ // an immut-ref after on top of this.
+ TyKind::Ref(.., Mutability::Mut) => is_mutbl = Mutability::Mut,
+
+ // The place isn't mutable once we dereference an immutable reference.
+ TyKind::Ref(.., Mutability::Not) => return Mutability::Not,
+
+ // Dereferencing a box doesn't change mutability
+ TyKind::Adt(def, ..) if def.is_box() => {}
+
+ unexpected_ty => panic!("deref of unexpected pointer type {:?}", unexpected_ty),
}
- Pat::Bind { id, .. } => match self.result.binding_modes[p] {
- crate::BindingMode::Move => {
- if self.is_ty_copy(self.result.binding_ty(*id)) {
- update_result(CaptureKind::ByRef(BorrowKind::Shared));
- } else {
- update_result(CaptureKind::ByValue);
- }
+ }
+
+ is_mutbl
+ }
+}
+
+/// Determines whether a child capture that is derived from a parent capture
+/// should be borrowed with the lifetime of the parent coroutine-closure's env.
+///
+/// There are two cases when this needs to happen:
+///
+/// (1.) Are we borrowing data owned by the parent closure? We can determine if
+/// that is the case by checking if the parent capture is by move, EXCEPT if we
+/// apply a deref projection of an immutable reference, reborrows of immutable
+/// references which aren't restricted to the LUB of the lifetimes of the deref
+/// chain. This is why `&'short mut &'long T` can be reborrowed as `&'long T`.
+///
+/// ```rust
+/// let x = &1i32; // Let's call this lifetime `'1`.
+/// let c = async move || {
+/// println!("{:?}", *x);
+/// // Even though the inner coroutine borrows by ref, we're only capturing `*x`,
+/// // not `x`, so the inner closure is allowed to reborrow the data for `'1`.
+/// };
+/// ```
+///
+/// (2.) If a coroutine is mutably borrowing from a parent capture, then that
+/// mutable borrow cannot live for longer than either the parent *or* the borrow
+/// that we have on the original upvar. Therefore we always need to borrow the
+/// child capture with the lifetime of the parent coroutine-closure's env.
+///
+/// ```rust
+/// let mut x = 1i32;
+/// let c = async || {
+/// x = 1;
+/// // The parent borrows `x` for some `&'1 mut i32`.
+/// // However, when we call `c()`, we implicitly autoref for the signature of
+/// // `AsyncFnMut::async_call_mut`. Let's call that lifetime `'call`. Since
+/// // the maximum that `&'call mut &'1 mut i32` can be reborrowed is `&'call mut i32`,
+/// // the inner coroutine should capture w/ the lifetime of the coroutine-closure.
+/// };
+/// ```
+///
+/// If either of these cases apply, then we should capture the borrow with the
+/// lifetime of the parent coroutine-closure's env. Luckily, if this function is
+/// not correct, then the program is not unsound, since we still borrowck and validate
+/// the choices made from this function -- the only side-effect is that the user
+/// may receive unnecessary borrowck errors.
+fn should_reborrow_from_env_of_parent_coroutine_closure(
+ parent_capture: &CapturedPlace,
+ child_capture: &CapturedPlace,
+) -> bool {
+ // (1.)
+ (!parent_capture.is_by_ref()
+ // This is just inlined `place.deref_tys()` but truncated to just
+ // the child projections. Namely, look for a `&T` deref, since we
+ // can always extend `&'short mut &'long T` to `&'long T`.
+ && !child_capture
+ .place
+ .projections
+ .iter()
+ .enumerate()
+ .skip(parent_capture.place.projections.len())
+ .any(|(idx, proj)| {
+ matches!(proj.kind, ProjectionKind::Deref)
+ && matches!(
+ child_capture.place.ty_before_projection(idx).kind(),
+ TyKind::Ref(.., Mutability::Not)
+ )
+ }))
+ // (2.)
+ || matches!(child_capture.info.capture_kind, UpvarCapture::ByRef(BorrowKind::Mutable))
+}
+
+/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240,
+/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`.
+fn restrict_repr_packed_field_ref_capture(
+ mut place: Place,
+ capture_info: &mut CaptureInfo,
+) -> Place {
+ let pos = place.projections.iter().enumerate().position(|(i, p)| {
+ let ty = place.ty_before_projection(i);
+
+ // Return true for fields of packed structs.
+ match p.kind {
+ ProjectionKind::Field { .. } => match ty.kind() {
+ TyKind::Adt(def, _) if def.repr().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.
+ true
}
- crate::BindingMode::Ref(r) => match r {
- Mutability::Mut => update_result(CaptureKind::ByRef(for_mut)),
- Mutability::Not => update_result(CaptureKind::ByRef(BorrowKind::Shared)),
- },
+
+ _ => false,
},
+ _ => false,
}
- if self.result.pat_adjustments.get(&p).is_some_and(|it| !it.is_empty()) {
- for_mut = BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture };
- }
- self.store.walk_pats_shallow(p, |p| self.walk_pat_inner(p, update_result, for_mut));
+ });
+
+ if let Some(pos) = pos {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, pos);
}
- fn is_upvar(&self, place: &HirPlace) -> bool {
- if let Some(c) = self.current_closure {
- let InternedClosure(_, root) = self.db.lookup_intern_closure(c);
- return self.store.is_binding_upvar(place.local, root);
- }
- false
+ place
+}
+
+/// Returns a Ty that applies the specified capture kind on the provided capture Ty
+fn apply_capture_kind_on_capture_ty<'db>(
+ interner: DbInterner<'db>,
+ ty: Ty<'db>,
+ capture_kind: UpvarCapture,
+ region: Region<'db>,
+) -> Ty<'db> {
+ match capture_kind {
+ UpvarCapture::ByValue | UpvarCapture::ByUse => ty,
+ UpvarCapture::ByRef(kind) => Ty::new_ref(interner, region, ty, kind.to_mutbl_lossy()),
}
+}
- fn is_ty_copy(&mut self, ty: Ty<'db>) -> bool {
- if let TyKind::Closure(id, _) = ty.kind() {
- // FIXME: We handle closure as a special case, since chalk consider every closure as copy. We
- // should probably let chalk know which closures are copy, but I don't know how doing it
- // without creating query cycles.
- return self
- .result
- .closure_info
- .get(&id.0)
- .map(|it| it.1 == FnTrait::Fn)
- .unwrap_or(true);
- }
- let ty = self.table.resolve_completely(ty);
- self.table.type_is_copy_modulo_regions(ty)
+struct InferBorrowKind {
+ // The def-id of the closure whose kind and upvar accesses are being inferred.
+ closure_def_id: ExprId,
+
+ /// For each Place that is captured by the closure, we track the minimal kind of
+ /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
+ ///
+ /// Consider closure where s.str1 is captured via an ImmutableBorrow and
+ /// s.str2 via a MutableBorrow
+ ///
+ /// ```rust,no_run
+ /// struct SomeStruct { str1: String, str2: String };
+ ///
+ /// // Assume that the HirId for the variable definition is `V1`
+ /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") };
+ ///
+ /// let fix_s = |new_s2| {
+ /// // Assume that the HirId for the expression `s.str1` is `E1`
+ /// println!("Updating SomeStruct with str1={0}", s.str1);
+ /// // Assume that the HirId for the expression `*s.str2` is `E2`
+ /// s.str2 = new_s2;
+ /// };
+ /// ```
+ ///
+ /// For closure `fix_s`, (at a high level) the map contains
+ ///
+ /// ```ignore (illustrative)
+ /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
+ /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
+ /// ```
+ capture_information: InferredCaptureInformation,
+ fake_reads: Vec<(Place, FakeReadCause, SmallVec<[CaptureSourceStack; 2]>)>,
+}
+
+impl<'db> euv::Delegate<'db> for InferBorrowKind {
+ #[instrument(skip(self), level = "debug")]
+ fn fake_read(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ cause: FakeReadCause,
+ ctx: &mut InferenceContext<'_, 'db>,
+ ) {
+ let PlaceBase::Upvar { .. } = place_with_id.place.base else { return };
+
+ // We need to restrict Fake Read precision to avoid fake reading unsafe code,
+ // such as deref of a raw pointer.
+ let dummy_capture_kind = UpvarCapture::ByRef(BorrowKind::Immutable);
+ 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 = restrict_capture_precision(place, &mut dummy_capture_info);
+
+ dummy_capture_info.capture_kind = dummy_capture_kind;
+ let place = restrict_repr_packed_field_ref_capture(place, &mut dummy_capture_info);
+ self.fake_reads.push((place, cause, place_with_id.origins));
}
- fn select_from_expr(&mut self, expr: ExprId) {
- self.walk_expr(expr);
+ #[instrument(skip(self), level = "debug")]
+ fn consume(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ let PlaceBase::Upvar { closure: upvar_closure, .. } = place_with_id.place.base else {
+ return;
+ };
+ assert_eq!(self.closure_def_id, upvar_closure);
+
+ let place = ctx.normalize_capture_place(place_with_id.place.clone());
+
+ self.capture_information.push((
+ place,
+ CaptureInfo { sources: place_with_id.origins, capture_kind: UpvarCapture::ByValue },
+ ));
}
- fn restrict_precision_for_unsafe(&mut self) {
- // FIXME: Borrow checker problems without this.
- let mut current_captures = std::mem::take(&mut self.current_captures);
- for capture in &mut current_captures {
- let mut ty = self.table.resolve_completely(self.result.binding_ty(capture.place.local));
- if ty.is_raw_ptr() || ty.is_union() {
- capture.kind = CaptureKind::ByRef(BorrowKind::Shared);
- self.truncate_capture_spans(capture, 0);
- capture.place.projections.clear();
- continue;
- }
- for (i, p) in capture.place.projections.iter().enumerate() {
- ty = p.projected_ty(
- &self.table.infer_ctxt,
- self.table.param_env,
- ty,
- self.owner.krate(self.db),
- );
- if ty.is_raw_ptr() || ty.is_union() {
- capture.kind = CaptureKind::ByRef(BorrowKind::Shared);
- self.truncate_capture_spans(capture, i + 1);
- capture.place.projections.truncate(i + 1);
- break;
- }
- }
- }
- self.current_captures = current_captures;
+ #[instrument(skip(self), level = "debug")]
+ fn use_cloned(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ let PlaceBase::Upvar { closure: upvar_closure, .. } = place_with_id.place.base else {
+ return;
+ };
+ assert_eq!(self.closure_def_id, upvar_closure);
+
+ let place = ctx.normalize_capture_place(place_with_id.place.clone());
+
+ self.capture_information.push((
+ place,
+ CaptureInfo { sources: place_with_id.origins, capture_kind: UpvarCapture::ByUse },
+ ));
}
- fn adjust_for_move_closure(&mut self) {
- // FIXME: Borrow checker won't allow without this.
- let mut current_captures = std::mem::take(&mut self.current_captures);
- for capture in &mut current_captures {
- if let Some(first_deref) =
- capture.place.projections.iter().position(|proj| *proj == HirPlaceProjection::Deref)
- {
- self.truncate_capture_spans(capture, first_deref);
- capture.place.projections.truncate(first_deref);
- }
- capture.kind = CaptureKind::ByValue;
+ #[instrument(skip(self), level = "debug")]
+ fn borrow(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ bk: BorrowKind,
+ ctx: &mut InferenceContext<'_, 'db>,
+ ) {
+ let PlaceBase::Upvar { closure: upvar_closure, .. } = place_with_id.place.base else {
+ return;
+ };
+ assert_eq!(self.closure_def_id, upvar_closure);
+
+ // The region here will get discarded/ignored
+ let capture_kind = UpvarCapture::ByRef(bk);
+ 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());
+
+ // 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
+ // of in `restrict_capture_precision`.
+ let place = restrict_repr_packed_field_ref_capture(place, &mut capture_info);
+
+ // Raw pointers don't inherit mutability
+ if place.deref_tys().any(Ty::is_raw_ptr) {
+ capture_info.capture_kind = UpvarCapture::ByRef(BorrowKind::Immutable);
}
- self.current_captures = current_captures;
+
+ self.capture_information.push((place, capture_info));
}
- fn minimize_captures(&mut self) {
- self.current_captures.sort_unstable_by_key(|it| it.place.projections.len());
- let mut hash_map = FxHashMap::<HirPlace, usize>::default();
- let result = mem::take(&mut self.current_captures);
- for mut item in result {
- let mut lookup_place = HirPlace { local: item.place.local, projections: vec![] };
- let mut it = item.place.projections.iter();
- let prev_index = loop {
- if let Some(k) = hash_map.get(&lookup_place) {
- break Some(*k);
- }
- match it.next() {
- Some(it) => {
- lookup_place.projections.push(*it);
- }
- None => break None,
- }
- };
- match prev_index {
- Some(p) => {
- let prev_projections_len = self.current_captures[p].place.projections.len();
- self.truncate_capture_spans(&mut item, prev_projections_len);
- self.current_captures[p].span_stacks.extend(item.span_stacks);
- let len = self.current_captures[p].place.projections.len();
- let kind_after_truncate =
- item.place.capture_kind_of_truncated_place(item.kind, len);
- self.current_captures[p].kind =
- cmp::max(kind_after_truncate, self.current_captures[p].kind);
- }
- None => {
- hash_map.insert(item.place.clone(), self.current_captures.len());
- self.current_captures.push(item);
- }
- }
- }
+ #[instrument(skip(self), level = "debug")]
+ fn mutate(&mut self, assignee_place: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ self.borrow(assignee_place, BorrowKind::Mutable, ctx);
}
+}
- fn consume_with_pat(&mut self, mut place: HirPlace, tgt_pat: PatId) {
- let adjustments_count =
- self.result.pat_adjustments.get(&tgt_pat).map(|it| it.len()).unwrap_or_default();
- place.projections.extend((0..adjustments_count).map(|_| HirPlaceProjection::Deref));
- self.current_capture_span_stack
- .extend((0..adjustments_count).map(|_| MirSpan::PatId(tgt_pat)));
- 'reset_span_stack: {
- match &self.store[tgt_pat] {
- Pat::Missing | Pat::Wild => (),
- Pat::Tuple { args, ellipsis } => {
- let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
- let field_count = match self.result.pat_ty(tgt_pat).kind() {
- TyKind::Tuple(s) => s.len(),
- _ => break 'reset_span_stack,
- };
- let fields = 0..field_count;
- let it = al.iter().zip(fields.clone()).chain(ar.iter().rev().zip(fields.rev()));
- for (&arg, i) in it {
- let mut p = place.clone();
- self.current_capture_span_stack.push(MirSpan::PatId(arg));
- p.projections.push(HirPlaceProjection::TupleField(i as u32));
- self.consume_with_pat(p, arg);
- self.current_capture_span_stack.pop();
- }
- }
- Pat::Or(pats) => {
- for pat in pats.iter() {
- self.consume_with_pat(place.clone(), *pat);
- }
- }
- Pat::Record { args, .. } => {
- let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else {
- break 'reset_span_stack;
- };
- match variant {
- VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
- self.consume_place(place)
- }
- VariantId::StructId(s) => {
- let vd = s.fields(self.db);
- for field_pat in args.iter() {
- let arg = field_pat.pat;
- let Some(local_id) = vd.field(&field_pat.name) else {
- continue;
- };
- let mut p = place.clone();
- self.current_capture_span_stack.push(MirSpan::PatId(arg));
- p.projections.push(HirPlaceProjection::Field(FieldId {
- parent: variant,
- local_id,
- }));
- self.consume_with_pat(p, arg);
- self.current_capture_span_stack.pop();
- }
- }
- }
- }
- Pat::Range { .. } | Pat::Slice { .. } | Pat::ConstBlock(_) | Pat::Lit(_) => {
- self.consume_place(place)
- }
- Pat::Path(path) => {
- if self.inside_assignment {
- self.mutate_path_pat(path, tgt_pat);
- }
- self.consume_place(place);
- }
- &Pat::Bind { id, subpat: _ } => {
- let mode = self.result.binding_modes[tgt_pat];
- let capture_kind = match mode {
- BindingMode::Move => {
- self.consume_place(place);
- break 'reset_span_stack;
- }
- BindingMode::Ref(Mutability::Not) => BorrowKind::Shared,
- BindingMode::Ref(Mutability::Mut) => {
- BorrowKind::Mut { kind: MutBorrowKind::Default }
- }
- };
- self.current_capture_span_stack.push(MirSpan::BindingId(id));
- self.add_capture(place, CaptureKind::ByRef(capture_kind));
- self.current_capture_span_stack.pop();
- }
- Pat::TupleStruct { path: _, args, ellipsis } => {
- let Some(variant) = self.result.variant_resolution_for_pat(tgt_pat) else {
- break 'reset_span_stack;
- };
- match variant {
- VariantId::EnumVariantId(_) | VariantId::UnionId(_) => {
- self.consume_place(place)
- }
- VariantId::StructId(s) => {
- let vd = s.fields(self.db);
- let (al, ar) =
- args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
- let fields = vd.fields().iter();
- let it = al
- .iter()
- .zip(fields.clone())
- .chain(ar.iter().rev().zip(fields.rev()));
- for (&arg, (i, _)) in it {
- let mut p = place.clone();
- self.current_capture_span_stack.push(MirSpan::PatId(arg));
- p.projections.push(HirPlaceProjection::Field(FieldId {
- parent: variant,
- local_id: i,
- }));
- self.consume_with_pat(p, arg);
- self.current_capture_span_stack.pop();
- }
- }
- }
- }
- Pat::Ref { pat, mutability: _ } => {
- self.current_capture_span_stack.push(MirSpan::PatId(tgt_pat));
- place.projections.push(HirPlaceProjection::Deref);
- self.consume_with_pat(place, *pat);
- self.current_capture_span_stack.pop();
- }
- Pat::Box { .. } => (), // not supported
- &Pat::Expr(expr) => {
- self.consume_place(place);
- let pat_capture_span_stack = mem::take(&mut self.current_capture_span_stack);
- let old_inside_assignment = mem::replace(&mut self.inside_assignment, false);
- let lhs_place = self.place_of_expr(expr);
- self.mutate_expr(expr, lhs_place);
- self.inside_assignment = old_inside_assignment;
- self.current_capture_span_stack = pat_capture_span_stack;
+/// Rust doesn't permit moving fields out of a type that implements drop
+#[instrument(skip(fcx), ret, level = "debug")]
+fn restrict_precision_for_drop_types<'a, 'db>(
+ fcx: &mut InferenceContext<'a, 'db>,
+ mut place: Place,
+ capture_info: &mut CaptureInfo,
+) -> Place {
+ let is_copy_type = fcx.infcx().type_is_copy_modulo_regions(fcx.table.param_env, place.ty());
+
+ if let (false, UpvarCapture::ByValue) = (is_copy_type, capture_info.capture_kind) {
+ for i in 0..place.projections.len() {
+ match place.ty_before_projection(i).kind() {
+ TyKind::Adt(def, _) if def.destructor(fcx.interner()).is_some() => {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, i);
+ break;
}
+ _ => {}
}
}
- self.current_capture_span_stack
- .truncate(self.current_capture_span_stack.len() - adjustments_count);
}
- fn consume_exprs(&mut self, exprs: impl Iterator<Item = ExprId>) {
- for expr in exprs {
- self.consume_expr(expr);
- }
+ place
+}
+
+/// Truncate `place` so that an `unsafe` block isn't required to capture it.
+/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
+/// them completely.
+/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
+fn restrict_precision_for_unsafe(mut place: Place, capture_info: &mut CaptureInfo) -> Place {
+ if place.base_ty.as_ref().is_raw_ptr() {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, 0);
}
- fn closure_kind(&self) -> FnTrait {
- let mut r = FnTrait::Fn;
- for it in &self.current_captures {
- r = cmp::min(
- r,
- match &it.kind {
- CaptureKind::ByRef(BorrowKind::Mut { .. }) => FnTrait::FnMut,
- CaptureKind::ByRef(BorrowKind::Shallow | BorrowKind::Shared) => FnTrait::Fn,
- CaptureKind::ByValue => FnTrait::FnOnce,
- },
- )
- }
- r
+ if place.base_ty.as_ref().is_union() {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, 0);
}
- fn analyze_closure(&mut self, closure: InternedClosureId) -> FnTrait {
- let InternedClosure(_, root) = self.db.lookup_intern_closure(closure);
- self.current_closure = Some(closure);
- let Expr::Closure { body, capture_by, .. } = &self.store[root] else {
- unreachable!("Closure expression id is always closure");
- };
- self.consume_expr(*body);
- for item in &self.current_captures {
- if matches!(
- item.kind,
- CaptureKind::ByRef(BorrowKind::Mut {
- kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow
- })
- ) && !item.place.projections.contains(&HirPlaceProjection::Deref)
- {
- // FIXME: remove the `mutated_bindings_in_closure` completely and add proper fake reads in
- // MIR. I didn't do that due duplicate diagnostics.
- self.result.mutated_bindings_in_closure.insert(item.place.local);
- }
+ for (i, proj) in place.projections.iter().enumerate() {
+ if proj.ty.as_ref().is_raw_ptr() {
+ // Don't apply any projections on top of a raw ptr.
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, i + 1);
+ break;
}
- self.restrict_precision_for_unsafe();
- // `closure_kind` should be done before adjust_for_move_closure
- // If there exists pre-deduced kind of a closure, use it instead of one determined by capture, as rustc does.
- // rustc also does diagnostics here if the latter is not a subtype of the former.
- let closure_kind = self
- .result
- .closure_info
- .get(&closure)
- .map_or_else(|| self.closure_kind(), |info| info.1);
- match capture_by {
- CaptureBy::Value => self.adjust_for_move_closure(),
- CaptureBy::Ref => (),
+
+ if proj.ty.as_ref().is_union() {
+ // Don't capture precise fields of a union.
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, i + 1);
+ break;
}
- self.minimize_captures();
- self.strip_captures_ref_span();
- let result = mem::take(&mut self.current_captures);
- let captures = result.into_iter().map(|it| it.with_ty(self)).collect::<Vec<_>>();
- self.result.closure_info.insert(closure, (captures, closure_kind));
- closure_kind
}
- fn strip_captures_ref_span(&mut self) {
- // FIXME: Borrow checker won't allow without this.
- let mut captures = std::mem::take(&mut self.current_captures);
- for capture in &mut captures {
- if matches!(capture.kind, CaptureKind::ByValue) {
- for span_stack in &mut capture.span_stacks {
- if span_stack[span_stack.len() - 1].is_ref_span(self.store) {
- span_stack.truncate(span_stack.len() - 1);
- }
- }
+ place
+}
+
+/// Truncate projections so that the following rules are obeyed by the captured `place`:
+/// - No Index projections are captured, since arrays are captured completely.
+/// - No unsafe block is required to capture `place`.
+///
+/// Returns the truncated place and updated capture mode.
+#[instrument(ret, level = "debug")]
+fn restrict_capture_precision(place: Place, capture_info: &mut CaptureInfo) -> Place {
+ let mut place = restrict_precision_for_unsafe(place, capture_info);
+
+ if place.projections.is_empty() {
+ // Nothing to do here
+ return place;
+ }
+
+ for (i, proj) in place.projections.iter().enumerate() {
+ match proj.kind {
+ ProjectionKind::Index | ProjectionKind::Subslice => {
+ // Arrays are completely captured, so we drop Index and Subslice projections
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, i);
+ return place;
}
+ ProjectionKind::Deref => {}
+ ProjectionKind::Field { .. } => {}
+ ProjectionKind::UnwrapUnsafeBinder => {}
}
- self.current_captures = captures;
}
- pub(crate) fn infer_closures(&mut self) {
- let deferred_closures = self.sort_closures();
- for (closure, exprs) in deferred_closures.into_iter().rev() {
- self.current_captures = vec![];
- let kind = self.analyze_closure(closure);
-
- for (derefed_callee, callee_ty, params, expr) in exprs {
- if let &Expr::Call { callee, .. } = &self.store[expr] {
- let mut adjustments =
- self.result.expr_adjustments.remove(&callee).unwrap_or_default().into_vec();
- self.write_fn_trait_method_resolution(
- kind,
- derefed_callee,
- &mut adjustments,
- callee_ty,
- &params,
- expr,
- );
- self.result.expr_adjustments.insert(callee, adjustments.into_boxed_slice());
- }
+ place
+}
+
+/// Truncate deref of any reference.
+#[instrument(ret, level = "debug")]
+fn adjust_for_move_closure(mut place: Place, capture_info: &mut CaptureInfo) -> Place {
+ let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
+
+ if let Some(idx) = first_deref {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, idx);
+ }
+
+ capture_info.capture_kind = UpvarCapture::ByValue;
+ place
+}
+
+/// Adjust closure capture just that if taking ownership of data, only move data
+/// from enclosing stack frame.
+#[instrument(ret, level = "debug")]
+fn adjust_for_non_move_closure(mut place: Place, capture_info: &mut CaptureInfo) -> Place {
+ let contains_deref =
+ place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
+
+ match capture_info.capture_kind {
+ UpvarCapture::ByValue | UpvarCapture::ByUse => {
+ if let Some(idx) = contains_deref {
+ truncate_place_to_len_and_update_capture_kind(&mut place, capture_info, idx);
}
}
+
+ UpvarCapture::ByRef(..) => {}
}
- /// We want to analyze some closures before others, to have a correct analysis:
- /// * We should analyze nested closures before the parent, since the parent should capture some of
- /// the things that its children captures.
- /// * If a closure calls another closure, we need to analyze the callee, to find out how we should
- /// capture it (e.g. by move for FnOnce)
- ///
- /// These dependencies are collected in the main inference. We do a topological sort in this function. It
- /// will consume the `deferred_closures` field and return its content in a sorted vector.
- fn sort_closures(
- &mut self,
- ) -> Vec<(InternedClosureId, Vec<(Ty<'db>, Ty<'db>, Vec<Ty<'db>>, ExprId)>)> {
- let mut deferred_closures = mem::take(&mut self.deferred_closures);
- let mut dependents_count: FxHashMap<InternedClosureId, usize> =
- deferred_closures.keys().map(|it| (*it, 0)).collect();
- for deps in self.closure_dependencies.values() {
- for dep in deps {
- *dependents_count.entry(*dep).or_default() += 1;
- }
+ place
+}
+
+/// At the end, `capture_info_a` will contain the selected info.
+fn determine_capture_info(capture_info_a: &mut CaptureInfo, capture_info_b: &mut CaptureInfo) {
+ // If the capture kind is equivalent then, we don't need to escalate and can compare the
+ // expressions.
+ let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
+ (UpvarCapture::ByValue, UpvarCapture::ByValue) => true,
+ (UpvarCapture::ByUse, UpvarCapture::ByUse) => true,
+ (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => ref_a == ref_b,
+ (UpvarCapture::ByValue, _) | (UpvarCapture::ByUse, _) | (UpvarCapture::ByRef(_), _) => {
+ false
}
- let mut queue: Vec<_> =
- deferred_closures.keys().copied().filter(|&it| dependents_count[&it] == 0).collect();
- let mut result = vec![];
- while let Some(it) = queue.pop() {
- if let Some(d) = deferred_closures.remove(&it) {
- result.push((it, d));
+ };
+
+ let swap = if eq_capture_kind {
+ false
+ } else {
+ // We select the CaptureKind which ranks higher based the following priority order:
+ // (ByUse | ByValue) > MutBorrow > UniqueImmBorrow > ImmBorrow
+ match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
+ (UpvarCapture::ByUse, UpvarCapture::ByValue)
+ | (UpvarCapture::ByValue, UpvarCapture::ByUse) => {
+ panic!("Same capture can't be ByUse and ByValue at the same time")
}
- for &dep in self.closure_dependencies.get(&it).into_iter().flat_map(|it| it.iter()) {
- let cnt = dependents_count.get_mut(&dep).unwrap();
- *cnt -= 1;
- if *cnt == 0 {
- queue.push(dep);
+ (UpvarCapture::ByValue, UpvarCapture::ByValue)
+ | (UpvarCapture::ByUse, UpvarCapture::ByUse)
+ | (UpvarCapture::ByValue | UpvarCapture::ByUse, UpvarCapture::ByRef(_)) => false,
+ (UpvarCapture::ByRef(_), UpvarCapture::ByValue | UpvarCapture::ByUse) => true,
+ (UpvarCapture::ByRef(ref_a), UpvarCapture::ByRef(ref_b)) => {
+ match (ref_a, ref_b) {
+ // Take LHS:
+ (BorrowKind::UniqueImmutable | BorrowKind::Mutable, BorrowKind::Immutable)
+ | (BorrowKind::Mutable, BorrowKind::UniqueImmutable) => false,
+
+ // Take RHS:
+ (BorrowKind::Immutable, BorrowKind::UniqueImmutable | BorrowKind::Mutable)
+ | (BorrowKind::UniqueImmutable, BorrowKind::Mutable) => true,
+
+ (BorrowKind::Immutable, BorrowKind::Immutable)
+ | (BorrowKind::UniqueImmutable, BorrowKind::UniqueImmutable)
+ | (BorrowKind::Mutable, BorrowKind::Mutable) => {
+ panic!("Expected unequal capture kinds");
+ }
}
}
}
- assert!(deferred_closures.is_empty(), "we should have analyzed all closures");
- result
+ };
+
+ if swap {
+ mem::swap(capture_info_a, capture_info_b);
}
+}
- pub(crate) fn add_current_closure_dependency(&mut self, dep: InternedClosureId) {
- if let Some(c) = self.current_closure
- && !dep_creates_cycle(&self.closure_dependencies, &mut FxHashSet::default(), c, dep)
- {
- self.closure_dependencies.entry(c).or_default().push(dep);
- }
+fn determine_capture_sources(
+ capture_info_a: &mut CaptureInfo,
+ capture_info_b: &mut CaptureInfo,
+ dedup_sources_scratch: &mut FxHashMap<ExprOrPatId, CaptureSourceStack>,
+) -> SmallVec<[CaptureSourceStack; 2]> {
+ dedup_sources_scratch.clear();
+ dedup_sources_scratch.extend(
+ mem::take(&mut capture_info_a.sources).into_iter().map(|it| (it.final_source(), it)),
+ );
+ dedup_sources_scratch.extend(
+ mem::take(&mut capture_info_b.sources).into_iter().map(|it| (it.final_source(), it)),
+ );
- fn dep_creates_cycle(
- closure_dependencies: &FxHashMap<InternedClosureId, Vec<InternedClosureId>>,
- visited: &mut FxHashSet<InternedClosureId>,
- from: InternedClosureId,
- to: InternedClosureId,
- ) -> bool {
- if !visited.insert(from) {
- return false;
- }
+ let mut result = mem::take(&mut capture_info_a.sources);
+ result.clear();
+ result.extend(dedup_sources_scratch.values().cloned());
+ result
+}
- if from == to {
- return true;
- }
+/// Truncates `place` to have up to `len` projections.
+/// `curr_mode` is the current required capture kind for the place.
+/// Returns the truncated `place` and the updated required capture kind.
+///
+/// Note: Capture kind changes from `MutBorrow` to `UniqueImmBorrow` if the truncated part of the `place`
+/// contained `Deref` of `&mut`.
+fn truncate_place_to_len_and_update_capture_kind(
+ place: &mut Place,
+ info: &mut CaptureInfo,
+ len: usize,
+) {
+ let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), TyKind::Ref(.., Mutability::Mut));
- if let Some(deps) = closure_dependencies.get(&to) {
- for dep in deps {
- if dep_creates_cycle(closure_dependencies, visited, from, *dep) {
- return true;
- }
+ // If the truncated part of the place contains `Deref` of a `&mut` then convert MutBorrow ->
+ // UniqueImmBorrow
+ // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so
+ // we don't need to worry about that case here.
+ match info.capture_kind {
+ UpvarCapture::ByRef(BorrowKind::Mutable) => {
+ for i in len..place.projections.len() {
+ if place.projections[i].kind == ProjectionKind::Deref
+ && is_mut_ref(place.ty_before_projection(i))
+ {
+ info.capture_kind = UpvarCapture::ByRef(BorrowKind::UniqueImmutable);
+ break;
}
}
+ }
- false
+ UpvarCapture::ByRef(..) => {}
+ UpvarCapture::ByValue | UpvarCapture::ByUse => {}
+ }
+
+ // Now fix the sources, to point at the smaller place.
+ for source in &mut info.sources {
+ // +1 because the first place is the base.
+ source.truncate(len + 1);
+ }
+
+ place.projections.truncate(len);
+}
+
+/// Determines the Ancestry relationship of Place A relative to Place B
+///
+/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B
+/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B
+/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other.
+fn determine_place_ancestry_relation(place_a: &Place, place_b: &Place) -> PlaceAncestryRelation {
+ // If Place A and Place B don't start off from the same root variable, they are divergent.
+ if place_a.base != place_b.base {
+ return PlaceAncestryRelation::Divergent;
+ }
+
+ // Assume of length of projections_a = n
+ let projections_a = &place_a.projections;
+
+ // Assume of length of projections_b = m
+ let projections_b = &place_b.projections;
+
+ let same_initial_projections =
+ iter::zip(projections_a, projections_b).all(|(proj_a, proj_b)| proj_a.kind == proj_b.kind);
+
+ if same_initial_projections {
+ use std::cmp::Ordering;
+
+ // First min(n, m) projections are the same
+ // Select Ancestor/Descendant
+ match projections_b.len().cmp(&projections_a.len()) {
+ Ordering::Greater => PlaceAncestryRelation::Ancestor,
+ Ordering::Equal => PlaceAncestryRelation::SamePlace,
+ Ordering::Less => PlaceAncestryRelation::Descendant,
}
+ } else {
+ PlaceAncestryRelation::Divergent
}
}
-/// Call this only when the last span in the stack isn't a split.
-fn apply_adjusts_to_place(
- current_capture_span_stack: &mut Vec<MirSpan>,
- mut r: HirPlace,
- adjustments: &[Adjustment],
-) -> Option<HirPlace> {
- let span = *current_capture_span_stack.last().expect("empty capture span stack");
- for adj in adjustments {
- match &adj.kind {
- Adjust::Deref(None) => {
- current_capture_span_stack.push(span);
- r.projections.push(HirPlaceProjection::Deref);
- }
- _ => return None,
+/// Reduces the precision of the captured place when the precision doesn't yield any benefit from
+/// borrow checking perspective, allowing us to save us on the size of the capture.
+///
+///
+/// Fields that are read through a shared reference will always be read via a shared ref or a copy,
+/// and therefore capturing precise paths yields no benefit. This optimization truncates the
+/// rightmost deref of the capture if the deref is applied to a shared ref.
+///
+/// Reason we only drop the last deref is because of the following edge case:
+///
+/// ```
+/// # struct A { field_of_a: Box<i32> }
+/// # struct B {}
+/// # struct C<'a>(&'a i32);
+/// struct MyStruct<'a> {
+/// a: &'static A,
+/// b: B,
+/// c: C<'a>,
+/// }
+///
+/// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
+/// || drop(&*m.a.field_of_a)
+/// // Here we really do want to capture `*m.a` because that outlives `'static`
+///
+/// // If we capture `m`, then the closure no longer outlives `'static`
+/// // it is constrained to `'a`
+/// }
+/// ```
+#[instrument(ret, level = "debug")]
+fn truncate_capture_for_optimization(mut place: Place, info: &mut CaptureInfo) -> Place {
+ let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), TyKind::Ref(.., Mutability::Not));
+
+ // Find the rightmost deref (if any). All the projections that come after this
+ // are fields or other "in-place pointer adjustments"; these refer therefore to
+ // data owned by whatever pointer is being dereferenced here.
+ let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind);
+
+ match idx {
+ // If that pointer is a shared reference, then we don't need those fields.
+ Some(idx) if is_shared_ref(place.ty_before_projection(idx)) => {
+ truncate_place_to_len_and_update_capture_kind(&mut place, info, idx + 1)
}
+ None | Some(_) => {}
}
- Some(r)
+
+ place
+}
+
+/// Precise capture is enabled if user is using Rust Edition 2021 or higher.
+/// `span` is the span of the closure.
+fn enable_precise_capture(edition: Edition) -> bool {
+ // FIXME: We should use the edition from the closure expr.
+ edition.at_least_2021()
+}
+
+fn analyze_coroutine_closure_captures<'a, T>(
+ parent_captures: impl IntoIterator<Item = &'a CapturedPlace>,
+ child_captures: impl IntoIterator<Item = &'a CapturedPlace>,
+ mut for_each: impl FnMut((usize, &'a CapturedPlace), (usize, &'a CapturedPlace)) -> T,
+) -> impl Iterator<Item = T> {
+ let mut result = SmallVec::<[_; 10]>::new();
+
+ let mut child_captures = child_captures.into_iter().enumerate().peekable();
+
+ // One parent capture may correspond to several child captures if we end up
+ // refining the set of captures via edition-2021 precise captures. We want to
+ // match up any number of child captures with one parent capture, so we keep
+ // peeking off this `Peekable` until the child doesn't match anymore.
+ for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
+ // Make sure we use every field at least once, b/c why are we capturing something
+ // if it's not used in the inner coroutine.
+ let mut field_used_at_least_once = false;
+
+ // A parent matches a child if they share the same prefix of projections.
+ // The child may have more, if it is capturing sub-fields out of
+ // something that is captured by-move in the parent closure.
+ while child_captures.peek().is_some_and(|(_, child_capture)| {
+ child_prefix_matches_parent_projections(parent_capture, child_capture)
+ }) {
+ let (child_field_idx, child_capture) = child_captures.next().unwrap();
+ // This analysis only makes sense if the parent capture is a
+ // prefix of the child capture.
+ assert!(
+ child_capture.place.projections.len() >= parent_capture.place.projections.len(),
+ "parent capture ({parent_capture:#?}) expected to be prefix of \
+ child capture ({child_capture:#?})"
+ );
+
+ result.push(for_each(
+ (parent_field_idx, parent_capture),
+ (child_field_idx, child_capture),
+ ));
+
+ field_used_at_least_once = true;
+ }
+
+ // Make sure the field was used at least once.
+ assert!(
+ field_used_at_least_once,
+ "we captured {parent_capture:#?} but it was not used in the child coroutine?"
+ );
+ }
+ assert_eq!(child_captures.next(), None, "leftover child captures?");
+
+ result.into_iter()
+}
+
+fn child_prefix_matches_parent_projections(
+ parent_capture: &CapturedPlace,
+ child_capture: &CapturedPlace,
+) -> bool {
+ let PlaceBase::Upvar { var_id: parent_base, .. } = parent_capture.place.base else {
+ panic!("expected capture to be an upvar");
+ };
+ let PlaceBase::Upvar { var_id: child_base, .. } = child_capture.place.base else {
+ panic!("expected capture to be an upvar");
+ };
+
+ parent_base == child_base
+ && std::iter::zip(&child_capture.place.projections, &parent_capture.place.projections)
+ .all(|(child, parent)| child.kind == parent.kind)
}
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
new file mode 100644
index 0000000000..099fa18168
--- /dev/null
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -0,0 +1,1705 @@
+//! A different sort of visitor for walking fn bodies. Unlike the
+//! normal visitor, which just walks the entire body in one shot, the
+//! `ExprUseVisitor` determines how expressions are being used.
+//!
+//! This is only used for upvar inference.
+
+use either::Either;
+use hir_def::{
+ AdtId, HasModule, VariantId,
+ attrs::AttrFlags,
+ hir::{
+ Array, AsmOperand, BindingId, Expr, ExprId, ExprOrPatId, MatchArm, Pat, PatId,
+ RecordLitField, RecordSpread, Statement,
+ },
+ 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 smallvec::{SmallVec, smallvec};
+use syntax::ast::{BinaryOp, UnaryOp};
+use tracing::{debug, instrument};
+
+use crate::{
+ Adjust, Adjustment, AutoBorrow, BindingMode,
+ infer::{CaptureSourceStack, InferenceContext, UpvarCapture, closure::analysis::BorrowKind},
+ method_resolution::CandidateId,
+ next_solver::{DbInterner, ErrorGuaranteed, StoredTy, Ty, TyKind},
+ upvars::UpvarsRef,
+ utils::EnumerateAndAdjustIterator,
+};
+
+type Result<T = (), E = ErrorGuaranteed> = std::result::Result<T, E>;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum ProjectionKind {
+ /// A dereference of a pointer, reference or `Box<T>` of the given type.
+ Deref,
+
+ /// `B.F` where `B` is the base expression and `F` is
+ /// the field. The field is identified by which variant
+ /// it appears in along with a field index. The variant
+ /// is used for enums.
+ Field { field_idx: u32, variant_idx: u32 },
+
+ /// Some index like `B[x]`, where `B` is the base
+ /// expression. We don't preserve the index `x` because
+ /// we won't need it.
+ Index,
+
+ /// A subslice covering a range of values like `B[x..y]`.
+ Subslice,
+
+ /// `unwrap_binder!(expr)`
+ UnwrapUnsafeBinder,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum PlaceBase {
+ /// A temporary variable.
+ Rvalue,
+ /// A named `static` item.
+ StaticItem,
+ /// A named local variable.
+ Local(BindingId),
+ /// An upvar referenced by closure env.
+ Upvar { closure: ExprId, var_id: BindingId },
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct Projection {
+ /// Type after the projection is applied.
+ pub ty: StoredTy,
+
+ /// Defines the kind of access made by the projection.
+ pub kind: ProjectionKind,
+}
+
+/// A `Place` represents how a value is located in memory. This does not
+/// 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)]
+pub struct Place {
+ /// The type of the `PlaceBase`
+ pub base_ty: StoredTy,
+ /// The "outermost" place that holds this value.
+ 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`.
+ ///
+ /// The types are in the reverse order that they are applied. So if
+ /// `x: &*const u32` and the `Place` is `**x`, then the types returned are
+ ///`*const u32` then `&*const u32`.
+ pub fn deref_tys<'db>(&self) -> impl Iterator<Item = Ty<'db>> {
+ self.projections.iter().enumerate().rev().filter_map(move |(index, proj)| {
+ if ProjectionKind::Deref == proj.kind {
+ Some(self.ty_before_projection(index))
+ } else {
+ None
+ }
+ })
+ }
+
+ /// Returns the type of this `Place` after all projections have been applied.
+ pub fn ty<'db>(&self) -> Ty<'db> {
+ self.projections.last().map_or(self.base_ty.as_ref(), |proj| proj.ty.as_ref())
+ }
+
+ /// Returns the type of this `Place` immediately before `projection_index`th projection
+ /// is applied.
+ pub fn ty_before_projection<'db>(&self, projection_index: usize) -> Ty<'db> {
+ assert!(projection_index < self.projections.len());
+ if projection_index == 0 {
+ self.base_ty.as_ref()
+ } else {
+ self.projections[projection_index - 1].ty.as_ref()
+ }
+ }
+}
+
+/// A `PlaceWithOrigin` represents how a value is located in memory. This does not
+/// 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)]
+pub(crate) struct PlaceWithOrigin {
+ /// `ExprId`s or `PatId`s of the expressions or patterns producing this value.
+ pub origins: SmallVec<[CaptureSourceStack; 2]>,
+
+ /// Information about the `Place`.
+ pub place: Place,
+}
+
+impl PlaceWithOrigin {
+ fn new_no_projections<'db>(
+ origin: impl Into<ExprOrPatId>,
+ base_ty: Ty<'db>,
+ base: PlaceBase,
+ ) -> PlaceWithOrigin {
+ Self::new(
+ smallvec![CaptureSourceStack::from_single(origin.into())],
+ base_ty,
+ base,
+ Vec::new(),
+ )
+ }
+
+ fn new<'db>(
+ origins: SmallVec<[CaptureSourceStack; 2]>,
+ base_ty: Ty<'db>,
+ base: PlaceBase,
+ projections: Vec<Projection>,
+ ) -> PlaceWithOrigin {
+ debug_assert!(origins.iter().all(|origin| origin.len() == projections.len() + 1));
+ PlaceWithOrigin { origins, place: Place { base_ty: base_ty.store(), base, projections } }
+ }
+
+ fn push_projection(&mut self, projection: Projection, origin: ExprOrPatId) {
+ self.place.projections.push(projection);
+ for origin_stack in &mut self.origins {
+ origin_stack.push(origin);
+ }
+ }
+}
+
+/// The `FakeReadCause` describes the type of pattern why a FakeRead statement exists.
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub enum FakeReadCause {
+ /// A fake read injected into a match guard to ensure that the discriminants
+ /// that are being matched on aren't modified while the match guard is being
+ /// evaluated.
+ ///
+ /// At the beginning of each match guard, a fake borrow is
+ /// inserted for each discriminant accessed in the entire `match` statement.
+ ///
+ /// Then, at the end of the match guard, a `FakeRead(ForMatchGuard)` is
+ /// inserted to keep the fake borrows alive until that point.
+ ///
+ /// This should ensure that you cannot change the variant for an enum while
+ /// you are in the midst of matching on it.
+ ForMatchGuard,
+
+ /// Fake read of the scrutinee of a `match` or destructuring `let`
+ /// (i.e. `let` with non-trivial pattern).
+ ///
+ /// In `match x { ... }`, we generate a `FakeRead(ForMatchedPlace, x)`
+ /// and insert it into the `otherwise_block` (which is supposed to be
+ /// unreachable for irrefutable pattern-matches like `match` or `let`).
+ ///
+ /// This is necessary because `let x: !; match x {}` doesn't generate any
+ /// actual read of x, so we need to generate a `FakeRead` to check that it
+ /// is initialized.
+ ///
+ /// If the `FakeRead(ForMatchedPlace)` is being performed with a closure
+ /// that doesn't capture the required upvars, the `FakeRead` within the
+ /// closure is omitted entirely.
+ ///
+ /// To make sure that this is still sound, if a closure matches against
+ /// a Place starting with an Upvar, we hoist the `FakeRead` to the
+ /// definition point of the closure.
+ ///
+ /// If the `FakeRead` comes from being hoisted out of a closure like this,
+ /// we record the `ExprId` of the closure. Otherwise, the `Option` will be `None`.
+ //
+ // We can use LocalDefId here since fake read statements are removed
+ // before codegen in the `CleanupNonCodegenStatements` pass.
+ ForMatchedPlace(Option<ExprId>),
+
+ /// A fake read injected into a match guard to ensure that the places
+ /// bound by the pattern are immutable for the duration of the match guard.
+ ///
+ /// Within a match guard, references are created for each place that the
+ /// pattern creates a binding for — this is known as the `RefWithinGuard`
+ /// version of the variables. To make sure that the references stay
+ /// alive until the end of the match guard, and properly prevent the
+ /// places in question from being modified, a `FakeRead(ForGuardBinding)`
+ /// is inserted at the end of the match guard.
+ ///
+ /// For details on how these references are created, see the extensive
+ /// documentation on `bind_matched_candidate_for_guard` in
+ /// `rustc_mir_build`.
+ ForGuardBinding,
+
+ /// Officially, the semantics of
+ ///
+ /// `let pattern = <expr>;`
+ ///
+ /// is that `<expr>` is evaluated into a temporary and then this temporary is
+ /// into the pattern.
+ ///
+ /// However, if we see the simple pattern `let var = <expr>`, we optimize this to
+ /// evaluate `<expr>` directly into the variable `var`. This is mostly unobservable,
+ /// but in some cases it can affect the borrow checker, as in #53695.
+ ///
+ /// Therefore, we insert a `FakeRead(ForLet)` immediately after each `let`
+ /// with a trivial pattern.
+ ///
+ /// FIXME: `ExprUseVisitor` has an entirely different opinion on what `FakeRead(ForLet)`
+ /// is supposed to mean. If it was accurate to what MIR lowering does,
+ /// would it even make sense to hoist these out of closures like
+ /// `ForMatchedPlace`?
+ ForLet(Option<ExprId>),
+
+ /// Currently, index expressions overloaded through the `Index` trait
+ /// get lowered differently than index expressions with builtin semantics
+ /// for arrays and slices — the latter will emit code to perform
+ /// bound checks, and then return a MIR place that will only perform the
+ /// indexing "for real" when it gets incorporated into an instruction.
+ ///
+ /// This is observable in the fact that the following compiles:
+ ///
+ /// ```
+ /// fn f(x: &mut [&mut [u32]], i: usize) {
+ /// x[i][x[i].len() - 1] += 1;
+ /// }
+ /// ```
+ ///
+ /// However, we need to be careful to not let the user invalidate the
+ /// bound check with an expression like
+ ///
+ /// `(*x)[1][{ x = y; 4}]`
+ ///
+ /// Here, the first bounds check would be invalidated when we evaluate the
+ /// second index expression. To make sure that this doesn't happen, we
+ /// create a fake borrow of `x` and hold it while we evaluate the second
+ /// index.
+ ///
+ /// This borrow is kept alive by a `FakeRead(ForIndex)` at the end of its
+ /// scope.
+ ForIndex,
+}
+
+/// This trait defines the callbacks you can expect to receive when
+/// employing the ExprUseVisitor.
+pub(crate) trait Delegate<'db> {
+ /// The value found at `place` is moved, depending
+ /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
+ ///
+ /// If the value is `Copy`, [`copy`][Self::copy] is called instead, which
+ /// by default falls back to [`borrow`][Self::borrow].
+ ///
+ /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
+ /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
+ /// id will be the id of the expression `expr` but the place itself will have
+ /// the id of the binding in the pattern `pat`.
+ fn consume(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>);
+
+ /// The value found at `place` is used, depending
+ /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`.
+ ///
+ /// Use of a `Copy` type in a ByUse context is considered a use
+ /// by `ImmBorrow` and `borrow` is called instead. This is because
+ /// a shared borrow is the "minimum access" that would be needed
+ /// to perform a copy.
+ ///
+ ///
+ /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for
+ /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic
+ /// id will be the id of the expression `expr` but the place itself will have
+ /// the id of the binding in the pattern `pat`.
+ fn use_cloned(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>);
+
+ /// The value found at `place` is being borrowed with kind `bk`.
+ /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
+ fn borrow(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ bk: BorrowKind,
+ ctx: &mut InferenceContext<'_, 'db>,
+ );
+
+ /// The value found at `place` is being copied.
+ /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
+ ///
+ /// If an implementation is not provided, use of a `Copy` type in a ByValue context is instead
+ /// considered a use by `ImmBorrow` and `borrow` is called instead. This is because a shared
+ /// borrow is the "minimum access" that would be needed to perform a copy.
+ fn copy(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default
+ // we treat a copy of `x` as a borrow of `x`.
+ self.borrow(place_with_id, BorrowKind::Immutable, ctx)
+ }
+
+ /// The path at `assignee_place` is being assigned to.
+ /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details).
+ fn mutate(&mut self, assignee_place: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>);
+
+ /// The path at `binding_place` is a binding that is being initialized.
+ ///
+ /// This covers cases such as `let x = 42;`
+ fn bind(&mut self, binding_place: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ // Bindings can normally be treated as a regular assignment, so by default we
+ // forward this to the mutate callback.
+ self.mutate(binding_place, ctx)
+ }
+
+ /// The `place` should be a fake read because of specified `cause`.
+ fn fake_read(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ cause: FakeReadCause,
+ ctx: &mut InferenceContext<'_, 'db>,
+ );
+}
+
+impl<'db, D: Delegate<'db>> Delegate<'db> for &mut D {
+ fn consume(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ (**self).consume(place_with_id, ctx)
+ }
+
+ fn use_cloned(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ (**self).use_cloned(place_with_id, ctx)
+ }
+
+ fn borrow(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ bk: BorrowKind,
+ ctx: &mut InferenceContext<'_, 'db>,
+ ) {
+ (**self).borrow(place_with_id, bk, ctx)
+ }
+
+ fn copy(&mut self, place_with_id: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ (**self).copy(place_with_id, ctx)
+ }
+
+ fn mutate(&mut self, assignee_place: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ (**self).mutate(assignee_place, ctx)
+ }
+
+ fn bind(&mut self, binding_place: PlaceWithOrigin, ctx: &mut InferenceContext<'_, 'db>) {
+ (**self).bind(binding_place, ctx)
+ }
+
+ fn fake_read(
+ &mut self,
+ place_with_id: PlaceWithOrigin,
+ cause: FakeReadCause,
+ ctx: &mut InferenceContext<'_, 'db>,
+ ) {
+ (**self).fake_read(place_with_id, cause, ctx)
+ }
+}
+
+/// A visitor that reports how each expression is being used.
+///
+/// See [module-level docs][self] and [`Delegate`] for details.
+pub(crate) struct ExprUseVisitor<'a, 'b, 'db, D: Delegate<'db>> {
+ cx: &'a mut InferenceContext<'b, 'db>,
+ delegate: D,
+ closure_expr: ExprId,
+ 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:
+ ///
+ /// - `delegate` -- who receives the callbacks
+ /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`)
+ /// - `typeck_results` --- typeck results for the code being analyzed
+ pub(crate) fn new(
+ cx: &'a mut InferenceContext<'b, 'db>,
+ closure_expr: ExprId,
+ upvars: UpvarsRef<'db>,
+ delegate: D,
+ ) -> Self {
+ ExprUseVisitor { delegate, closure_expr, upvars, cx }
+ }
+
+ pub(crate) fn consume_closure_body(&mut self, params: &[PatId], body: ExprId) -> Result {
+ for &param in params {
+ let param_ty = self.pat_ty_adjusted(param)?;
+ debug!("consume_body: param_ty = {:?}", param_ty);
+
+ 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.consume_expr(body)?;
+
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn consume_or_copy(&mut self, place_with_id: PlaceWithOrigin) {
+ if self.cx.table.type_is_copy_modulo_regions(place_with_id.place.ty()) {
+ self.delegate.copy(place_with_id, self.cx);
+ } else {
+ self.delegate.consume(place_with_id, self.cx);
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(crate) fn consume_clone_or_copy(&mut self, place_with_id: PlaceWithOrigin) {
+ // `x.use` will do one of the following
+ // * if it implements `Copy`, it will be a copy
+ // * if it implements `UseCloned`, it will be a call to `clone`
+ // * otherwise, it is a move
+ //
+ // we do a conservative approximation of this, treating it as a move unless we know that it implements copy or `UseCloned`
+ if self.cx.table.type_is_copy_modulo_regions(place_with_id.place.ty()) {
+ self.delegate.copy(place_with_id, self.cx);
+ } else if self.cx.table.type_is_use_cloned_modulo_regions(place_with_id.place.ty()) {
+ self.delegate.use_cloned(place_with_id, self.cx);
+ } else {
+ self.delegate.consume(place_with_id, self.cx);
+ }
+ }
+
+ fn consume_exprs(&mut self, exprs: &[ExprId]) -> Result {
+ for &expr in exprs {
+ self.consume_expr(expr)?;
+ }
+ 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)?;
+ self.consume_or_copy(place_with_id);
+ self.walk_expr(expr)?;
+ Ok(())
+ }
+
+ fn mutate_expr(&mut self, expr: ExprId) -> Result {
+ let place_with_id = self.cat_expr(expr)?;
+ self.delegate.mutate(place_with_id, self.cx);
+ self.walk_expr(expr)?;
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn borrow_expr(&mut self, expr: ExprId, bk: BorrowKind) -> Result {
+ let place_with_id = self.cat_expr(expr)?;
+ self.delegate.borrow(place_with_id, bk, self.cx);
+ self.walk_expr(expr)?;
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ pub(crate) fn walk_expr(&mut self, expr: ExprId) -> Result {
+ self.walk_adjustment(expr)?;
+
+ match self.cx.store[expr] {
+ Expr::Path(_) => {}
+
+ Expr::UnaryOp { op: UnaryOp::Deref, expr: base } => {
+ // *base
+ self.walk_expr(base)?;
+ }
+
+ Expr::Field { expr: base, .. } => {
+ // base.f
+ self.walk_expr(base)?;
+ }
+
+ Expr::Index { base: lhs, index: rhs } => {
+ // lhs[rhs]
+ self.walk_expr(lhs)?;
+ self.consume_expr(rhs)?;
+ }
+
+ Expr::Call { callee, ref args } => {
+ // callee(args)
+ self.consume_expr(callee)?;
+ self.consume_exprs(args)?;
+ }
+
+ Expr::MethodCall { receiver, ref args, .. } => {
+ // callee.m(args)
+ self.consume_expr(receiver)?;
+ self.consume_exprs(args)?;
+ }
+
+ Expr::RecordLit { ref fields, spread, .. } => {
+ self.walk_struct_expr(fields, spread)?;
+ }
+
+ Expr::Tuple { ref exprs } => {
+ self.consume_exprs(exprs)?;
+ }
+
+ Expr::If {
+ condition: cond_expr,
+ then_branch: then_expr,
+ else_branch: opt_else_expr,
+ } => {
+ self.consume_expr(cond_expr)?;
+ self.consume_expr(then_expr)?;
+ if let Some(else_expr) = opt_else_expr {
+ self.consume_expr(else_expr)?;
+ }
+ }
+
+ Expr::Let { pat, expr: init } => {
+ self.walk_local(init, pat, None, |this| {
+ this.borrow_expr(init, BorrowKind::Immutable)
+ })?;
+ }
+
+ Expr::Match { expr: discr, ref arms } => {
+ let discr_place = self.cat_expr(discr)?;
+ self.fake_read_scrutinee(discr_place.clone(), true);
+ self.walk_expr(discr)?;
+
+ for arm in arms {
+ self.walk_arm(discr_place.clone(), arm)?;
+ }
+ }
+
+ Expr::Array(Array::ElementList { elements: ref exprs }) => {
+ self.consume_exprs(exprs)?;
+ }
+
+ Expr::Ref { expr: base, mutability: m, .. } => {
+ // &base
+ // make sure that the thing we are pointing out stays valid
+ // for the lifetime `scope_r` of the resulting ptr:
+ let bk = BorrowKind::from_hir_mutbl(m);
+ self.borrow_expr(base, bk)?;
+ }
+
+ Expr::InlineAsm(ref asm) => {
+ for (_, op) in &asm.operands {
+ match *op {
+ AsmOperand::In { expr, .. } => {
+ self.consume_expr(expr)?;
+ }
+ AsmOperand::Out { expr: Some(expr), .. }
+ | AsmOperand::InOut { expr, .. } => {
+ self.mutate_expr(expr)?;
+ }
+ AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
+ self.consume_expr(in_expr)?;
+ if let Some(out_expr) = out_expr {
+ self.mutate_expr(out_expr)?;
+ }
+ }
+ AsmOperand::Out { expr: None, .. }
+ | AsmOperand::Const { .. }
+ | AsmOperand::Sym { .. } => {}
+ AsmOperand::Label(block) => {
+ self.walk_expr(block)?;
+ }
+ }
+ }
+ }
+
+ Expr::Continue { .. }
+ | Expr::Literal(..)
+ | Expr::Const(..)
+ | Expr::OffsetOf(..)
+ | Expr::Missing
+ | Expr::Underscore => {}
+
+ Expr::Loop { body: blk, .. } => {
+ self.walk_expr(blk)?;
+ }
+
+ Expr::UnaryOp { expr: lhs, .. } => {
+ self.consume_expr(lhs)?;
+ }
+
+ Expr::BinaryOp {
+ lhs,
+ rhs,
+ op: Some(BinaryOp::ArithOp(..) | BinaryOp::CmpOp(..) | BinaryOp::LogicOp(..)),
+ } => {
+ self.consume_expr(lhs)?;
+ self.consume_expr(rhs)?;
+ }
+
+ Expr::Block { ref statements, tail, .. }
+ | Expr::Unsafe { ref statements, tail, .. } => {
+ for stmt in statements {
+ self.walk_stmt(stmt)?;
+ }
+
+ if let Some(tail_expr) = tail {
+ self.consume_expr(tail_expr)?;
+ }
+ }
+
+ Expr::Break { expr: opt_expr, .. } | Expr::Return { expr: opt_expr } => {
+ if let Some(expr) = opt_expr {
+ self.consume_expr(expr)?;
+ }
+ }
+
+ Expr::Become { expr } | Expr::Await { expr } | Expr::Box { expr } => {
+ self.consume_expr(expr)?;
+ }
+
+ Expr::Assignment { target, value } => {
+ 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.reset_to_guard(update_guard);
+ }
+
+ Expr::Cast { expr: base, .. } => {
+ self.consume_expr(base)?;
+ }
+
+ Expr::BinaryOp { lhs, rhs, op: None | Some(BinaryOp::Assignment { .. }) } => {
+ self.consume_expr(lhs)?;
+ self.consume_expr(rhs)?;
+ }
+
+ Expr::Array(Array::Repeat { initializer: base, .. }) => {
+ self.consume_expr(base)?;
+ }
+
+ Expr::Closure { .. } => {
+ self.walk_captures(expr);
+ }
+
+ Expr::Yield { expr: value } | Expr::Yeet { expr: value } => {
+ if let Some(value) = value {
+ self.consume_expr(value)?;
+ }
+ }
+
+ Expr::Range { lhs, rhs, .. } => {
+ if let Some(lhs) = lhs {
+ self.consume_expr(lhs)?;
+ }
+ if let Some(rhs) = rhs {
+ self.consume_expr(rhs)?;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn walk_stmt(&mut self, stmt: &Statement) -> Result {
+ match *stmt {
+ Statement::Let { pat, initializer: Some(expr), else_branch: els, .. } => {
+ self.walk_local(expr, pat, els, |_| Ok(()))?;
+ }
+
+ Statement::Let { .. } => {}
+
+ Statement::Item(_) => {
+ // We don't visit nested items in this visitor,
+ // only the fn body we were given.
+ }
+
+ Statement::Expr { expr, .. } => {
+ self.consume_expr(expr)?;
+ }
+ }
+ Ok(())
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn fake_read_scrutinee(&mut self, discr_place: PlaceWithOrigin, refutable: bool) {
+ let closure_def_id = match discr_place.place.base {
+ PlaceBase::Upvar { closure, var_id: _ } => Some(closure),
+ _ => None,
+ };
+
+ let cause = if refutable {
+ FakeReadCause::ForMatchedPlace(closure_def_id)
+ } else {
+ FakeReadCause::ForLet(closure_def_id)
+ };
+
+ self.delegate.fake_read(discr_place, cause, self.cx);
+ }
+
+ fn walk_local<F>(&mut self, expr: ExprId, pat: PatId, els: Option<ExprId>, mut f: F) -> Result
+ where
+ F: FnMut(&mut Self) -> Result,
+ {
+ self.walk_expr(expr)?;
+ 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)?;
+ if let Some(els) = els {
+ self.walk_expr(els)?;
+ }
+ Ok(())
+ }
+
+ fn walk_struct_expr(&mut self, fields: &[RecordLitField], spread: RecordSpread) -> Result {
+ // Consume the expressions supplying values for each field.
+ for field in fields {
+ self.consume_expr(field.expr)?;
+ }
+
+ let RecordSpread::Expr(with_expr) = spread else { return Ok(()) };
+
+ let with_place = self.cat_expr(with_expr)?;
+
+ // 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() {
+ TyKind::Adt(adt, args) if adt.is_struct() => {
+ let AdtId::StructId(adt) = adt.def_id().0 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.
+ for (f_index, with_field) in adt_fields.iter() {
+ let is_mentioned = fields.iter().any(|f| f.name == with_field.name);
+ if !is_mentioned {
+ let field_place = self.cat_projection(
+ with_expr.into(),
+ with_place.clone(),
+ adt_field_types[f_index].get().instantiate(self.cx.interner(), args),
+ ProjectionKind::Field {
+ field_idx: f_index.into_raw().into_u32(),
+ variant_idx: 0,
+ },
+ );
+ self.consume_or_copy(field_place);
+ }
+ }
+ }
+ _ => {}
+ }
+
+ // walk the with expression so that complex expressions
+ // are properly handled.
+ self.walk_expr(with_expr)?;
+
+ Ok(())
+ }
+
+ fn expr_adjustments(&self, expr: ExprId) -> SmallVec<[Adjustment; 5]> {
+ // Due to borrowck problems, we cannot borrow the adjustments, unfortunately.
+ self.cx.result.expr_adjustment(expr).unwrap_or_default().into()
+ }
+
+ /// Invoke the appropriate delegate calls for anything that gets
+ /// consumed or borrowed as part of the automatic adjustment
+ /// process.
+ fn walk_adjustment(&mut self, expr: ExprId) -> Result {
+ let adjustments = self.expr_adjustments(expr);
+ let mut place_with_id = self.cat_expr_unadjusted(expr)?;
+ for adjustment in &adjustments {
+ debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
+ match adjustment.kind {
+ Adjust::NeverToAny | Adjust::Pointer(_) => {
+ // Creating a closure/fn-pointer or unsizing consumes
+ // the input and stores it into the resulting rvalue.
+ self.consume_or_copy(place_with_id.clone());
+ }
+
+ Adjust::Deref(None) => {}
+
+ // Autoderefs for overloaded Deref calls in fact reference
+ // their receiver. That is, if we have `(*x)` where `x`
+ // is of type `Rc<T>`, then this in fact is equivalent to
+ // `x.deref()`. Since `deref()` is declared with `&self`,
+ // this is an autoref of `x`.
+ Adjust::Deref(Some(ref deref)) => {
+ let bk = BorrowKind::from_mutbl(deref.0);
+ self.delegate.borrow(place_with_id.clone(), bk, self.cx);
+ }
+
+ Adjust::Borrow(ref autoref) => {
+ self.walk_autoref(expr, place_with_id.clone(), autoref);
+ }
+ }
+ place_with_id = self.cat_expr_adjusted(expr, place_with_id, adjustment)?;
+ }
+ Ok(())
+ }
+
+ /// Walks the autoref `autoref` applied to the autoderef'd
+ /// `expr`. `base_place` is `expr` represented as a place,
+ /// after all relevant autoderefs have occurred.
+ fn walk_autoref(&mut self, expr: ExprId, base_place: PlaceWithOrigin, autoref: &AutoBorrow) {
+ debug!("walk_autoref(expr={:?} base_place={:?} autoref={:?})", expr, base_place, autoref);
+
+ match *autoref {
+ AutoBorrow::Ref(m) => {
+ self.delegate.borrow(base_place, BorrowKind::from_mutbl(m.into()), self.cx);
+ }
+
+ AutoBorrow::RawPtr(m) => {
+ debug!("walk_autoref: expr={:?} base_place={:?}", expr, base_place);
+
+ self.delegate.borrow(base_place, BorrowKind::from_mutbl(m), self.cx);
+ }
+ }
+ }
+
+ fn walk_arm(&mut self, discr_place: PlaceWithOrigin, arm: &MatchArm) -> Result {
+ self.walk_pat(discr_place, arm.pat, arm.guard.is_some(), PatWalkMode::Declaration)?;
+
+ if let Some(e) = arm.guard {
+ self.consume_expr(e)?;
+ }
+
+ self.consume_expr(arm.expr)
+ }
+
+ /// The core driver for walking a pattern
+ ///
+ /// This should mirror how pattern-matching gets lowered to MIR, as
+ /// otherwise lowering will ICE when trying to resolve the upvars.
+ ///
+ /// However, it is okay to approximate it here by doing *more* accesses than
+ /// the actual MIR builder will, which is useful when some checks are too
+ /// cumbersome to perform here. For example, if after typeck it becomes
+ /// clear that only one variant of an enum is inhabited, and therefore a
+ /// read of the discriminant is not necessary, `walk_pat` will have
+ /// over-approximated the necessary upvar capture granularity.
+ ///
+ /// 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 {
+ self.cat_pattern(discr_place.clone(), pat, &mut |this, place, pat| {
+ debug!("walk_pat: pat.kind={:?}", this.cx.store[pat]);
+ let read_discriminant = {
+ let place = place.clone();
+ |this: &mut Self| {
+ this.delegate.borrow(place, BorrowKind::Immutable, this.cx);
+ }
+ };
+
+ match this.cx.store[pat] {
+ Pat::Bind { id, .. } => {
+ debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
+ let bm = this.cx.result.binding_modes[pat];
+ debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat, bm);
+
+ // pat_ty: the type of the binding being produced.
+ let pat_ty = this.node_ty(pat.into())?;
+ debug!("walk_pat: pat_ty={:?}", pat_ty);
+
+ if let Ok(binding_place) = this.cat_local(pat.into(), pat_ty, id) {
+ this.delegate.bind(binding_place, this.cx);
+ }
+
+ // Subtle: MIR desugaring introduces immutable borrows for each pattern
+ // binding when lowering pattern guards to ensure that the guard does not
+ // modify the scrutinee.
+ if has_guard {
+ read_discriminant(this);
+ }
+
+ // It is also a borrow or copy/move of the value being matched.
+ // 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)) => {
+ let bk = BorrowKind::from_mutbl(m);
+ this.delegate.borrow(place, bk, this.cx);
+ }
+ None | Some(BindingMode::Move) => {
+ debug!("walk_pat binding consuming pat");
+ this.consume_or_copy(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
+
+ let is_assoc_const = this
+ .cx
+ .result
+ .assoc_resolutions_for_pat(pat)
+ .is_some_and(|it| matches!(it.0, CandidateId::ConstId(_)));
+ let resolution = this.cx.resolver.resolve_path_in_value_ns_fully(
+ this.cx.db,
+ path,
+ 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 {
+ // 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()) {
+ // Otherwise, this is a struct/enum variant, and so it's
+ // only a read if we need to read the discriminant.
+ read_discriminant(this);
+ }
+ }
+ Pat::Lit(_) | Pat::ConstBlock(_) | Pat::Range { .. } => {
+ // When matching against a literal or range, we need to
+ // borrow the place to compare it against the pattern.
+ //
+ // Note that we do this read even if the range matches all
+ // possible values, such as 0..=u8::MAX. This is because
+ // we don't want to depend on consteval here.
+ //
+ // FIXME: What if the type being matched only has one
+ // possible value?
+ read_discriminant(this);
+ }
+ Pat::Record { .. } | Pat::TupleStruct { .. } => {
+ if this.is_multivariant_adt(place.place.ty()) {
+ read_discriminant(this);
+ }
+ }
+ Pat::Slice { prefix: ref lhs, slice: wild, suffix: ref rhs } => {
+ // We don't need to test the length if the pattern is `[..]`
+ if matches!((&**lhs, wild, &**rhs), (&[], Some(_), &[]))
+ // Arrays have a statically known size, so
+ // there is no need to read their length
+ || place.place.ty().strip_references().is_array()
+ {
+ // No read necessary
+ } else {
+ read_discriminant(this);
+ }
+ }
+ Pat::Expr(expr) if mode == PatWalkMode::Assignment => {
+ // Destructuring assignment.
+ this.mutate_expr(expr)?;
+ }
+ Pat::Or(_)
+ | Pat::Box { .. }
+ | Pat::Ref { .. }
+ | Pat::Tuple { .. }
+ | Pat::Wild
+ | Pat::Missing => {
+ // 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(())
+ })
+ }
+
+ /// Handle the case where the current body contains a closure.
+ ///
+ /// When the current body being handled is a closure, then we must make sure that
+ /// - The parent closure only captures Places from the nested closure that are not local to it.
+ ///
+ /// In the following example the closures `c` only captures `p.x` even though `incr`
+ /// is a capture of the nested closure
+ ///
+ /// ```
+ /// struct P { x: i32 }
+ /// let mut p = P { x: 4 };
+ /// let c = || {
+ /// let incr = 10;
+ /// let nested = || p.x += incr;
+ /// };
+ /// ```
+ ///
+ /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
+ /// closure as the DefId.
+ #[instrument(skip(self), level = "debug")]
+ fn walk_captures(&mut self, closure_expr: ExprId) {
+ fn upvar_is_local_variable(upvars: UpvarsRef<'_>, var_id: BindingId) -> bool {
+ upvars.contains(var_id)
+ }
+
+ // If we have a nested closure, we want to include the fake reads present in the nested
+ // closure.
+ // `remove()` then re-insert and not `get()` due to borrowck errors.
+ if let Some(closure_data) = self.cx.result.closures_data.remove(&closure_expr) {
+ for (fake_read, cause, origins) in closure_data.fake_reads.iter() {
+ match fake_read.base {
+ PlaceBase::Upvar { var_id, closure: _ } => {
+ if upvar_is_local_variable(self.upvars, var_id) {
+ // The nested closure might be fake reading the current (enclosing) closure's local variables.
+ // The only places we want to fake read before creating the parent closure are the ones that
+ // are not local to it/ defined by it.
+ //
+ // ```rust,ignore(cannot-test-this-because-pseudo-code)
+ // let v1 = (0, 1);
+ // let c = || { // fake reads: v1
+ // let v2 = (0, 1);
+ // let e = || { // fake reads: v1, v2
+ // let (_, t1) = v1;
+ // let (_, t2) = v2;
+ // }
+ // }
+ // ```
+ // This check is performed when visiting the body of the outermost closure (`c`) and ensures
+ // that we don't add a fake read of v2 in c.
+ continue;
+ }
+ }
+ _ => {
+ panic!(
+ "Do not know how to get ExprId out of Rvalue and StaticItem {:?}",
+ fake_read.base
+ );
+ }
+ };
+ self.delegate.fake_read(
+ PlaceWithOrigin { place: fake_read.clone(), origins: origins.clone() },
+ *cause,
+ self.cx,
+ );
+ }
+
+ for (var_id, min_list) in closure_data.min_captures.iter() {
+ if !self.upvars.contains(*var_id) {
+ // The nested closure might be capturing the current (enclosing) closure's local variables.
+ // We check if the root variable is ever mentioned within the enclosing closure, if not
+ // then for the current body (if it's a closure) these aren't captures, we will ignore them.
+ continue;
+ }
+ for captured_place in min_list {
+ let place = &captured_place.place;
+ let capture_info = &captured_place.info;
+
+ // Mark the place to be captured by the enclosing closure
+ let place_base =
+ PlaceBase::Upvar { var_id: *var_id, closure: self.closure_expr };
+ let place_with_id = PlaceWithOrigin::new(
+ capture_info.sources.clone(),
+ place.base_ty.as_ref(),
+ place_base,
+ place.projections.clone(),
+ );
+
+ match capture_info.capture_kind {
+ UpvarCapture::ByValue => {
+ self.consume_or_copy(place_with_id);
+ }
+ UpvarCapture::ByUse => {
+ self.consume_clone_or_copy(place_with_id);
+ }
+ UpvarCapture::ByRef(upvar_borrow) => {
+ self.delegate.borrow(place_with_id, upvar_borrow, self.cx);
+ }
+ }
+ }
+ }
+
+ self.cx.result.closures_data.insert(closure_expr, closure_data);
+ }
+ }
+
+ fn error_reported_in_ty(&self, ty: Ty<'db>) -> Result {
+ if ty.is_ty_error() { Err(ErrorGuaranteed) } else { Ok(()) }
+ }
+}
+
+/// 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
+/// places were called "categorizations".
+///
+/// Note that a [`Place`] differs somewhat from the expression itself. For
+/// example, auto-derefs are explicit. Also, an index `a[b]` is decomposed into
+/// two operations: a dereference to reach the array data and then an index to
+/// jump forward to the relevant item.
+impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
+ fn expect_and_resolve_type(&mut self, ty: Option<Ty<'db>>) -> Result<Ty<'db>> {
+ match ty {
+ Some(ty) => {
+ let ty = self.cx.infcx().resolve_vars_if_possible(ty);
+ self.error_reported_in_ty(ty)?;
+ Ok(ty)
+ }
+ None => Err(ErrorGuaranteed),
+ }
+ }
+
+ fn node_ty(&mut self, id: ExprOrPatId) -> Result<Ty<'db>> {
+ self.expect_and_resolve_type(self.cx.result.type_of_expr_or_pat(id))
+ }
+
+ fn expr_ty(&mut self, expr: ExprId) -> Result<Ty<'db>> {
+ 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))
+ }
+
+ /// Returns the type of value that this pattern matches against.
+ /// Some non-obvious cases:
+ ///
+ /// - a `ref x` binding matches against a value of type `T` and gives
+ /// `x` the type `&T`; we return `T`.
+ /// - a pattern with implicit derefs (thanks to default binding
+ /// modes #42640) may look like `Some(x)` but in fact have
+ /// implicit deref patterns attached (e.g., it is really
+ /// `&Some(x)`). In that case, we return the "outermost" type
+ /// (e.g., `&Option<T>`).
+ fn pat_ty_adjusted(&mut self, pat: PatId) -> Result<Ty<'db>> {
+ // Check for implicit `&` types wrapping the pattern; note
+ // 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()
+ {
+ debug!("pat_ty(pat={:?}) found adjustment `{:?}`", pat, first_adjust);
+ return Ok(first_adjust.as_ref());
+ }
+ 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))
+ }
+
+ fn cat_expr(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> {
+ self.cat_expr_(expr, &self.expr_adjustments(expr))
+ }
+
+ /// This recursion helper avoids going through *too many*
+ /// adjustments, since *only* non-overloaded deref recurses.
+ fn cat_expr_(&mut self, expr: ExprId, adjustments: &[Adjustment]) -> Result<PlaceWithOrigin> {
+ match adjustments.split_last() {
+ None => self.cat_expr_unadjusted(expr),
+ Some((adjustment, previous)) => {
+ self.cat_expr_adjusted_with(expr, |this| this.cat_expr_(expr, previous), adjustment)
+ }
+ }
+ }
+
+ fn cat_expr_adjusted(
+ &mut self,
+ expr: ExprId,
+ previous: PlaceWithOrigin,
+ adjustment: &Adjustment,
+ ) -> Result<PlaceWithOrigin> {
+ self.cat_expr_adjusted_with(expr, |_this| Ok(previous), adjustment)
+ }
+
+ fn cat_expr_adjusted_with<F>(
+ &mut self,
+ expr: ExprId,
+ previous: F,
+ adjustment: &Adjustment,
+ ) -> Result<PlaceWithOrigin>
+ where
+ F: FnOnce(&mut Self) -> Result<PlaceWithOrigin>,
+ {
+ let target = self.cx.infcx().resolve_vars_if_possible(adjustment.target.as_ref());
+ match adjustment.kind {
+ Adjust::Deref(overloaded) => {
+ // Equivalent to *expr or something similar.
+ let base = if let Some(deref) = overloaded {
+ let ref_ty = Ty::new_ref(
+ self.cx.interner(),
+ self.cx.types.regions.erased,
+ target,
+ deref.0,
+ );
+ self.cat_rvalue(expr.into(), ref_ty)
+ } else {
+ previous(self)?
+ };
+ self.cat_deref(expr.into(), base)
+ }
+
+ Adjust::NeverToAny | Adjust::Pointer(_) | Adjust::Borrow(_) => {
+ // Result is an rvalue.
+ Ok(self.cat_rvalue(expr.into(), target))
+ }
+ }
+ }
+
+ fn cat_expr_unadjusted(&mut self, expr: ExprId) -> Result<PlaceWithOrigin> {
+ let expr_ty = self.expr_ty(expr)?;
+ match self.cx.store[expr] {
+ Expr::UnaryOp { expr: e_base, op: UnaryOp::Deref } => {
+ if self.cx.result.method_resolutions.contains_key(&expr) {
+ self.cat_overloaded_place(expr, e_base)
+ } else {
+ let base = self.cat_expr(e_base)?;
+ self.cat_deref(expr.into(), base)
+ }
+ }
+
+ Expr::Field { expr: base, .. } => {
+ let base = self.cat_expr(base)?;
+ debug!(?base);
+
+ let field_idx = self
+ .cx
+ .result
+ .field_resolutions
+ .get(&expr)
+ .map(|field| match *field {
+ Either::Left(field) => field.local_id.into_raw().into_u32(),
+ Either::Right(tuple_field) => tuple_field.index,
+ })
+ .ok_or(ErrorGuaranteed)?;
+
+ Ok(self.cat_projection(
+ expr.into(),
+ base,
+ expr_ty,
+ ProjectionKind::Field { field_idx, variant_idx: 0 },
+ ))
+ }
+
+ Expr::Index { base, index: _ } => {
+ // rustc checks if this is an overloaded index, but the check is buggy and treats any indexing
+ // as overloaded, see https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/.E2.9C.94.20Is.20builtin.20indexing.20any.20special.20in.20typeck.3F/near/565881390.
+ // So that's what we do here.
+ self.cat_overloaded_place(expr, base)
+ }
+
+ Expr::Path(ref path) => {
+ let resolver_guard =
+ self.cx.resolver.update_to_inner_scope(self.cx.db, self.cx.owner, expr);
+ let resolution = self.cx.resolver.resolve_path_in_value_ns_fully(
+ self.cx.db,
+ path,
+ self.cx.store.expr_path_hygiene(expr),
+ );
+ self.cx.resolver.reset_to_guard(resolver_guard);
+ match (resolution, self.cx.result.assoc_resolutions_for_expr(expr)) {
+ (_, Some((CandidateId::FunctionId(_) | CandidateId::ConstId(_), _)))
+ | (
+ Some(
+ ValueNs::ConstId(_)
+ | ValueNs::GenericParam(_)
+ | ValueNs::FunctionId(_)
+ | ValueNs::ImplSelf(_)
+ | ValueNs::EnumVariantId(_)
+ | ValueNs::StructId(_),
+ ),
+ None,
+ ) => Ok(self.cat_rvalue(expr.into(), expr_ty)),
+ (Some(ValueNs::StaticId(_)), None) => Ok(PlaceWithOrigin::new_no_projections(
+ expr,
+ expr_ty,
+ PlaceBase::StaticItem,
+ )),
+ (Some(ValueNs::LocalBinding(var_id)), None) => {
+ self.cat_local(expr.into(), expr_ty, var_id)
+ }
+ (None, None) => Err(ErrorGuaranteed),
+ }
+ }
+
+ _ => Ok(self.cat_rvalue(expr.into(), expr_ty)),
+ }
+ }
+
+ fn cat_local(
+ &mut self,
+ id: ExprOrPatId,
+ expr_ty: Ty<'db>,
+ var_id: BindingId,
+ ) -> Result<PlaceWithOrigin> {
+ if self.upvars.contains(var_id) {
+ self.cat_upvar(id, var_id)
+ } else {
+ Ok(PlaceWithOrigin::new_no_projections(id, expr_ty, PlaceBase::Local(var_id)))
+ }
+ }
+
+ /// Categorize an upvar.
+ ///
+ /// Note: the actual upvar access contains invisible derefs of closure
+ /// environment and upvar reference as appropriate. Only regionck cares
+ /// about these dereferences, so we let it compute them as needed.
+ fn cat_upvar(&mut self, hir_id: ExprOrPatId, var_id: BindingId) -> Result<PlaceWithOrigin> {
+ let var_ty = self.expect_and_resolve_type(
+ self.cx.result.type_of_binding.get(var_id).map(|it| it.as_ref()),
+ )?;
+
+ Ok(PlaceWithOrigin::new_no_projections(
+ hir_id,
+ var_ty,
+ PlaceBase::Upvar { closure: self.closure_expr, var_id },
+ ))
+ }
+
+ fn cat_rvalue(&self, hir_id: ExprOrPatId, expr_ty: Ty<'db>) -> PlaceWithOrigin {
+ PlaceWithOrigin::new_no_projections(hir_id, expr_ty, PlaceBase::Rvalue)
+ }
+
+ fn cat_projection(
+ &self,
+ node: ExprOrPatId,
+ mut base_place: PlaceWithOrigin,
+ ty: Ty<'db>,
+ kind: ProjectionKind,
+ ) -> PlaceWithOrigin {
+ base_place.push_projection(Projection { kind, ty: ty.store() }, node);
+ base_place
+ }
+
+ fn cat_overloaded_place(&mut self, expr: ExprId, base: ExprId) -> Result<PlaceWithOrigin> {
+ // Reconstruct the output assuming it's a reference with the
+ // same region and mutability as the receiver. This holds for
+ // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`.
+ 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()
+ else {
+ return Err(ErrorGuaranteed);
+ };
+ let ref_ty = Ty::new_ref(self.cx.interner(), region, place_ty, mutbl);
+
+ let base = self.cat_rvalue(expr.into(), ref_ty);
+ self.cat_deref(expr.into(), base)
+ }
+
+ fn cat_deref(
+ &mut self,
+ node: ExprOrPatId,
+ mut base_place: PlaceWithOrigin,
+ ) -> 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)
+ else {
+ debug!("explicit deref of non-derefable type: {:?}", base_curr_ty);
+ return Err(ErrorGuaranteed);
+ };
+ base_place.push_projection(
+ Projection { kind: ProjectionKind::Deref, ty: deref_ty.store() },
+ node,
+ );
+ Ok(base_place)
+ }
+
+ /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern
+ /// Here `pat_hir_id` is the ExprId of the pattern itself.
+ fn variant_index_for_adt(&self, pat_id: PatId) -> Result<(u32, VariantId)> {
+ let variant = self.cx.result.variant_resolution_for_pat(pat_id).ok_or(ErrorGuaranteed)?;
+ let variant_idx = match variant {
+ VariantId::EnumVariantId(variant) => variant.loc(self.cx.db).index,
+ VariantId::StructId(_) | VariantId::UnionId(_) => 0,
+ };
+ Ok((variant_idx, variant))
+ }
+
+ /// Returns the total number of fields in a tuple used within a Tuple pattern.
+ /// 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() {
+ TyKind::Tuple(args) => args.len(),
+ _ => panic!("tuple pattern not applied to a tuple"),
+ }
+ }
+
+ /// Here, `place` is the `PlaceWithId` being matched and pat is the pattern it
+ /// is being matched against.
+ ///
+ /// In general, the way that this works is that we walk down the pattern,
+ /// constructing a `PlaceWithId` that represents the path that will be taken
+ /// to reach the value being matched.
+ fn cat_pattern<F>(
+ &mut self,
+ mut place_with_id: PlaceWithOrigin,
+ pat: PatId,
+ op: &mut F,
+ ) -> Result
+ where
+ F: FnMut(&mut Self, PlaceWithOrigin, PatId) -> Result,
+ {
+ // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithId` correspondingly.
+ // `PlaceWithId`s are constructed differently from patterns. For example, in
+ //
+ // ```
+ // match foo {
+ // &&Some(x, ) => { ... },
+ // _ => { ... },
+ // }
+ // ```
+ //
+ // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the
+ // corresponding `PlaceWithId` we start with the `PlaceWithId` for `foo`, and then, by traversing the
+ // pattern, try to answer the question: given the address of `foo`, how is `x` reached?
+ //
+ // `&&Some(x,)` `place_foo`
+ // `&Some(x,)` `deref { place_foo}`
+ // `Some(x,)` `deref { deref { place_foo }}`
+ // `(x,)` `field0 { deref { deref { place_foo }}}` <- resulting place
+ //
+ // The above example has no adjustments. If the code were instead the (after adjustments,
+ // equivalent) version
+ //
+ // ```
+ // match foo {
+ // Some(x, ) => { ... },
+ // _ => { ... },
+ // }
+ // ```
+ //
+ // 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 {
+ 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)?;
+ }
+ let place_with_id = place_with_id; // lose mutability
+ debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id);
+
+ // Invoke the callback, but only now, after the `place_with_id` has adjusted.
+ //
+ // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that
+ // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We
+ // don't want to call `op` with these incompatible values. As written, what happens instead
+ // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern
+ // `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)?;
+
+ match self.cx.store[pat] {
+ Pat::Tuple { args: ref subpats, ellipsis: dots_pos } => {
+ // (p1, ..., pN)
+ let total_fields = self.total_fields_in_tuple(pat);
+
+ for (i, &subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
+ let subpat_ty = self.pat_ty_adjusted(subpat)?;
+ let projection_kind =
+ ProjectionKind::Field { field_idx: i as u32, variant_idx: 0 };
+ let sub_place = self.cat_projection(
+ pat.into(),
+ place_with_id.clone(),
+ subpat_ty,
+ projection_kind,
+ );
+ self.cat_pattern(sub_place, subpat, op)?;
+ }
+ }
+
+ Pat::TupleStruct { args: ref subpats, ellipsis: dots_pos, .. } => {
+ // S(p1, ..., pN)
+ let (variant_index, variant) = self.variant_index_for_adt(pat)?;
+ let total_fields = variant.fields(self.cx.db).len();
+
+ for (i, &subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) {
+ let subpat_ty = self.pat_ty_adjusted(subpat)?;
+ let projection_kind =
+ ProjectionKind::Field { variant_idx: variant_index, field_idx: i as u32 };
+ let sub_place = self.cat_projection(
+ pat.into(),
+ place_with_id.clone(),
+ subpat_ty,
+ projection_kind,
+ );
+ self.cat_pattern(sub_place, subpat, op)?;
+ }
+ }
+
+ Pat::Record { args: ref field_pats, .. } => {
+ // S { f1: p1, ..., fN: pN }
+
+ let (variant_index, variant) = self.variant_index_for_adt(pat)?;
+ let fields = variant.fields(self.cx.db);
+
+ for fp in field_pats {
+ let field_ty = self.pat_ty_adjusted(fp.pat)?;
+ let field_index = fields.field(&fp.name).ok_or(ErrorGuaranteed)?;
+
+ let field_place = self.cat_projection(
+ pat.into(),
+ place_with_id.clone(),
+ field_ty,
+ ProjectionKind::Field {
+ variant_idx: variant_index,
+ field_idx: field_index.into_raw().into_u32(),
+ },
+ );
+ self.cat_pattern(field_place, fp.pat, op)?;
+ }
+ }
+
+ Pat::Or(ref pats) => {
+ for &pat in pats {
+ self.cat_pattern(place_with_id.clone(), pat, op)?;
+ }
+ }
+
+ Pat::Bind { subpat: Some(subpat), .. } => {
+ self.cat_pattern(place_with_id, subpat, op)?;
+ }
+
+ Pat::Box { inner: subpat } | Pat::Ref { pat: subpat, .. } => {
+ // box p1, &p1, &mut p1. we can ignore the mutability of
+ // PatKind::Ref since that information is already contained
+ // in the type.
+ let subplace = self.cat_deref(pat.into(), place_with_id)?;
+ self.cat_pattern(subplace, 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())
+ .builtin_index()
+ else {
+ debug!("explicit index of non-indexable type {:?}", place_with_id);
+ panic!("explicit index of non-indexable type");
+ };
+ let elt_place = self.cat_projection(
+ pat.into(),
+ place_with_id.clone(),
+ element_ty,
+ ProjectionKind::Index,
+ );
+ for &before_pat in before {
+ self.cat_pattern(elt_place.clone(), before_pat, op)?;
+ }
+ if let Some(slice_pat) = slice {
+ let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?;
+ let slice_place = self.cat_projection(
+ pat.into(),
+ place_with_id,
+ slice_pat_ty,
+ ProjectionKind::Subslice,
+ );
+ self.cat_pattern(slice_place, slice_pat, op)?;
+ }
+ for &after_pat in after {
+ self.cat_pattern(elt_place.clone(), after_pat, op)?;
+ }
+ }
+
+ Pat::Bind { subpat: None, .. }
+ | Pat::Expr(..)
+ | Pat::Path(_)
+ | Pat::Lit(..)
+ | Pat::ConstBlock(..)
+ | Pat::Range { .. }
+ | Pat::Missing
+ | Pat::Wild => {
+ // always ok
+ }
+ }
+
+ Ok(())
+ }
+
+ /// 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
+ /// happen to be uninhabited.
+ ///
+ /// Here, it is not practical to perform such a check, because inhabitedness
+ /// queries require typeck results, and typeck requires closure capture analysis.
+ ///
+ /// Moreover, the language is moving towards uninhabited variants still semantically
+ /// causing a discriminant read, so we *shouldn't* perform any such check.
+ ///
+ /// 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() {
+ // 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 {
+ AdtId::StructId(_) | AdtId::UnionId(_) => false,
+ AdtId::EnumId(did) => {
+ let has_foreign_non_exhaustive = || {
+ AttrFlags::query(self.cx.db, did.into()).contains(AttrFlags::NON_EXHAUSTIVE)
+ && did.krate(self.cx.db) != self.cx.krate()
+ };
+ did.enum_variants(self.cx.db).variants.len() > 1 || has_foreign_non_exhaustive()
+ }
+ }
+ } else {
+ false
+ }
+ }
+}
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 47a7049248..732a583047 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -35,22 +35,23 @@
//! // and are then unable to coerce `&7i32` to `&mut i32`.
//! ```
+use std::ops::ControlFlow;
+
use hir_def::{
- CallableDefId,
+ CallableDefId, TraitId,
attrs::AttrFlags,
hir::{ExprId, ExprOrPatId},
signatures::FunctionSignature,
};
use rustc_ast_ir::Mutability;
use rustc_type_ir::{
- BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable,
- TypeVisitableExt,
+ BoundVar, DebruijnIndex, InferTy, TyVid, TypeAndMut, TypeFoldable, TypeFolder,
+ TypeSuperFoldable, TypeVisitableExt,
error::TypeError,
- inherent::{
- Const as _, GenericArg as _, GenericArgs as _, IntoKind, Safety as _, SliceLike, Ty as _,
- },
+ inherent::{Const as _, GenericArg as _, GenericArgs as _, IntoKind, Safety as _, Ty as _},
+ solve::{Certainty, NoSolution},
};
-use smallvec::{SmallVec, smallvec};
+use smallvec::SmallVec;
use tracing::{debug, instrument};
use crate::{
@@ -62,16 +63,16 @@ use crate::{
},
next_solver::{
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
- Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
- GenericArgs, ParamEnv, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
+ Canonical, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs,
+ Goal, ParamEnv, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
TypingMode,
abi::Safety,
infer::{
DbInternerInferExt, InferCtxt, InferOk, InferResult,
relate::RelateResult,
- select::{ImplSource, SelectionError},
- traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations},
+ traits::{Obligation, ObligationCause, PredicateObligations},
},
+ inspect::{InspectGoal, ProofTreeVisitor},
obligation_ctxt::ObligationCtxt,
},
upvars::upvars_mentioned,
@@ -85,9 +86,7 @@ trait CoerceDelegate<'db> {
fn set_diverging(&mut self, diverging_ty: Ty<'db>);
- fn set_tainted_by_errors(&mut self);
-
- fn type_var_is_sized(&mut self, var: TyVid) -> bool;
+ fn type_var_is_sized(&self, var: TyVid) -> bool;
}
struct Coerce<D> {
@@ -130,11 +129,6 @@ where
D: CoerceDelegate<'db>,
{
#[inline]
- fn set_tainted_by_errors(&mut self) {
- self.delegate.set_tainted_by_errors();
- }
-
- #[inline]
fn infcx(&self) -> &InferCtxt<'db> {
self.delegate.infcx()
}
@@ -680,125 +674,30 @@ where
// Create an obligation for `Source: CoerceUnsized<Target>`.
let cause = self.cause.clone();
-
- // Use a FIFO queue for this custom fulfillment procedure.
- //
- // A Vec (or SmallVec) is not a natural choice for a queue. However,
- // this code path is hot, and this queue usually has a max length of 1
- // and almost never more than 3. By using a SmallVec we avoid an
- // allocation, at the (very small) cost of (occasionally) having to
- // shift subsequent elements down when removing the front element.
- let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new(
+ let pred = TraitRef::new(
self.interner(),
- cause,
- self.param_env(),
- TraitRef::new(
- self.interner(),
- coerce_unsized_did.into(),
- [coerce_source, coerce_target]
+ coerce_unsized_did.into(),
+ [coerce_source, coerce_target],
+ );
+ let obligation = Obligation::new(self.interner(), cause, self.param_env(), pred);
+
+ coercion.obligations.push(obligation);
+
+ if self
+ .delegate
+ .infcx()
+ .visit_proof_tree(
+ Goal::new(self.infcx().interner, self.param_env(), pred),
+ &mut CoerceVisitor {
+ delegate: &self.delegate,
+ errored: false,
+ unsize_did,
+ coerce_unsized_did,
+ },
)
- )];
- // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid
- // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where
- // inference might unify those two inner type variables later.
- let traits = [coerce_unsized_did, unsize_did];
- while !queue.is_empty() {
- let obligation = queue.remove(0);
- let trait_pred = match obligation.predicate.kind().no_bound_vars() {
- Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred)))
- if traits.contains(&trait_pred.def_id().0) =>
- {
- self.infcx().resolve_vars_if_possible(trait_pred)
- }
- // Eagerly process alias-relate obligations in new trait solver,
- // since these can be emitted in the process of solving trait goals,
- // but we need to constrain vars before processing goals mentioning
- // them.
- Some(PredicateKind::AliasRelate(..)) => {
- let mut ocx = ObligationCtxt::new(self.infcx());
- ocx.register_obligation(obligation);
- if !ocx.try_evaluate_obligations().is_empty() {
- return Err(TypeError::Mismatch);
- }
- coercion.obligations.extend(ocx.into_pending_obligations());
- continue;
- }
- _ => {
- coercion.obligations.push(obligation);
- continue;
- }
- };
- debug!("coerce_unsized resolve step: {:?}", trait_pred);
- match self.infcx().select(&obligation.with(self.interner(), trait_pred)) {
- // Uncertain or unimplemented.
- Ok(None) => {
- if trait_pred.def_id().0 == unsize_did {
- let self_ty = trait_pred.self_ty();
- let unsize_ty = trait_pred.trait_ref.args[1].expect_ty();
- debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred);
- match (self_ty.kind(), unsize_ty.kind()) {
- (TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..))
- if self.delegate.type_var_is_sized(v) =>
- {
- debug!("coerce_unsized: have sized infer {:?}", v);
- coercion.obligations.push(obligation);
- // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going
- // for unsizing.
- }
- _ => {
- // Some other case for `$0: Unsize<Something>`. Note that we
- // hit this case even if `Something` is a sized type, so just
- // don't do the coercion.
- debug!("coerce_unsized: ambiguous unsize");
- return Err(TypeError::Mismatch);
- }
- }
- } else {
- debug!("coerce_unsized: early return - ambiguous");
- if !coerce_source.references_non_lt_error()
- && !coerce_target.references_non_lt_error()
- {
- // rustc always early-returns here, even when the types contains errors. However not bailing
- // improves error recovery, and while we don't implement generic consts properly, it also helps
- // correct code.
- return Err(TypeError::Mismatch);
- }
- }
- }
- Err(SelectionError::Unimplemented) => {
- debug!("coerce_unsized: early return - can't prove obligation");
- return Err(TypeError::Mismatch);
- }
-
- Err(SelectionError::TraitDynIncompatible(_)) => {
- // Dyn compatibility errors in coercion will *always* be due to the
- // fact that the RHS of the coercion is a non-dyn compatible `dyn Trait`
- // written in source somewhere (otherwise we will never have lowered
- // the dyn trait from HIR to middle).
- //
- // There's no reason to emit yet another dyn compatibility error,
- // especially since the span will differ slightly and thus not be
- // deduplicated at all!
- self.set_tainted_by_errors();
- }
- Err(_err) => {
- // FIXME: Report an error:
- // let guar = self.err_ctxt().report_selection_error(
- // obligation.clone(),
- // &obligation,
- // &err,
- // );
- self.set_tainted_by_errors();
- // Treat this like an obligation and follow through
- // with the unsizing - the lack of a coercion should
- // be silent, as it causes a type mismatch later.
- }
-
- Ok(Some(ImplSource::UserDefined(impl_source))) => {
- queue.extend(impl_source.nested);
- }
- Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
- }
+ .is_break()
+ {
+ return Err(TypeError::Mismatch);
}
Ok(coercion)
@@ -983,12 +882,7 @@ impl<'db> CoerceDelegate<'db> for InferenceCoercionDelegate<'_, '_, 'db> {
}
#[inline]
- fn set_tainted_by_errors(&mut self) {
- self.0.set_tainted_by_errors();
- }
-
- #[inline]
- fn type_var_is_sized(&mut self, var: TyVid) -> bool {
+ fn type_var_is_sized(&self, var: TyVid) -> bool {
self.0.table.type_var_is_sized(var)
}
}
@@ -1560,8 +1454,7 @@ impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> {
(self.target_features, TargetFeatureIsSafeInTarget::No)
}
fn set_diverging(&mut self, _diverging_ty: Ty<'db>) {}
- fn set_tainted_by_errors(&mut self) {}
- fn type_var_is_sized(&mut self, _var: TyVid) -> bool {
+ fn type_var_is_sized(&self, _var: TyVid) -> bool {
false
}
}
@@ -1663,7 +1556,7 @@ fn coerce<'db>(
Const::new_bound(
self.interner,
self.debruijn,
- BoundConst { var: BoundVar::from_usize(i) },
+ BoundConst::new(BoundVar::from_usize(i)),
)
},
)
@@ -1718,9 +1611,84 @@ fn coerce<'db>(
fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
let InternedClosure(owner, expr) = closure.loc(db);
- let Some(body_owner) = owner.as_def_with_body() else {
- return false;
- };
- upvars_mentioned(db, body_owner)
+ upvars_mentioned(db, owner)
.is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
}
+
+/// Recursively visit goals to decide whether an unsizing is possible.
+/// `Break`s when it isn't, and an error should be raised.
+/// `Continue`s when an unsizing ok based on an implementation of the `Unsize` trait / lang item.
+struct CoerceVisitor<'a, D> {
+ delegate: &'a D,
+ /// Whether the coercion is impossible. If so we sometimes still try to
+ /// coerce in these cases to emit better errors. This changes the behavior
+ /// when hitting the recursion limit.
+ errored: bool,
+ unsize_did: TraitId,
+ coerce_unsized_did: TraitId,
+}
+
+impl<'a, 'db, D: CoerceDelegate<'db>> ProofTreeVisitor<'db> for CoerceVisitor<'a, D> {
+ type Result = ControlFlow<()>;
+
+ fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result {
+ let Some(pred) = goal.goal().predicate.as_trait_clause() else {
+ return ControlFlow::Continue(());
+ };
+
+ // Make sure this predicate is referring to either an `Unsize` or `CoerceUnsized` trait,
+ // Otherwise there's nothing to do.
+ let def_id = pred.def_id().0;
+ if def_id != self.unsize_did && def_id != self.coerce_unsized_did {
+ return ControlFlow::Continue(());
+ }
+
+ match goal.result() {
+ // If we prove the `Unsize` or `CoerceUnsized` goal, continue recursing.
+ Ok(Certainty::Yes) => ControlFlow::Continue(()),
+ Err(NoSolution) => {
+ self.errored = true;
+ // Even if we find no solution, continue recursing if we find a single candidate
+ // for which we're shallowly certain it holds to get the right error source.
+ if let [only_candidate] = &goal.candidates()[..]
+ && only_candidate.shallow_certainty() == Certainty::Yes
+ {
+ only_candidate.visit_nested_no_probe(self)
+ } else {
+ ControlFlow::Break(())
+ }
+ }
+ Ok(Certainty::Maybe { .. }) => {
+ // FIXME: structurally normalize?
+ if def_id == self.unsize_did
+ && let TyKind::Dynamic(..) = pred.skip_binder().trait_ref.args.type_at(1).kind()
+ && let TyKind::Infer(InferTy::TyVar(vid)) = pred.self_ty().skip_binder().kind()
+ && self.delegate.type_var_is_sized(vid)
+ {
+ // We get here when trying to unsize a type variable to a `dyn Trait`,
+ // knowing that that variable is sized. Unsizing definitely has to happen in that case.
+ // If the variable weren't sized, we may not need an unsizing coercion.
+ // In general, we don't want to add coercions too eagerly since it makes error messages much worse.
+ ControlFlow::Continue(())
+ } else if let Some(cand) = goal.unique_applicable_candidate()
+ && cand.shallow_certainty() == Certainty::Yes
+ {
+ cand.visit_nested_no_probe(self)
+ } else {
+ ControlFlow::Break(())
+ }
+ }
+ }
+ }
+
+ fn on_recursion_limit(&mut self) -> Self::Result {
+ if self.errored {
+ // This prevents accidentally committing unfulfilled unsized coercions while trying to
+ // find the error source for diagnostics.
+ // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/266.
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+}
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index dc57b1d1c2..d80ea71674 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -11,23 +11,20 @@ use hir_def::{
InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
},
resolver::ValueNs,
- signatures::{FunctionSignature, VariantFields},
+ signatures::VariantFields,
};
use hir_def::{FunctionId, hir::ClosureKind};
use hir_expand::name::Name;
use rustc_ast_ir::Mutability;
use rustc_type_ir::{
- CoroutineArgs, CoroutineArgsParts, InferTy, Interner,
+ InferTy, Interner,
inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _},
};
use syntax::ast::RangeOp;
use tracing::debug;
use crate::{
- Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness,
- autoderef::InferenceContextAutoderef,
- consteval,
- db::InternedCoroutine,
+ Adjust, Adjustment, CallableDefId, DeclContext, DeclOrigin, Rawness, consteval,
generics::generics,
infer::{
AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable,
@@ -36,7 +33,7 @@ use crate::{
lower::{GenericPredicates, lower_mutability},
method_resolution::{self, CandidateId, MethodCallee, MethodError},
next_solver::{
- ErrorGuaranteed, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError,
+ ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError,
infer::{
BoundRegionConversionTime, InferOk,
traits::{Obligation, ObligationCause},
@@ -44,7 +41,6 @@ use crate::{
obligation_ctxt::ObligationCtxt,
util::clauses_as_obligations,
},
- traits::FnTrait,
};
use super::{
@@ -244,7 +240,6 @@ impl<'db> InferenceContext<'_, 'db> {
| Expr::Assignment { .. }
| Expr::Yield { .. }
| Expr::Cast { .. }
- | Expr::Async { .. }
| Expr::Unsafe { .. }
| Expr::Await { .. }
| Expr::Ref { .. }
@@ -390,9 +385,6 @@ impl<'db> InferenceContext<'_, 'db> {
})
.1
}
- Expr::Async { id: _, statements, tail } => {
- self.infer_async_block(tgt_expr, statements, tail)
- }
&Expr::Loop { body, label } => {
// FIXME: should be:
// let ty = expected.coercion_target_type(&mut self.table);
@@ -1184,134 +1176,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
oprnd_t
}
-
- fn infer_async_block(
- &mut self,
- tgt_expr: ExprId,
- statements: &[Statement],
- tail: &Option<ExprId>,
- ) -> Ty<'db> {
- let ret_ty = self.table.next_ty_var();
- let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
- let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty);
- let prev_ret_coercion = self.return_coercion.replace(CoerceMany::new(ret_ty));
-
- // FIXME: We should handle async blocks like we handle closures
- let expected = &Expectation::has_type(ret_ty);
- let (_, inner_ty) = self.with_breakable_ctx(BreakableKind::Border, None, None, |this| {
- let ty = this.infer_block(tgt_expr, statements, *tail, None, expected);
- if let Some(target) = expected.only_has_type(&mut this.table) {
- match this.coerce(tgt_expr.into(), ty, target, AllowTwoPhase::No, ExprIsRead::Yes) {
- Ok(res) => res,
- Err(_) => {
- this.result.type_mismatches.get_or_insert_default().insert(
- tgt_expr.into(),
- TypeMismatch { expected: target.store(), actual: ty.store() },
- );
- target
- }
- }
- } else {
- ty
- }
- });
-
- self.diverges = prev_diverges;
- self.return_ty = prev_ret_ty;
- self.return_coercion = prev_ret_coercion;
-
- self.lower_async_block_type_impl_trait(inner_ty, tgt_expr)
- }
-
- pub(crate) fn lower_async_block_type_impl_trait(
- &mut self,
- inner_ty: Ty<'db>,
- tgt_expr: ExprId,
- ) -> Ty<'db> {
- let coroutine_id = InternedCoroutine(self.owner, tgt_expr);
- let coroutine_id = self.db.intern_coroutine(coroutine_id).into();
- let parent_args = GenericArgs::identity_for_item(self.interner(), self.generic_def.into());
- Ty::new_coroutine(
- self.interner(),
- coroutine_id,
- CoroutineArgs::new(
- self.interner(),
- CoroutineArgsParts {
- parent_args: parent_args.as_slice(),
- kind_ty: self.types.types.unit,
- // rustc uses a special lang item type for the resume ty. I don't believe this can cause us problems.
- resume_ty: self.types.types.unit,
- yield_ty: self.types.types.unit,
- return_ty: inner_ty,
- // FIXME: Infer upvars.
- tupled_upvars_ty: self.types.types.unit,
- },
- )
- .args,
- )
- }
-
- pub(crate) fn write_fn_trait_method_resolution(
- &mut self,
- fn_x: FnTrait,
- derefed_callee: Ty<'db>,
- adjustments: &mut Vec<Adjustment>,
- callee_ty: Ty<'db>,
- params: &[Ty<'db>],
- tgt_expr: ExprId,
- ) {
- match fn_x {
- FnTrait::FnOnce | FnTrait::AsyncFnOnce => (),
- FnTrait::FnMut | FnTrait::AsyncFnMut => {
- if let TyKind::Ref(lt, inner, Mutability::Mut) = derefed_callee.kind() {
- if adjustments
- .last()
- .map(|it| matches!(it.kind, Adjust::Borrow(_)))
- .unwrap_or(true)
- {
- // prefer reborrow to move
- adjustments
- .push(Adjustment { kind: Adjust::Deref(None), target: inner.store() });
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Mut,
- inner,
- lt,
- ))
- }
- } else {
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Mut,
- derefed_callee,
- self.table.next_region_var(),
- ));
- }
- }
- FnTrait::Fn | FnTrait::AsyncFn => {
- if !matches!(derefed_callee.kind(), TyKind::Ref(_, _, Mutability::Not)) {
- adjustments.push(Adjustment::borrow(
- self.interner(),
- Mutability::Not,
- derefed_callee,
- self.table.next_region_var(),
- ));
- }
- }
- }
- let Some(trait_) = fn_x.get_id(self.lang_items) else {
- return;
- };
- let trait_data = trait_.trait_items(self.db);
- if let Some(func) = trait_data.method_by_name(&fn_x.method_name()) {
- let subst = GenericArgs::new_from_slice(&[
- callee_ty.into(),
- Ty::new_tup(self.interner(), params).into(),
- ]);
- self.write_method_resolution(tgt_expr, func, subst);
- }
- }
-
fn infer_expr_array(&mut self, array: &Array, expected: &Expectation<'db>) -> Ty<'db> {
let elem_ty = match expected
.to_option(&mut self.table)
@@ -1728,76 +1592,6 @@ impl<'db> InferenceContext<'_, 'db> {
MethodCallee { def_id, args, sig }
}
- fn infer_call(
- &mut self,
- tgt_expr: ExprId,
- callee: ExprId,
- args: &[ExprId],
- expected: &Expectation<'db>,
- ) -> Ty<'db> {
- let callee_ty = self.infer_expr(callee, &Expectation::none(), ExprIsRead::Yes);
- let callee_ty = self.table.try_structurally_resolve_type(callee_ty);
- let interner = self.interner();
- let mut derefs = InferenceContextAutoderef::new_from_inference_context(self, callee_ty);
- let (res, derefed_callee) = loop {
- let Some((callee_deref_ty, _)) = derefs.next() else {
- break (None, callee_ty);
- };
- if let Some(res) = derefs.ctx().table.callable_sig(callee_deref_ty, args.len()) {
- break (Some(res), callee_deref_ty);
- }
- };
- // if the function is unresolved, we use is_varargs=true to
- // suppress the arg count diagnostic here
- let is_varargs = derefed_callee.callable_sig(interner).is_some_and(|sig| sig.c_variadic())
- || res.is_none();
- let (param_tys, ret_ty) = match res {
- Some((func, params, ret_ty)) => {
- let infer_ok = derefs.adjust_steps_as_infer_ok();
- let mut adjustments = self.table.register_infer_ok(infer_ok);
- if let Some(fn_x) = func {
- self.write_fn_trait_method_resolution(
- fn_x,
- derefed_callee,
- &mut adjustments,
- callee_ty,
- &params,
- tgt_expr,
- );
- }
- if let TyKind::Closure(c, _) = self.table.resolve_completely(callee_ty).kind() {
- self.add_current_closure_dependency(c.into());
- self.deferred_closures.entry(c.into()).or_default().push((
- derefed_callee,
- callee_ty,
- params.clone(),
- tgt_expr,
- ));
- }
- self.write_expr_adj(callee, adjustments.into_boxed_slice());
- (params, ret_ty)
- }
- None => {
- self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
- call_expr: tgt_expr,
- found: callee_ty.store(),
- });
- (Vec::new(), Ty::new_error(interner, ErrorGuaranteed))
- }
- };
- let indices_to_skip = self.check_legacy_const_generics(derefed_callee, args);
- self.check_call(
- tgt_expr,
- args,
- callee_ty,
- &param_tys,
- ret_ty,
- &indices_to_skip,
- is_varargs,
- expected,
- )
- }
-
fn check_call(
&mut self,
tgt_expr: ExprId,
@@ -1819,6 +1613,7 @@ impl<'db> InferenceContext<'_, 'db> {
args,
indices_to_skip,
is_varargs,
+ TupleArgumentsFlag::DontTupleArguments,
);
ret_ty
}
@@ -1949,13 +1744,22 @@ impl<'db> InferenceContext<'_, 'db> {
};
let ret_ty = sig.output();
- self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic);
+ self.check_call_arguments(
+ tgt_expr,
+ param_tys,
+ ret_ty,
+ expected,
+ args,
+ &[],
+ sig.c_variadic,
+ TupleArgumentsFlag::DontTupleArguments,
+ );
ret_ty
}
/// Generic function that factors out common logic from function calls,
/// method calls and overloaded operators.
- pub(in super::super) fn check_call_arguments(
+ pub(super) fn check_call_arguments(
&mut self,
call_expr: ExprId,
// Types (as defined in the *signature* of the target function)
@@ -1968,7 +1772,18 @@ impl<'db> InferenceContext<'_, 'db> {
skip_indices: &[u32],
// Whether the function is variadic, for example when imported from C
c_variadic: bool,
+ // Whether the arguments have been bundled in a tuple (ex: closures)
+ tuple_arguments: TupleArgumentsFlag,
) {
+ let formal_input_tys: Vec<_> = formal_input_tys
+ .iter()
+ .map(|&ty| {
+ let generalized_ty = self.table.next_ty_var();
+ let _ = self.demand_eqtype(call_expr.into(), ty, generalized_ty);
+ generalized_ty
+ })
+ .collect();
+
// First, let's unify the formal method signature with the expectation eagerly.
// We use this to guide coercion inference; it's output is "fudged" which means
// any remaining type variables are assigned to new, unrelated variables. This
@@ -1988,29 +1803,68 @@ impl<'db> InferenceContext<'_, 'db> {
// No argument expectations are produced if unification fails.
let origin = ObligationCause::new();
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(),
+ self.table.param_env,
+ ClauseKind::WellFormed(ty.into()),
+ ));
+ }
+
if !ocx.try_evaluate_obligations().is_empty() {
return Err(TypeError::Mismatch);
}
// Record all the argument types, with the args
// produced from the above subtyping unification.
- Ok(Some(
- formal_input_tys
- .iter()
- .map(|&ty| self.table.infer_ctxt.resolve_vars_if_possible(ty))
- .collect(),
- ))
+ Ok(Some(formal_input_tys.clone()))
})
.ok()
})
.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
+ // FIXME: Emit an error.
+ (vec![self.types.types.error; provided_args.len()], None)
+ }
+ }
+ } else {
+ (formal_input_tys.to_vec(), 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 {
+ let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys {
assert_eq!(expected_input_tys.len(), formal_input_tys.len());
expected_input_tys
} else {
- formal_input_tys
+ formal_input_tys.clone()
};
let minimum_input_count = expected_input_tys.len();
@@ -2158,7 +2012,7 @@ impl<'db> InferenceContext<'_, 'db> {
);
let param_env = self.table.param_env;
self.table.register_predicates(clauses_as_obligations(
- generic_predicates.iter_instantiated_copied(self.interner(), parameters.as_slice()),
+ generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()),
ObligationCause::new(),
param_env,
));
@@ -2183,51 +2037,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
- /// Returns the argument indices to skip.
- fn check_legacy_const_generics(&mut self, callee: Ty<'db>, args: &[ExprId]) -> Box<[u32]> {
- let (func, _subst) = match callee.kind() {
- TyKind::FnDef(callable, subst) => {
- let func = match callable.0 {
- CallableDefId::FunctionId(f) => f,
- _ => return Default::default(),
- };
- (func, subst)
- }
- _ => return Default::default(),
- };
-
- let data = FunctionSignature::of(self.db, func);
- let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
- else {
- return Default::default();
- };
- let mut legacy_const_generics_indices = Box::<[u32]>::from(legacy_const_generics_indices);
-
- // only use legacy const generics if the param count matches with them
- if data.params.len() + legacy_const_generics_indices.len() != args.len() {
- if args.len() <= data.params.len() {
- return Default::default();
- } else {
- // there are more parameters than there should be without legacy
- // const params; use them
- legacy_const_generics_indices.sort_unstable();
- return legacy_const_generics_indices;
- }
- }
-
- // check legacy const parameters
- for arg_idx in legacy_const_generics_indices.iter().copied() {
- if arg_idx >= args.len() as u32 {
- continue;
- }
- let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
- self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
- // FIXME: evaluate and unify with the const
- }
- legacy_const_generics_indices.sort_unstable();
- legacy_const_generics_indices
- }
-
pub(super) fn with_breakable_ctx<T>(
&mut self,
kind: BreakableKind,
@@ -2243,3 +2052,28 @@ impl<'db> InferenceContext<'_, 'db> {
(if ctx.may_break { ctx.coerce.map(|ctx| ctx.complete(self)) } else { None }, res)
}
}
+
+/// Controls whether the arguments are tupled. This is used for the call
+/// operator.
+///
+/// Tupling means that all call-side arguments are packed into a tuple and
+/// passed as a single parameter. For example, if tupling is enabled, this
+/// function:
+/// ```
+/// fn f(x: (isize, isize)) {}
+/// ```
+/// Can be called as:
+/// ```ignore UNSOLVED (can this be done in user code?)
+/// # fn f(x: (isize, isize)) {}
+/// f(1, 2);
+/// ```
+/// Instead of:
+/// ```
+/// # fn f(x: (isize, isize)) {}
+/// f((1, 2));
+/// ```
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub(super) enum TupleArgumentsFlag {
+ DontTupleArguments,
+ TupleArguments,
+}
diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs
index bfe43fc928..b2369f6a87 100644
--- a/crates/hir-ty/src/infer/mutability.rs
+++ b/crates/hir-ty/src/infer/mutability.rs
@@ -38,7 +38,7 @@ impl<'db> InferenceContext<'_, 'db> {
) {
self.table.register_predicates(infer_ok.obligations);
}
- *d = OverloadedDeref(Some(mutability));
+ *d = OverloadedDeref(mutability);
}
}
Adjust::Borrow(b) => match b {
@@ -86,7 +86,6 @@ impl<'db> InferenceContext<'_, 'db> {
}
Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)),
Expr::Block { id: _, statements, tail, label: _ }
- | Expr::Async { id: _, statements, tail }
| Expr::Unsafe { id: _, statements, tail } => {
for st in statements.iter() {
match st {
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 71d68ccd47..3cadc8e933 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -228,7 +228,7 @@ 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_copied(interner, subst.as_slice()),
+ predicates.iter_instantiated(interner, subst.as_slice()),
ObligationCause::new(),
param_env,
));
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index d093412b42..b0f916b8c0 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -3,9 +3,7 @@
use std::fmt;
use base_db::Crate;
-use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId};
-use hir_expand::name::Name;
-use intern::sym;
+use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId};
use rustc_hash::FxHashSet;
use rustc_type_ir::{
TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom,
@@ -17,9 +15,9 @@ use smallvec::SmallVec;
use crate::{
db::HirDatabase,
next_solver::{
- AliasTy, Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg,
- GenericArgs, Goal, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef,
- Ty, TyKind, TypingMode,
+ Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal,
+ ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind,
+ TypingMode,
fulfill::{FulfillmentCtxt, NextSolverError},
infer::{
DbInternerInferExt, InferCtxt, InferOk, InferResult,
@@ -31,7 +29,7 @@ use crate::{
obligation_ctxt::ObligationCtxt,
},
traits::{
- FnTrait, NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt,
+ NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt,
next_trait_solve_in_ctxt,
},
};
@@ -174,6 +172,10 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.type_is_copy_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)
+ }
+
pub(crate) fn type_var_is_sized(&self, self_ty: TyVid) -> bool {
let Some(sized_did) = self.interner().lang_items().Sized else {
return true;
@@ -360,9 +362,6 @@ impl<'db> InferenceTable<'db> {
/// in this case.
pub(crate) fn try_structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> {
if let TyKind::Alias(..) = ty.kind() {
- // We need to use a separate variable here as otherwise the temporary for
- // `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting
- // in a reentrant borrow, causing an ICE.
let result = self
.infer_ctxt
.at(&ObligationCause::misc(), self.param_env)
@@ -445,6 +444,18 @@ impl<'db> InferenceTable<'db> {
}
}
+ 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]);
+ self.register_predicate(Obligation::new(
+ self.interner(),
+ cause,
+ self.param_env,
+ trait_ref,
+ ));
+ }
+ }
+
pub(crate) fn register_infer_ok<T>(&mut self, infer_ok: InferOk<'db, T>) -> T {
let InferOk { value, obligations } = infer_ok;
self.register_predicates(obligations);
@@ -489,78 +500,6 @@ impl<'db> InferenceTable<'db> {
}
}
- pub(crate) fn callable_sig(
- &mut self,
- ty: Ty<'db>,
- num_args: usize,
- ) -> Option<(Option<FnTrait>, Vec<Ty<'db>>, Ty<'db>)> {
- match ty.callable_sig(self.interner()) {
- Some(sig) => {
- let sig = sig.skip_binder();
- Some((None, sig.inputs_and_output.inputs().to_vec(), sig.output()))
- }
- None => {
- let (f, args_ty, return_ty) = self.callable_sig_from_fn_trait(ty, num_args)?;
- Some((Some(f), args_ty, return_ty))
- }
- }
- }
-
- fn callable_sig_from_fn_trait(
- &mut self,
- ty: Ty<'db>,
- num_args: usize,
- ) -> Option<(FnTrait, Vec<Ty<'db>>, Ty<'db>)> {
- let lang_items = self.interner().lang_items();
- for (fn_trait_name, output_assoc_name, subtraits) in [
- (FnTrait::FnOnce, sym::Output, &[FnTrait::Fn, FnTrait::FnMut][..]),
- (FnTrait::AsyncFnMut, sym::CallRefFuture, &[FnTrait::AsyncFn]),
- (FnTrait::AsyncFnOnce, sym::CallOnceFuture, &[]),
- ] {
- let fn_trait = fn_trait_name.get_id(lang_items)?;
- let trait_data = fn_trait.trait_items(self.db);
- let output_assoc_type =
- trait_data.associated_type_by_name(&Name::new_symbol_root(output_assoc_name))?;
-
- let mut arg_tys = Vec::with_capacity(num_args);
- let arg_ty = Ty::new_tup_from_iter(
- self.interner(),
- std::iter::repeat_with(|| {
- let ty = self.next_ty_var();
- arg_tys.push(ty);
- ty
- })
- .take(num_args),
- );
- let args = GenericArgs::new_from_slice(&[ty.into(), arg_ty.into()]);
- let trait_ref = TraitRef::new_from_args(self.interner(), fn_trait.into(), args);
-
- let proj_args = self.infer_ctxt.fill_rest_fresh_args(output_assoc_type.into(), args);
- let projection = Ty::new_alias(
- self.interner(),
- rustc_type_ir::AliasTyKind::Projection,
- AliasTy::new_from_args(self.interner(), output_assoc_type.into(), proj_args),
- );
-
- let pred = Predicate::upcast_from(trait_ref, self.interner());
- if !self.try_obligation(pred).no_solution() {
- self.register_obligation(pred);
- let return_ty = self.normalize_alias_ty(projection);
- for &fn_x in subtraits {
- let fn_x_trait = fn_x.get_id(lang_items)?;
- let trait_ref =
- TraitRef::new_from_args(self.interner(), fn_x_trait.into(), args);
- let pred = Predicate::upcast_from(trait_ref, self.interner());
- if !self.try_obligation(pred).no_solution() {
- return Some((fn_x, arg_tys, return_ty));
- }
- }
- return Some((fn_trait_name, arg_tys, return_ty));
- }
- }
- None
- }
-
pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: TypeFoldable<DbInterner<'db>>,
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs
index 54332122d0..798c62c192 100644
--- a/crates/hir-ty/src/layout.rs
+++ b/crates/hir-ty/src/layout.rs
@@ -21,7 +21,7 @@ use rustc_type_ir::{
use triomphe::Arc;
use crate::{
- InferenceResult, ParamEnvAndCrate,
+ ParamEnvAndCrate,
consteval::try_const_usize,
db::HirDatabase,
next_solver::{
@@ -331,25 +331,18 @@ pub fn layout_of_ty_query(
ptr.valid_range_mut().start = 1;
Layout::scalar(dl, ptr)
}
- TyKind::Closure(id, args) => {
- let def = db.lookup_intern_closure(id.0);
- let infer = InferenceResult::of(db, def.0);
- let (captures, _) = infer.closure_info(id.0);
- let fields = captures
- .iter()
- .map(|it| {
- let ty = it.ty.get().instantiate(interner, args.as_closure().parent_args());
- db.layout_of_ty(ty.store(), trait_env.clone())
- })
- .collect::<Result<Vec<_>, _>>()?;
- let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>();
- let fields = fields.iter().collect::<IndexVec<_, _>>();
- cx.calc.univariant(&fields, &ReprOptions::default(), StructKind::AlwaysSized)?
+ TyKind::Closure(_, args) => {
+ return db.layout_of_ty(args.as_closure().tupled_upvars_ty().store(), trait_env);
+ }
+ TyKind::Coroutine(_, args) => {
+ return db.layout_of_ty(args.as_coroutine().tupled_upvars_ty().store(), trait_env);
+ }
+ TyKind::CoroutineClosure(_, args) => {
+ return db
+ .layout_of_ty(args.as_coroutine_closure().tupled_upvars_ty().store(), trait_env);
}
- TyKind::Coroutine(_, _)
- | TyKind::CoroutineWitness(_, _)
- | TyKind::CoroutineClosure(_, _) => {
+ TyKind::CoroutineWitness(_, _) => {
return Err(LayoutError::NotImplemented);
}
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
index b0986c423b..1752b56b0f 100644
--- a/crates/hir-ty/src/layout/target.rs
+++ b/crates/hir-ty/src/layout/target.rs
@@ -2,7 +2,7 @@
use base_db::{Crate, target::TargetLoadError};
use hir_def::layout::TargetDataLayout;
-use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors};
+use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutError};
use triomphe::Arc;
use crate::db::HirDatabase;
@@ -16,30 +16,29 @@ pub fn target_data_layout_query(
Ok(it) => Ok(Arc::new(it)),
Err(e) => {
Err(match e {
- TargetDataLayoutErrors::InvalidAddressSpace { addr_space, cause, err } => {
+ TargetDataLayoutError::InvalidAddressSpace { addr_space, cause, err } => {
format!(
r#"invalid address space `{addr_space}` for `{cause}` in "data-layout": {err}"#
)
}
- TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => format!(r#"invalid {kind} `{bit}` for `{cause}` in "data-layout": {err}"#),
- TargetDataLayoutErrors::MissingAlignment { cause } => format!(r#"missing alignment for `{cause}` in "data-layout""#),
- TargetDataLayoutErrors::InvalidAlignment { cause, err } => format!(
- r#"invalid alignment for `{cause}` in "data-layout": `{align}` is {err_kind}"#,
- align = err.align(),
- err_kind = match err {
- AlignFromBytesError::NotPowerOfTwo(_) => "not a power of two",
- AlignFromBytesError::TooLarge(_) => "too large",
- }
- ),
- TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
+ TargetDataLayoutError::InvalidBits { kind, bit, cause, err } => format!(r#"invalid {kind} `{bit}` for `{cause}` in "data-layout": {err}"#),
+ TargetDataLayoutError::MissingAlignment { cause } => format!(r#"missing alignment for `{cause}` in "data-layout""#),
+ TargetDataLayoutError::InvalidAlignment { cause, err } => {
+ let (align, err_kind) = match err {
+ AlignFromBytesError::NotPowerOfTwo(align) => (align, "not a power of two"),
+ AlignFromBytesError::TooLarge(align) => (align, "too large"),
+ };
+ format!(r#"invalid alignment for `{cause}` in "data-layout": `{align}` is {err_kind}"#)
+ },
+ TargetDataLayoutError::InconsistentTargetArchitecture { dl, target } => {
format!(r#"inconsistent target specification: "data-layout" claims architecture is {dl}-endian, while "target-endian" is `{target}`"#)
}
- TargetDataLayoutErrors::InconsistentTargetPointerWidth {
+ TargetDataLayoutError::InconsistentTargetPointerWidth {
pointer_size,
target,
} => format!(r#"inconsistent target specification: "data-layout" claims pointers are {pointer_size}-bit, while "target-pointer-width" is `{target}`"#),
- TargetDataLayoutErrors::InvalidBitsSize { err } => err,
- TargetDataLayoutErrors::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifier in "data-layout": {err}"#),
+ TargetDataLayoutError::InvalidBitsSize { err } => err,
+ TargetDataLayoutError::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifier in "data-layout": {err}"#),
}.into())
}
},
diff --git a/crates/hir-ty/src/layout/tests/closure.rs b/crates/hir-ty/src/layout/tests/closure.rs
index 9e761aa98f..d214b70865 100644
--- a/crates/hir-ty/src/layout/tests/closure.rs
+++ b/crates/hir-ty/src/layout/tests/closure.rs
@@ -125,6 +125,8 @@ fn capture_specific_fields2() {
#[test]
fn capture_specific_fields() {
size_and_align_expr! {
+ minicore: fn;
+ stmts: []
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
@@ -132,6 +134,8 @@ fn capture_specific_fields() {
}
}
size_and_align_expr! {
+ minicore: fn;
+ stmts: []
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
@@ -140,7 +144,7 @@ fn capture_specific_fields() {
}
}
size_and_align_expr! {
- minicore: copy;
+ minicore: fn, copy;
stmts: [
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
@@ -151,6 +155,8 @@ fn capture_specific_fields() {
}
}
size_and_align_expr! {
+ minicore: fn;
+ stmts: []
struct X(i64, i32, (u8, i128));
let y: X = X(2, 5, (7, 3));
move |x: i64| {
@@ -159,6 +165,8 @@ fn capture_specific_fields() {
}
}
size_and_align_expr! {
+ minicore: fn;
+ stmts: []
struct X(i64, i32, (u8, i128));
let y = &&X(2, 5, (7, 3));
move |x: i64| {
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index e6b8329ca8..d004b5e3ef 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -62,7 +62,7 @@ use std::{hash::Hash, ops::ControlFlow};
use hir_def::{
CallableDefId, ExpressionStoreOwnerId, GenericDefId, TypeAliasId, TypeOrConstParamId,
- TypeParamId, hir::generics::GenericParams, resolver::TypeNs, type_ref::Rawness,
+ TypeParamId, resolver::TypeNs, type_ref::Rawness,
};
use hir_expand::name::Name;
use indexmap::{IndexMap, map::Entry};
@@ -84,7 +84,7 @@ use crate::{
lower::SupertraitsInfo,
next_solver::{
AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical,
- CanonicalVarKind, CanonicalVars, ClauseKind, Const, ConstKind, DbInterner, FnSig,
+ CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig,
GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi,
},
};
@@ -92,10 +92,8 @@ use crate::{
pub use autoderef::autoderef;
pub use infer::{
Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult,
- InferenceTyDiagnosticSource, OverloadedDeref, PointerCast,
- cast::CastError,
- closure::analysis::{CaptureKind, CapturedItem},
- could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect,
+ 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,
@@ -109,6 +107,16 @@ pub use utils::{
is_fn_unsafe_to_call, target_feature_is_safe_in_target,
};
+pub mod closure_analysis {
+ pub use crate::infer::{
+ CaptureInfo, CaptureSourceStack, CapturedPlace, ClosureData, UpvarCapture,
+ closure::analysis::{
+ BorrowKind,
+ expr_use_visitor::{FakeReadCause, Place, PlaceBase, Projection, ProjectionKind},
+ },
+ };
+}
+
/// A constant can have reference to other things. Memory map job is holding
/// the necessary bits of memory of the const eval session to keep the constant
/// meaningful.
@@ -197,7 +205,7 @@ pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<usize>
generics::generics(db, id.parent).type_or_const_param_idx(id)
}
-#[derive(Debug, Copy, Clone, Eq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FnAbi {
Aapcs,
AapcsUnwind,
@@ -239,21 +247,6 @@ pub enum FnAbi {
Unknown,
}
-impl PartialEq for FnAbi {
- fn eq(&self, _other: &Self) -> bool {
- // FIXME: Proper equality breaks `coercion::two_closures_lub` test
- true
- }
-}
-
-impl Hash for FnAbi {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- // Required because of the FIXME above and due to us implementing `Eq`, without this
- // we would break the `Hash` + `Eq` contract
- core::mem::discriminant(&Self::Unknown).hash(state);
- }
-}
-
impl FnAbi {
#[rustfmt::skip]
pub fn from_symbol(s: &Symbol) -> FnAbi {
@@ -435,7 +428,7 @@ where
ConstKind::Error(_) => {
let var = rustc_type_ir::BoundVar::from_usize(self.vars.len());
self.vars.push(CanonicalVarKind::Const(rustc_type_ir::UniverseIndex::ZERO));
- Ok(Const::new_bound(self.interner, self.binder, BoundConst { var }))
+ Ok(Const::new_bound(self.interner, self.binder, BoundConst::new(var)))
}
ConstKind::Infer(_) => error(),
ConstKind::Bound(BoundVarIndexKind::Bound(index), _) if index > self.binder => {
@@ -479,7 +472,7 @@ where
Canonical {
value,
max_universe: rustc_type_ir::UniverseIndex::ZERO,
- variables: CanonicalVars::new_from_slice(&error_replacer.vars),
+ var_kinds: CanonicalVarKinds::new_from_slice(&error_replacer.vars),
}
}
@@ -495,10 +488,7 @@ pub fn associated_type_shorthand_candidates(
TypeNs::GenericParam(param) => (def, param),
TypeNs::SelfType(impl_) => {
let impl_trait = db.impl_trait(impl_)?.skip_binder().def_id.0;
- let param = TypeParamId::from_unchecked(TypeOrConstParamId {
- parent: impl_trait.into(),
- local_id: GenericParams::SELF_PARAM_ID_IN_SELF,
- });
+ let param = TypeParamId::trait_self(impl_trait);
(impl_trait.into(), param)
}
_ => return None,
@@ -554,8 +544,11 @@ pub fn callable_sig_from_fn_trait<'db>(
let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args);
let projection = Ty::new_alias(
table.interner(),
- rustc_type_ir::AliasTyKind::Projection,
- AliasTy::new_from_args(table.interner(), output_assoc_type.into(), args),
+ AliasTy::new_from_args(
+ table.interner(),
+ rustc_type_ir::Projection { def_id: output_assoc_type.into() },
+ args,
+ ),
);
let pred = Predicate::upcast_from(trait_ref, table.interner());
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 7259099107..335aff2c1d 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -537,8 +537,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
let args = GenericArgs::identity_for_item(self.interner, opaque_ty_id);
Ty::new_alias(
self.interner,
- AliasTyKind::Opaque,
- AliasTy::new_from_args(self.interner, opaque_ty_id, args),
+ AliasTy::new_from_args(
+ self.interner,
+ AliasTyKind::Opaque { def_id: opaque_ty_id },
+ args,
+ ),
)
}
ImplTraitLoweringMode::Disallowed => {
@@ -1039,8 +1042,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
let args = GenericArgs::identity_for_item(interner, def_id);
let self_ty = Ty::new_alias(
self.interner,
- rustc_type_ir::AliasTyKind::Opaque,
- AliasTy::new_from_args(interner, def_id, args),
+ AliasTy::new_from_args(interner, rustc_type_ir::Opaque { def_id }, args),
);
let (predicates, assoc_ty_bounds_start) =
self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| {
@@ -1869,10 +1871,14 @@ fn resolve_type_param_assoc_type_shorthand(
.skip_binder();
let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args);
let current_result = StoredEarlyBinder::bind((assoc_type, args.store()));
- if let Some(this_trait_resolution) = this_trait_resolution {
- return AssocTypeShorthandResolution::Ambiguous {
- sub_trait_resolution: Some(this_trait_resolution),
- };
+ if let Some(this_trait_resolution) = &this_trait_resolution {
+ if *this_trait_resolution == current_result {
+ continue;
+ } else {
+ return AssocTypeShorthandResolution::Ambiguous {
+ sub_trait_resolution: Some(this_trait_resolution.clone()),
+ };
+ }
} else if let Some(prev_resolution) = &supertraits_resolution {
if let AssocTypeShorthandResolution::Ambiguous {
sub_trait_resolution: Some(prev_resolution),
@@ -2016,17 +2022,21 @@ fn type_alias_bounds_with_diagnostics<'db>(
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct GenericPredicates {
- // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait
- // predicate for the parent. Then come the bounds of the associated types of the parents,
- // then the explicit, self-only predicates for the parent, then the explicit, self-only trait
- // predicate for the child, then the bounds of the associated types of the child,
- // then the implicit trait predicate for the child, if `is_trait` is `true`.
+ // The order is the following:
+ //
+ // 1. If `has_trait_implied_predicate == true`, the implicit trait predicate.
+ // 2. The bounds of the associated types of the parents, coming from `Trait<Assoc: Trait>`.
+ // Note: associated type bounds from `Self::Assoc: Trait` on traits *won't* be included
+ // here, they are in 3.
+ // 3. The explicit, self-only predicates for the parent.
+ // 4. The explicit, self-only trait predicate for the child,
+ // 5. The bounds of the associated types of the child.
predicates: StoredEarlyBinder<StoredClauses>,
+ // Keep this ordered according to the above.
+ has_trait_implied_predicate: bool,
parent_explicit_self_predicates_start: u32,
own_predicates_start: u32,
own_assoc_ty_bounds_start: u32,
- is_trait: bool,
- parent_is_trait: bool,
}
#[salsa::tracked]
@@ -2065,11 +2075,10 @@ impl GenericPredicates {
let len = predicates.get().skip_binder().len() as u32;
Self {
predicates,
+ has_trait_implied_predicate: false,
parent_explicit_self_predicates_start: 0,
own_predicates_start: 0,
own_assoc_ty_bounds_start: len,
- is_trait: false,
- parent_is_trait: false,
}
}
@@ -2082,58 +2091,68 @@ impl GenericPredicates {
pub fn query_all<'db>(
db: &'db dyn HirDatabase,
def: GenericDefId,
- ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+ ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> {
Self::query(db, def).all_predicates()
}
#[inline]
- pub fn query_own<'db>(
+ pub fn query_own_explicit<'db>(
db: &'db dyn HirDatabase,
def: GenericDefId,
- ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
- Self::query(db, def).own_predicates()
+ ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> {
+ Self::query(db, def).own_explicit_predicates()
}
#[inline]
pub fn query_explicit<'db>(
db: &'db dyn HirDatabase,
def: GenericDefId,
- ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
+ ) -> EarlyBinder<'db, impl Iterator<Item = Clause<'db>>> {
Self::query(db, def).explicit_predicates()
}
#[inline]
- pub fn query_explicit_implied<'db>(
- db: &'db dyn HirDatabase,
- def: GenericDefId,
- ) -> EarlyBinder<'db, &'db [Clause<'db>]> {
- Self::query(db, def).explicit_implied_predicates()
+ pub fn all_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> {
+ self.predicates.get().map_bound(|it| it.as_slice().iter().copied())
}
#[inline]
- pub fn all_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> {
- self.predicates.get().map_bound(|it| it.as_slice())
+ pub fn own_explicit_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> {
+ self.predicates
+ .get()
+ .map_bound(|it| it.as_slice()[self.own_predicates_start as usize..].iter().copied())
}
#[inline]
- pub fn own_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> {
- self.predicates.get().map_bound(|it| &it.as_slice()[self.own_predicates_start as usize..])
+ pub fn explicit_predicates(&self) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> {
+ self.predicates.get().map_bound(|it| {
+ it.as_slice()[usize::from(self.has_trait_implied_predicate)..].iter().copied()
+ })
}
- /// Returns the predicates, minus the implicit `Self: Trait` predicate and bounds of the
- /// associated types for a trait.
#[inline]
- pub fn explicit_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> {
+ pub fn explicit_non_assoc_types_predicates(
+ &self,
+ ) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> {
self.predicates.get().map_bound(|it| {
- &it.as_slice()[self.parent_explicit_self_predicates_start as usize
+ it.as_slice()[self.parent_explicit_self_predicates_start as usize
..self.own_assoc_ty_bounds_start as usize]
+ .iter()
+ .copied()
})
}
#[inline]
- pub fn explicit_implied_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> {
- self.predicates.get().map_bound(|it| {
- &it.as_slice()[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)]
+ pub fn explicit_assoc_types_predicates(
+ &self,
+ ) -> EarlyBinder<'_, impl Iterator<Item = Clause<'_>>> {
+ self.predicates.get().map_bound(|predicates| {
+ let predicates = predicates.as_slice();
+ predicates[usize::from(self.has_trait_implied_predicate)
+ ..self.parent_explicit_self_predicates_start as usize]
+ .iter()
+ .copied()
+ .chain(predicates[self.own_assoc_ty_bounds_start as usize..].iter().copied())
})
}
}
@@ -2142,10 +2161,8 @@ 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_copied(),
- );
+ let clauses =
+ rustc_type_ir::elaborate::elaborate(interner, predicates.all_predicates().iter_identity());
let clauses = Clauses::new_from_iter(interner, clauses);
// FIXME: We should normalize projections here, like rustc does.
@@ -2290,42 +2307,28 @@ fn generic_predicates(db: &dyn HirDatabase, def: GenericDefId) -> (GenericPredic
let diagnostics = create_diagnostics(ctx.diagnostics);
- // The order is:
- //
- // 1. parent implicit trait pred
- // 2. parent assoc bounds
- // 3. parent self only preds
- // 4. own self only preds
- // 5. own assoc ty bounds
- // 6. own implicit trait pred
- //
- // The purpose of this is to index the slice of the followings, without making extra `Vec`s or
- // iterators:
- // - explicit self only predicates, of own or own + self
- // - explicit predicates, of own or own + self
let predicates = parent_implicit_trait_predicate
.iter()
+ .chain(own_implicit_trait_predicate.iter())
.chain(parent_assoc_ty_bounds.iter())
.chain(parent_predicates.iter())
.chain(own_predicates.iter())
.chain(own_assoc_ty_bounds.iter())
- .chain(own_implicit_trait_predicate.iter())
.copied()
.collect::<Vec<_>>();
- let parent_is_trait = parent_implicit_trait_predicate.is_some();
- let is_trait = own_implicit_trait_predicate.is_some();
+ let has_trait_implied_predicate =
+ parent_implicit_trait_predicate.is_some() || own_implicit_trait_predicate.is_some();
let parent_explicit_self_predicates_start =
- parent_is_trait as u32 + parent_assoc_ty_bounds.len() as u32;
+ has_trait_implied_predicate as u32 + parent_assoc_ty_bounds.len() as u32;
let own_predicates_start =
parent_explicit_self_predicates_start + parent_predicates.len() as u32;
let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32;
let predicates = GenericPredicates {
+ has_trait_implied_predicate,
parent_explicit_self_predicates_start,
own_predicates_start,
own_assoc_ty_bounds_start,
- is_trait,
- parent_is_trait,
predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()),
};
return (predicates, diagnostics);
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index 889f0792d3..4f70732178 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -214,10 +214,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
);
Ty::new_alias(
self.ctx.interner,
- AliasTyKind::Projection,
AliasTy::new_from_args(
self.ctx.interner,
- associated_ty.into(),
+ AliasTyKind::Projection { def_id: associated_ty.into() },
args,
),
)
@@ -949,10 +948,9 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
bound,
Ty::new_alias(
self.ctx.interner,
- AliasTyKind::Projection,
AliasTy::new_from_args(
self.ctx.interner,
- associated_ty.into(),
+ AliasTyKind::Projection { def_id: associated_ty.into() },
args,
),
),
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 05b9ea5d74..68c4833d81 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -219,7 +219,6 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
/// between multiple candidates. We otherwise treat them as ordinary inference
/// variable to avoid rejecting otherwise correct code.
#[derive(Debug)]
-#[expect(dead_code)]
pub(super) enum TreatNotYetDefinedOpaques {
AsInfer,
AsRigid,
@@ -324,7 +323,7 @@ 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_copied(interner, args.as_slice()),
+ bounds.iter_instantiated(interner, args.as_slice()),
ObligationCause::new(),
self.param_env,
);
diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs
index ec589085a8..94c70c29f7 100644
--- a/crates/hir-ty/src/method_resolution/confirm.rs
+++ b/crates/hir-ty/src/method_resolution/confirm.rs
@@ -136,7 +136,7 @@ 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_copied(self.interner(), filler_args.as_slice()),
+ .iter_instantiated(self.interner(), filler_args.as_slice()),
);
// Unify the (adjusted) self type with what the method expects.
@@ -509,7 +509,7 @@ 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_copied(self.interner(), all_args),
+ .iter_instantiated(self.interner(), all_args),
ObligationCause::new(),
self.ctx.table.param_env,
);
diff --git a/crates/hir-ty/src/method_resolution/probe.rs b/crates/hir-ty/src/method_resolution/probe.rs
index 8c76bfbc07..3604076ccd 100644
--- a/crates/hir-ty/src/method_resolution/probe.rs
+++ b/crates/hir-ty/src/method_resolution/probe.rs
@@ -1595,7 +1595,7 @@ 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_copied(self.interner(), impl_args.as_slice()),
+ impl_bounds.iter_instantiated(self.interner(), impl_args.as_slice()),
ObligationCause::new(),
self.param_env(),
);
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index a8865cd54e..a8e06f3a2b 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -6,8 +6,7 @@ use base_db::Crate;
use either::Either;
use hir_def::{
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
- expr_store::ExpressionStore,
- hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
+ hir::{BindingId, Expr, ExprId, Ordering, PatId},
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
use rustc_ast_ir::Mutability;
@@ -23,8 +22,8 @@ use crate::{
display::{DisplayTarget, HirDisplay},
infer::PointerCast,
next_solver::{
- Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, StoredConst, StoredGenericArgs,
- StoredTy, Ty, TyKind,
+ Allocation, AllocationData, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv,
+ StoredAllocation, StoredConst, StoredGenericArgs, StoredTy, Ty, TyKind,
infer::{InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
},
@@ -107,7 +106,13 @@ pub enum OperandKind {
/// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
Move(Place),
/// Constants are already semantically values, and remain unchanged.
- Constant { konst: StoredConst, ty: StoredTy },
+ Constant {
+ konst: StoredConst,
+ ty: StoredTy,
+ },
+ Allocation {
+ allocation: StoredAllocation,
+ },
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
/// handles it with the `Constant` variant somehow.
Static(StaticId),
@@ -115,11 +120,10 @@ pub enum OperandKind {
impl<'db> Operand {
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self {
- let interner = DbInterner::conjure();
Operand {
- kind: OperandKind::Constant {
- konst: Const::new_valtree(interner, ty, data, memory_map).store(),
- ty: ty.store(),
+ kind: OperandKind::Allocation {
+ allocation: Allocation::new(AllocationData { ty, memory: data, memory_map })
+ .store(),
},
span: None,
}
@@ -163,7 +167,6 @@ impl<V: PartialEq> ProjectionElem<V> {
infcx: &InferCtxt<'db>,
env: ParamEnv<'db>,
mut base: Ty<'db>,
- closure_field: impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db>,
krate: Crate,
) -> Ty<'db> {
let interner = infcx.interner;
@@ -218,7 +221,7 @@ impl<V: PartialEq> ProjectionElem<V> {
}
},
ProjectionElem::ClosureField(f) => match base.kind() {
- TyKind::Closure(id, subst) => closure_field(id.0, subst, *f),
+ TyKind::Closure(_, args) => args.as_closure().tupled_upvars_ty().tuple_fields()[*f],
_ => {
never!("Only closure has closure field");
Ty::new_error(interner, ErrorGuaranteed)
@@ -706,19 +709,31 @@ pub enum MutBorrowKind {
}
impl BorrowKind {
- fn from_hir(m: hir_def::type_ref::Mutability) -> Self {
+ fn from_hir_mutability(m: hir_def::type_ref::Mutability) -> Self {
match m {
hir_def::type_ref::Mutability::Shared => BorrowKind::Shared,
hir_def::type_ref::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
}
}
- fn from_rustc(m: rustc_ast_ir::Mutability) -> Self {
+ fn from_rustc_mutability(m: rustc_ast_ir::Mutability) -> Self {
match m {
rustc_ast_ir::Mutability::Not => BorrowKind::Shared,
rustc_ast_ir::Mutability::Mut => BorrowKind::Mut { kind: MutBorrowKind::Default },
}
}
+
+ fn from_hir(bk: crate::infer::closure::analysis::BorrowKind) -> Self {
+ match bk {
+ crate::closure_analysis::BorrowKind::Immutable => Self::Shared,
+ crate::closure_analysis::BorrowKind::UniqueImmutable => {
+ Self::Mut { kind: MutBorrowKind::ClosureCapture }
+ }
+ crate::closure_analysis::BorrowKind::Mutable => {
+ Self::Mut { kind: MutBorrowKind::Default }
+ }
+ }
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -1074,6 +1089,7 @@ pub struct MirBody {
pub start_block: BasicBlockId,
pub owner: DefWithBodyId,
pub binding_locals: ArenaMap<BindingId, LocalId>,
+ pub upvar_locals: FxHashMap<BindingId, Vec<(LocalId, crate::closure_analysis::Place)>>,
pub param_locals: Vec<LocalId>,
/// This field stores the closures directly owned by this body. It is used
/// in traversing every mir body.
@@ -1095,7 +1111,9 @@ impl MirBody {
OperandKind::Copy(p) | OperandKind::Move(p) => {
f(p, store);
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. }
+ | OperandKind::Static(_)
+ | OperandKind::Allocation { .. } => (),
}
}
for (_, block) in self.basic_blocks.iter_mut() {
@@ -1183,6 +1201,7 @@ impl MirBody {
start_block: _,
owner: _,
binding_locals,
+ upvar_locals,
param_locals,
closures,
projection_store,
@@ -1191,6 +1210,7 @@ impl MirBody {
basic_blocks.shrink_to_fit();
locals.shrink_to_fit();
binding_locals.shrink_to_fit();
+ upvar_locals.shrink_to_fit();
param_locals.shrink_to_fit();
closures.shrink_to_fit();
for (_, b) in basic_blocks.iter_mut() {
@@ -1208,20 +1228,6 @@ pub enum MirSpan {
SelfParam,
Unknown,
}
-
-impl MirSpan {
- pub fn is_ref_span(&self, store: &ExpressionStore) -> bool {
- match *self {
- MirSpan::ExprId(expr) => matches!(store[expr], Expr::Ref { .. }),
- // FIXME: Figure out if this is correct wrt. match ergonomics.
- MirSpan::BindingId(binding) => {
- matches!(store[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
- }
- MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
- }
- }
-}
-
impl_from!(ExprId, PatId for MirSpan);
impl From<&ExprId> for MirSpan {
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs
index 3ff2db15aa..17715d3fcd 100644
--- a/crates/hir-ty/src/mir/borrowck.rs
+++ b/crates/hir-ty/src/mir/borrowck.rs
@@ -8,17 +8,16 @@ use std::iter;
use hir_def::{DefWithBodyId, ExpressionStoreOwnerId, HasModule};
use la_arena::ArenaMap;
use rustc_hash::FxHashMap;
-use rustc_type_ir::inherent::GenericArgs as _;
use stdx::never;
use triomphe::Arc;
use crate::{
- InferenceResult,
- db::{HirDatabase, InternedClosure, InternedClosureId},
+ closure_analysis::ProjectionKind as HirProjectionKind,
+ db::{HirDatabase, InternedClosureId},
display::DisplayTarget,
mir::OperandKind,
next_solver::{
- DbInterner, GenericArgs, ParamEnv, StoredTy, Ty, TypingMode,
+ DbInterner, ParamEnv, StoredTy, Ty, TypingMode,
infer::{DbInternerInferExt, InferCtxt},
},
};
@@ -68,25 +67,49 @@ pub struct BorrowckResult {
fn all_mir_bodies(
db: &dyn HirDatabase,
def: DefWithBodyId,
- mut cb: impl FnMut(Arc<MirBody>),
-) -> Result<(), MirLowerError> {
+ 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,
c: InternedClosureId,
- cb: &mut impl FnMut(Arc<MirBody>),
+ results: &mut Vec<BorrowckResult>,
+ cb: &mut impl FnMut(Arc<MirBody>) -> BorrowckResult,
+ merge_from_closures: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult),
) -> Result<(), MirLowerError> {
match db.mir_body_for_closure(c) {
Ok(body) => {
- cb(body.clone());
- body.closures.iter().try_for_each(|&it| for_closure(db, it, cb))
+ let parent_index = results.len();
+ results.push(cb(body.clone()));
+ 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),
}
}
+
+ fn merge(
+ results: &mut [BorrowckResult],
+ merge: &mut impl FnMut(&mut BorrowckResult, &BorrowckResult),
+ 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 mut results = Vec::new();
match db.mir_body(def) {
Ok(body) => {
- cb(body.clone());
- body.closures.iter().try_for_each(|&it| for_closure(db, it, &mut cb))
+ results.push(cb(body.clone()));
+ 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())
}
Err(e) => Err(e),
}
@@ -100,34 +123,50 @@ pub fn borrowck_query(
let module = def.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
let env = db.trait_environment(ExpressionStoreOwnerId::from(def));
- let mut res = vec![];
// 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());
- all_mir_bodies(db, def, |body| {
- // FIXME(next-solver): Opaques.
- let infcx = interner.infer_ctxt().build(typing_mode);
- res.push(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,
- });
- })?;
- Ok(res.into())
-}
-
-fn make_fetch_closure_field<'db>(
- db: &'db dyn HirDatabase,
-) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> {
- |c: InternedClosureId, subst: GenericArgs<'db>, f: usize| {
- let InternedClosure(owner, _) = db.lookup_intern_closure(c);
- let interner = DbInterner::new_no_crate(db);
- let infer = InferenceResult::of(db, owner);
- let (captures, _) = infer.closure_info(c);
- let parent_subst = subst.as_closure().parent_args();
- captures.get(f).expect("broken closure field").ty.get().instantiate(interner, parent_subst)
- }
+ let res = all_mir_bodies(
+ db,
+ def,
+ |body| {
+ // 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,
+ }
+ },
+ |parent, child| {
+ 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 {
+ if !capture_place
+ .projections
+ .iter()
+ .any(|proj| matches!(proj.kind, HirProjectionKind::Deref))
+ {
+ let parent_mol = &mut parent.mutability_of_locals[parent_local];
+ match (&*parent_mol, &child.mutability_of_locals[*child_local]) {
+ (MutabilityReason::Mut { .. }, _) => {}
+ (_, MutabilityReason::Mut { .. }) => {
+ // FIXME: Fix the child spans.
+ *parent_mol = MutabilityReason::Mut { spans: Vec::new() }
+ }
+ (MutabilityReason::Not, _) => {}
+ (_, MutabilityReason::Not) => *parent_mol = MutabilityReason::Not,
+ (MutabilityReason::Unused, MutabilityReason::Unused) => {}
+ }
+ }
+ }
+ }
+ },
+ )?;
+ Ok(res)
}
fn moved_out_of_ref<'db>(
@@ -145,13 +184,7 @@ fn moved_out_of_ref<'db>(
if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
is_dereference_of_ref = true;
}
- ty = proj.projected_ty(
- infcx,
- env,
- ty,
- make_fetch_closure_field(db),
- body.owner.module(db).krate(db),
- );
+ ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db));
}
if is_dereference_of_ref
&& !infcx.type_is_copy_modulo_regions(env, ty)
@@ -160,7 +193,7 @@ fn moved_out_of_ref<'db>(
result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.store() });
}
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@@ -242,19 +275,13 @@ fn partially_moved<'db>(
OperandKind::Copy(p) | OperandKind::Move(p) => {
let mut ty: Ty<'db> = body.locals[p.local].ty.as_ref();
for proj in p.projection.lookup(&body.projection_store) {
- ty = proj.projected_ty(
- infcx,
- env,
- ty,
- make_fetch_closure_field(db),
- body.owner.module(db).krate(db),
- );
+ ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db));
}
if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() {
result.push(PartiallyMoved { span, ty: ty.store(), local: p.local });
}
}
- OperandKind::Constant { .. } | OperandKind::Static(_) => (),
+ OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {}
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@@ -397,13 +424,7 @@ fn place_case<'db>(
}
ProjectionElem::OpaqueCast(_) => (),
}
- ty = proj.projected_ty(
- infcx,
- env,
- ty,
- make_fetch_closure_field(db),
- body.owner.module(db).krate(db),
- );
+ ty = proj.projected_ty(infcx, env, ty, body.owner.module(db).krate(db));
}
if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct }
}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 505db1776f..80e429c4c8 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -21,7 +21,7 @@ use hir_expand::{InFile, mod_path::path, name::Name};
use intern::sym;
use la_arena::ArenaMap;
use macros::GenericTypeVisitable;
-use rustc_abi::TargetDataLayout;
+use rustc_abi::{Size, TargetDataLayout};
use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
@@ -40,14 +40,14 @@ use triomphe::Arc;
use crate::{
CallableDefId, ComplexMemoryMap, InferenceResult, MemoryMap, ParamEnvAndCrate,
consteval::{self, ConstEvalError, try_const_usize},
- db::{HirDatabase, InternedClosure, InternedClosureId},
+ db::{HirDatabase, InternedClosureId},
display::{ClosureStyle, DisplayTarget, HirDisplay},
infer::PointerCast,
layout::{Layout, LayoutError, RustcEnumVariantIdx},
method_resolution::{is_dyn_method, lookup_impl_const},
next_solver::{
- Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArgs, Region,
- StoredConst, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValueConst,
+ AliasTy, Allocation, AllocationData, Const, ConstKind, DbInterner, ErrorGuaranteed,
+ GenericArgs, Region, StoredTy, Ty, TyKind, TypingMode, UnevaluatedConst, ValTree,
infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
},
@@ -359,7 +359,7 @@ pub enum MirEvalError {
MirLowerErrorForClosure(InternedClosureId, MirLowerError),
TypeIsUnsized(StoredTy, &'static str),
NotSupported(String),
- InvalidConst(StoredConst),
+ InvalidConst,
InFunction(
Box<MirEvalError>,
Vec<(Either<FunctionId, InternedClosureId>, MirSpan, DefWithBodyId)>,
@@ -484,7 +484,7 @@ impl MirEvalError {
| MirEvalError::MirLowerErrorForClosure(_, _)
| MirEvalError::TypeIsUnsized(_, _)
| MirEvalError::NotSupported(_)
- | MirEvalError::InvalidConst(_)
+ | MirEvalError::InvalidConst
| MirEvalError::ExecutionLimitExceeded
| MirEvalError::StackOverflow
| MirEvalError::CoerceUnsizedError(_)
@@ -537,7 +537,7 @@ impl std::fmt::Debug for MirEvalError {
Self::InternalError(arg0) => f.debug_tuple("InternalError").field(arg0).finish(),
Self::InvalidVTableId(arg0) => f.debug_tuple("InvalidVTableId").field(arg0).finish(),
Self::NotSupported(arg0) => f.debug_tuple("NotSupported").field(arg0).finish(),
- Self::InvalidConst(arg0) => f.debug_tuple("InvalidConst").field(&arg0).finish(),
+ Self::InvalidConst => f.write_str("InvalidConst"),
Self::InFunction(e, stack) => {
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
}
@@ -606,10 +606,10 @@ pub fn interpret_mir<'db>(
// (and probably should) do better here, for example by excluding bindings outside of the target expression.
assert_placeholder_ty_is_unused: bool,
trait_env: Option<ParamEnvAndCrate<'db>>,
-) -> Result<'db, (Result<'db, Const<'db>>, MirOutput)> {
+) -> Result<'db, (Result<'db, Allocation<'db>>, MirOutput)> {
let ty = body.locals[return_slot()].ty.as_ref();
let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused, trait_env)?;
- let it: Result<'db, Const<'db>> = (|| {
+ let it: Result<'db, Allocation<'db>> = (|| {
if evaluator.ptr_size() != size_of::<usize>() {
not_supported!("targets with different pointer size from host");
}
@@ -620,7 +620,7 @@ pub fn interpret_mir<'db>(
ty,
&Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
)?;
- let bytes = bytes.into();
+ let bytes = Box::from(bytes);
let memory_map = if memory_map.memory.is_empty() && evaluator.vtable_map.is_empty() {
MemoryMap::Empty
} else {
@@ -628,7 +628,7 @@ pub fn interpret_mir<'db>(
memory_map.vtable.shrink_to_fit();
MemoryMap::Complex(Box::new(memory_map))
};
- Ok(Const::new_valtree(evaluator.interner(), ty, bytes, memory_map))
+ Ok(Allocation::new(AllocationData { ty, memory: bytes, memory_map }))
})();
Ok((it, MirOutput { stdout: evaluator.stdout, stderr: evaluator.stderr }))
}
@@ -731,24 +731,7 @@ impl<'db> Evaluator<'db> {
return *r;
}
let (ty, proj) = pair;
- let r = proj.projected_ty(
- &self.infcx,
- self.param_env.param_env,
- ty,
- |c, subst, f| {
- let InternedClosure(owner, _) = self.db.lookup_intern_closure(c);
- let infer = InferenceResult::of(self.db, owner);
- let (captures, _) = infer.closure_info(c);
- let parent_subst = subst.as_closure().parent_args();
- captures
- .get(f)
- .expect("broken closure field")
- .ty
- .get()
- .instantiate(self.interner(), parent_subst)
- },
- self.crate_id,
- );
+ let r = proj.projected_ty(&self.infcx, self.param_env.param_env, ty, self.crate_id);
self.projected_ty_cache.borrow_mut().insert((ty, proj), r);
r
}
@@ -898,6 +881,7 @@ impl<'db> Evaluator<'db> {
Ok(match &o.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => self.place_ty(p, locals)?,
OperandKind::Constant { konst: _, ty } => ty.as_ref(),
+ OperandKind::Allocation { allocation } => allocation.as_ref().ty,
&OperandKind::Static(s) => {
let ty = InferenceResult::of(self.db, DefWithBodyId::from(s))
.expr_ty(Body::of(self.db, s.into()).root_expr());
@@ -1927,19 +1911,152 @@ impl<'db> Evaluator<'db> {
OperandKind::Constant { konst, .. } => {
self.allocate_const_in_heap(locals, konst.as_ref())?
}
+ OperandKind::Allocation { allocation } => {
+ self.allocate_allocation_in_heap(locals, allocation.as_ref())?
+ }
})
}
+ fn allocate_valtree_in_heap(
+ &mut self,
+ ty: Ty<'db>,
+ valtree: ValTree<'db>,
+ ) -> Result<'db, Interval> {
+ match ty.kind() {
+ TyKind::Bool => {
+ let value = valtree.inner().to_leaf().try_to_bool().unwrap();
+ let addr = self.heap_allocate(1, 1)?;
+ self.write_memory(addr, &[u8::from(value)])?;
+ Ok(Interval::new(addr, 1))
+ }
+ TyKind::Char => {
+ let value = valtree.inner().to_leaf().to_u32();
+ let addr = self.heap_allocate(4, 4)?;
+ self.write_memory(addr, &value.to_le_bytes())?;
+ Ok(Interval::new(addr, 4))
+ }
+ TyKind::Int(int_ty) => {
+ let size = int_ty.bit_width().unwrap_or(self.ptr_size() as u64);
+ let value = valtree.inner().to_leaf().to_int(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::Uint(uint_ty) => {
+ let size = uint_ty.bit_width().unwrap_or(self.ptr_size() as u64);
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::Float(float_ty) => {
+ let size = float_ty.bit_width();
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size as usize, size as usize)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size as usize])?;
+ Ok(Interval::new(addr, size as usize))
+ }
+ TyKind::RawPtr(..) => {
+ let size = self.ptr_size();
+ let value = valtree.inner().to_leaf().to_uint(Size::from_bytes(size));
+ let addr = self.heap_allocate(size, size)?;
+ self.write_memory(addr, &value.to_le_bytes()[..size])?;
+ Ok(Interval::new(addr, size))
+ }
+ TyKind::Ref(_, inner_ty, _) => match inner_ty.kind() {
+ TyKind::Str => {
+ let bytes = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => Ok(value.value.inner().to_leaf().to_u8()),
+ _ => not_supported!("unsupported const"),
+ })
+ .collect::<Result<'_, Vec<_>>>()?;
+ let bytes_addr = self.heap_allocate(bytes.len(), 1)?;
+ self.write_memory(bytes_addr, &bytes)?;
+ let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
+ self.write_memory(ref_addr, &bytes_addr.to_bytes())?;
+ let mut len = [0; 16];
+ len[..size_of::<usize>()].copy_from_slice(&bytes.len().to_le_bytes());
+ self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
+ Ok(Interval::new(ref_addr, self.ptr_size() * 2))
+ }
+ TyKind::Slice(inner_ty) => {
+ let item_layout = self.layout(inner_ty)?;
+ let items = valtree
+ .inner()
+ .to_branch()
+ .iter()
+ .map(|konst| match konst.kind() {
+ ConstKind::Value(value) => {
+ self.allocate_valtree_in_heap(value.ty, value.value)
+ }
+ _ => not_supported!("unsupported const"),
+ })
+ .collect::<Result<'_, Vec<_>>>()?;
+ let items_addr = self.heap_allocate(
+ items.len() * (item_layout.size.bits() as usize),
+ item_layout.align.bits_usize(),
+ )?;
+ for (i, item) in items.iter().enumerate() {
+ self.copy_from_interval(
+ items_addr.offset(i * (item_layout.size.bits() as usize)),
+ *item,
+ )?;
+ }
+ let ref_addr = self.heap_allocate(self.ptr_size() * 2, self.ptr_size())?;
+ self.write_memory(ref_addr, &items_addr.to_bytes())?;
+ let mut len = [0; 16];
+ len[..size_of::<usize>()].copy_from_slice(&items.len().to_le_bytes());
+ self.write_memory(ref_addr.offset(self.ptr_size()), &len[..self.ptr_size()])?;
+ Ok(Interval::new(ref_addr, self.ptr_size() * 2))
+ }
+ TyKind::Dynamic(..) => not_supported!("`dyn Trait` consts not supported yet"),
+ _ => {
+ let inner_addr = self.allocate_valtree_in_heap(inner_ty, valtree)?;
+ let ref_addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
+ self.write_memory(ref_addr, &inner_addr.addr.to_bytes())?;
+ Ok(Interval::new(ref_addr, self.ptr_size()))
+ }
+ },
+ TyKind::Adt(_, _) | TyKind::Array(_, _) | TyKind::Tuple(_) => {
+ not_supported!(
+ "ADTs, arrays and tuples are unsupported in consts currently (requires `adt_const_params`)"
+ )
+ }
+ TyKind::Pat(_, _)
+ | TyKind::Slice(_)
+ | TyKind::FnDef(_, _)
+ | TyKind::Foreign(_)
+ | TyKind::Dynamic(_, _)
+ | TyKind::UnsafeBinder(..)
+ | TyKind::FnPtr(..)
+ | TyKind::Closure(_, _)
+ | TyKind::CoroutineClosure(_, _)
+ | TyKind::Coroutine(_, _)
+ | TyKind::CoroutineWitness(_, _)
+ | TyKind::Never
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Infer(_)
+ | TyKind::Str
+ | TyKind::Error(_) => not_supported!("unsupported const"),
+ }
+ }
+
#[allow(clippy::double_parens)]
fn allocate_const_in_heap(
&mut self,
locals: &Locals,
konst: Const<'db>,
) -> Result<'db, Interval> {
- let result_owner;
- let value = match konst.kind() {
- ConstKind::Value(value) => value,
- ConstKind::Unevaluated(UnevaluatedConst { def: const_id, args: subst }) => 'b: {
+ match konst.kind() {
+ ConstKind::Value(value) => self.allocate_valtree_in_heap(value.ty, value.value),
+ 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 {
@@ -1947,7 +2064,7 @@ impl<'db> Evaluator<'db> {
id = hir_def::GeneralConstId::ConstId(c);
subst = s;
}
- result_owner = match id {
+ let allocation = match id {
GeneralConstId::ConstId(const_id) => {
self.db.const_eval(const_id, subst, Some(self.param_env)).map_err(|e| {
let name = id.name(self.db);
@@ -1964,21 +2081,24 @@ impl<'db> Evaluator<'db> {
not_supported!("anonymous const evaluation")
}
};
- if let ConstKind::Value(value) = result_owner.kind() {
- break 'b value;
- }
- not_supported!("unevaluatable constant");
+ self.allocate_allocation_in_heap(locals, allocation)
}
_ => not_supported!("evaluating unknown const"),
- };
- let ValueConst { ty, value } = value;
- let ConstBytes { memory: v, memory_map } = value.inner();
+ }
+ }
+
+ fn allocate_allocation_in_heap(
+ &mut self,
+ locals: &Locals,
+ allocation: Allocation<'db>,
+ ) -> Result<'db, Interval> {
+ let AllocationData { ty, memory: ref v, ref memory_map } = *allocation;
let patch_map = memory_map.transform_addresses(|b, align| {
let addr = self.heap_allocate(b.len(), align)?;
self.write_memory(addr, b)?;
Ok(addr.to_usize())
})?;
- let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
+ let (size, align) = self.size_align_of(allocation.ty, locals)?.unwrap_or((v.len(), 1));
let v: Cow<'_, [u8]> = if size != v.len() {
// Handle self enum
if size == 16 && v.len() < 16 {
@@ -1986,7 +2106,7 @@ impl<'db> Evaluator<'db> {
} else if size < 16 && v.len() == 16 {
Cow::Borrowed(&v[0..size])
} else {
- return Err(MirEvalError::InvalidConst(konst.store()));
+ return Err(MirEvalError::InvalidConst);
}
} else {
Cow::Borrowed(v)
@@ -2211,10 +2331,10 @@ impl<'db> Evaluator<'db> {
match size {
Some((size, _)) => {
let addr_usize = from_bytes!(usize, bytes);
- mm.insert(
- addr_usize,
- this.read_memory(Address::from_usize(addr_usize), size)?.into(),
- )
+ let bytes =
+ this.read_memory(Address::from_usize(addr_usize), size)?.to_vec();
+ mm.insert(addr_usize, bytes.clone().into());
+ rec(this, &bytes, t, locals, mm, stack_depth_limit - 1)?;
}
None => {
let mut check_inner = None;
@@ -2340,7 +2460,7 @@ impl<'db> Evaluator<'db> {
}
AdtId::UnionId(_) => (),
},
- TyKind::Alias(AliasTyKind::Projection, _) => {
+ TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { .. }, .. }) => {
let mut ocx = ObligationCtxt::new(&this.infcx);
let ty = ocx
.structurally_normalize_ty(
@@ -2379,9 +2499,20 @@ impl<'db> Evaluator<'db> {
match size {
Some(_) => {
let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
- if let Some(it) = patch_map.get(&current) {
- self.write_memory(addr, &it.to_le_bytes())?;
- }
+ let patched = match patch_map.get(&current) {
+ Some(it) => {
+ self.write_memory(addr, &it.to_le_bytes())?;
+ *it
+ }
+ None => current,
+ };
+ self.patch_addresses(
+ patch_map,
+ ty_of_bytes,
+ Address::from_usize(patched),
+ t,
+ locals,
+ )?;
}
None => {
let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
@@ -2472,7 +2603,7 @@ impl<'db> Evaluator<'db> {
| TyKind::Error(_)
| TyKind::Placeholder(_)
| TyKind::Dynamic(_, _)
- | TyKind::Alias(_, _)
+ | TyKind::Alias(..)
| TyKind::Bound(_, _)
| TyKind::Infer(_)
| TyKind::Pat(_, _)
@@ -2829,10 +2960,10 @@ impl<'db> Evaluator<'db> {
};
let static_data = StaticSignature::of(self.db, st);
let result = if !static_data.flags.contains(StaticFlags::EXTERN) {
- let konst = self.db.const_eval_static(st).map_err(|e| {
+ let allocation = self.db.const_eval_static(st).map_err(|e| {
MirEvalError::ConstEvalError(static_data.name.as_str().to_owned(), Box::new(e))
})?;
- self.allocate_const_in_heap(locals, konst)?
+ self.allocate_allocation_in_heap(locals, allocation)?
} else {
let ty = InferenceResult::of(self.db, DefWithBodyId::from(st))
.expr_ty(Body::of(self.db, st.into()).root_expr());
@@ -2992,7 +3123,7 @@ impl<'db> Evaluator<'db> {
pub fn render_const_using_debug_impl<'db>(
db: &'db dyn HirDatabase,
owner: DefWithBodyId,
- c: Const<'db>,
+ c: Allocation<'db>,
ty: Ty<'db>,
) -> Result<'db, String> {
let mut evaluator = Evaluator::new(db, owner, false, None)?;
@@ -3003,7 +3134,7 @@ pub fn render_const_using_debug_impl<'db>(
.map_err(|_| MirEvalError::NotSupported("unreachable".to_owned()))?,
drop_flags: DropFlags::default(),
};
- let data = evaluator.allocate_const_in_heap(locals, c)?;
+ let data = evaluator.allocate_allocation_in_heap(locals, c)?;
let resolver = owner.resolver(db);
let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully(
db,
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index ff6c99ca53..9586d38abc 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -6,18 +6,16 @@ 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, IntoKind, SliceLike, Ty as _};
+use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _};
use stdx::never;
use crate::{
- InferenceResult,
display::DisplayTarget,
drop::{DropGlue, has_drop_glue},
mir::eval::{
- Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay,
- InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, Layout, Locals,
- Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported,
- pad16,
+ Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay, Interval,
+ IntervalAndTy, IntervalOrOwned, ItemContainerId, Layout, Locals, Lookup, MirEvalError,
+ MirSpan, Mutability, Result, Ty, TyKind, from_bytes, not_supported, pad16,
},
next_solver::Region,
};
@@ -147,19 +145,14 @@ impl<'db> Evaluator<'db> {
return destination
.write_from_interval(self, Interval { addr, size: destination.size });
}
- TyKind::Closure(id, subst) => {
- let [arg] = args else {
- not_supported!("wrong arg count for clone");
- };
- let addr = Address::from_bytes(arg.get(self)?)?;
- let InternedClosure(owner, _) = self.db.lookup_intern_closure(id.0);
- let infer = InferenceResult::of(self.db, owner);
- let (captures, _) = infer.closure_info(id.0);
- let layout = self.layout(self_ty)?;
- let db = self.db;
- let ty_iter = captures.iter().map(|c| c.ty(db, subst));
- self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
- }
+ TyKind::Closure(_, closure_args) => self.exec_clone(
+ def,
+ args,
+ closure_args.as_closure().tupled_upvars_ty(),
+ locals,
+ destination,
+ span,
+ )?,
TyKind::Tuple(subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
@@ -518,7 +511,7 @@ impl<'db> Evaluator<'db> {
"sched_getaffinity" => {
let [_pid, _set_size, set] = args else {
return Err(MirEvalError::InternalError(
- "libc::write args are not provided".into(),
+ "sched_getaffinity args are not provided".into(),
));
};
let set = Address::from_bytes(set.get(self)?)?;
@@ -530,9 +523,7 @@ impl<'db> Evaluator<'db> {
}
"getenv" => {
let [name] = args else {
- return Err(MirEvalError::InternalError(
- "libc::write args are not provided".into(),
- ));
+ return Err(MirEvalError::InternalError("getenv args are not provided".into()));
};
let mut name_buf = vec![];
let name = {
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index 44785d948a..0f0ed729c9 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -8,8 +8,9 @@ use hir_def::{
HasModule, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleId,
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{
- ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
- Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, generics::GenericParams,
+ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ClosureKind, ExprId, ExprOrPatId,
+ LabelId, Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread,
+ generics::GenericParams,
},
item_tree::FieldsShape,
lang_item::LangItems,
@@ -17,10 +18,11 @@ use hir_def::{
signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature},
};
use hir_expand::name::Name;
+use itertools::{EitherOrBoth, Itertools};
use la_arena::ArenaMap;
use rustc_apfloat::Float;
use rustc_hash::FxHashMap;
-use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _};
+use rustc_type_ir::inherent::{AdtDef, Const as _, GenericArgs as _, IntoKind, Ty as _};
use span::{Edition, FileId};
use syntax::TextRange;
use triomphe::Arc;
@@ -32,8 +34,11 @@ use crate::{
display::{DisplayTarget, HirDisplay, hir_display_with_store},
generics::generics,
infer::{
- CaptureKind, CapturedItem, TypeMismatch, cast::CastTy,
- closure::analysis::HirPlaceProjection,
+ CaptureSourceStack, CapturedPlace, TypeMismatch, UpvarCapture,
+ cast::CastTy,
+ closure::analysis::expr_use_visitor::{
+ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
+ },
},
inhabitedness::is_ty_uninhabited_from,
layout::LayoutError,
@@ -51,7 +56,6 @@ use crate::{
abi::Safety,
infer::{DbInternerInferExt, InferCtxt},
},
- traits::FnTrait,
};
use super::OperandKind;
@@ -303,6 +307,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
locals,
start_block,
binding_locals,
+ upvar_locals: FxHashMap::default(),
param_locals: vec![],
owner,
closures: vec![],
@@ -439,7 +444,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
else {
return Ok(None);
};
- let bk = BorrowKind::from_rustc(m);
+ let bk = BorrowKind::from_rustc_mutability(m);
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current))
}
@@ -956,7 +961,6 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
}
Expr::Await { .. } => not_supported!("await"),
Expr::Yeet { .. } => not_supported!("yeet"),
- Expr::Async { .. } => not_supported!("async block"),
&Expr::Const(_) => {
// let subst = self.placeholder_subst();
// self.lower_const(
@@ -996,7 +1000,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
let Some((p, current)) = self.lower_expr_as_place(current, *expr, true)? else {
return Ok(None);
};
- let bk = BorrowKind::from_hir(*mutability);
+ let bk = BorrowKind::from_hir_mutability(*mutability);
self.push_assignment(current, place, Rvalue::Ref(bk, p), expr_id.into());
Ok(Some(current))
}
@@ -1245,55 +1249,64 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
);
Ok(Some(current))
}
- Expr::Closure { .. } => {
+ Expr::Closure { closure_kind: ClosureKind::Closure, .. } => {
let ty = self.expr_ty_without_adjust(expr_id);
let TyKind::Closure(id, _) = ty.kind() else {
not_supported!("closure with non closure type");
};
self.result.closures.push(id.0);
- let (captures, _) = self.infer.closure_info(id.0);
- let mut operands = vec![];
- for capture in captures.iter() {
- let p = Place {
- local: self.binding_local(capture.place.local)?,
- projection: self.result.projection_store.intern(
- capture
- .place
- .projections
- .clone()
- .into_iter()
- .map(|it| match it {
- HirPlaceProjection::Deref => ProjectionElem::Deref,
- HirPlaceProjection::Field(field_id) => {
- ProjectionElem::Field(Either::Left(field_id))
- }
- HirPlaceProjection::TupleField(idx) => {
- ProjectionElem::Field(Either::Right(TupleFieldId {
- tuple: TupleId(!0), // Dummy as it's unused
- index: idx,
- }))
- }
- })
- .collect(),
- ),
+ let closure_data = &self.infer.closures_data[&id.0.loc(self.db).1];
+
+ let span = |sources: &[CaptureSourceStack]| match sources
+ .first()
+ .map(|it| it.final_source())
+ {
+ Some(ExprOrPatId::ExprId(it)) => it.into(),
+ Some(ExprOrPatId::PatId(it)) => it.into(),
+ None => MirSpan::Unknown,
+ };
+ let convert_place = |this: &mut Self, place: &HirPlace| {
+ let (HirPlaceBase::Local(local) | HirPlaceBase::Upvar { var_id: local, .. }) =
+ place.base
+ else {
+ not_supported!("non-local capture");
};
- match &capture.kind {
- CaptureKind::ByRef(bk) => {
- let tmp_ty = capture.ty.get().instantiate_identity();
+ Ok(Place {
+ local: this.binding_local(local)?,
+ projection: this
+ .result
+ .projection_store
+ .intern(convert_closure_capture_projections(self.db, place).collect()),
+ })
+ };
+
+ for (place, _, sources) in &closure_data.fake_reads {
+ let p = convert_place(self, place)?;
+ self.push_fake_read(current, p, span(sources));
+ }
+
+ let captures = closure_data.min_captures.values().flatten();
+ let mut operands = vec![];
+ for capture in captures {
+ let p = convert_place(self, &capture.place)?;
+ match capture.info.capture_kind {
+ UpvarCapture::ByRef(bk) => {
+ let tmp_ty = capture.captured_ty(self.db);
// FIXME: Handle more than one span.
- let capture_spans = capture.spans();
- let tmp: Place = self.temp(tmp_ty, current, capture_spans[0])?.into();
+ let capture_span = span(&capture.info.sources);
+ let tmp: Place = self.temp(tmp_ty, current, capture_span)?.into();
self.push_assignment(
current,
tmp,
- Rvalue::Ref(*bk, p),
- capture_spans[0],
+ Rvalue::Ref(BorrowKind::from_hir(bk), p),
+ capture_span,
);
operands.push(Operand { kind: OperandKind::Move(tmp), span: None });
}
- CaptureKind::ByValue => {
+ UpvarCapture::ByValue => {
operands.push(Operand { kind: OperandKind::Move(p), span: None })
}
+ UpvarCapture::ByUse => not_supported!("capture by use"),
}
}
self.push_assignment(
@@ -1304,6 +1317,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
);
Ok(Some(current))
}
+ Expr::Closure { closure_kind, .. } => not_supported!("{closure_kind:?} closure"),
Expr::Tuple { exprs } => {
let Some(values) = exprs
.iter()
@@ -1492,8 +1506,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
hir_def::hir::Literal::Float(f, _) => match size()? {
16 => Box::new(f.to_f128().to_bits().to_le_bytes()),
- 8 => Box::new(f.to_f64().to_le_bytes()),
- 4 => Box::new(f.to_f32().to_le_bytes()),
+ 8 => Box::new(f.to_f64().to_bits().to_le_bytes()),
+ 4 => Box::new(f.to_f32().to_bits().to_le_bytes()),
2 => Box::new(u16::try_from(f.to_f16().to_bits()).unwrap().to_le_bytes()),
_ => {
return Err(MirLowerError::TypeError(
@@ -1527,31 +1541,14 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
subst: GenericArgs<'db>,
const_id: GeneralConstId,
) -> Result<'db, Operand> {
- let konst = if !subst.is_empty() {
- // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering.
- Const::new_unevaluated(
- self.interner(),
- UnevaluatedConst { def: const_id.into(), args: subst },
- )
- } else {
- match const_id {
- id @ GeneralConstId::ConstId(const_id) => {
- self.db.const_eval(const_id, subst, None).map_err(|e| {
- let name = id.name(self.db);
- MirLowerError::ConstEvalError(name.into(), Box::new(e))
- })?
- }
- GeneralConstId::StaticId(static_id) => {
- self.db.const_eval_static(static_id).map_err(|e| {
- let name = const_id.name(self.db);
- MirLowerError::ConstEvalError(name.into(), Box::new(e))
- })?
- }
- GeneralConstId::AnonConstId(_) => {
- return Err(MirLowerError::IncompleteExpr);
- }
- }
- };
+ 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 {
@@ -2084,6 +2081,44 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
}
}
+fn convert_closure_capture_projections(
+ db: &dyn HirDatabase,
+ place: &HirPlace,
+) -> impl Iterator<Item = PlaceElem> {
+ place.projections.iter().enumerate().map(|(i, proj)| match proj.kind {
+ HirProjectionKind::Deref => ProjectionElem::Deref,
+ HirProjectionKind::Field { field_idx, variant_idx } => {
+ let ty = place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => {
+ ProjectionElem::Field(Either::Right(TupleFieldId {
+ tuple: TupleId(!0), // Dummy as it's unused
+ index: field_idx,
+ }))
+ }
+ TyKind::Adt(adt_def, _) => {
+ let local_field_id = LocalFieldId::from_raw(RawIdx::from_u32(field_idx));
+ let field = match adt_def.def_id().0 {
+ AdtId::StructId(id) => {
+ FieldId { parent: id.into(), local_id: local_field_id }
+ }
+ AdtId::UnionId(id) => {
+ FieldId { parent: id.into(), local_id: local_field_id }
+ }
+ AdtId::EnumId(id) => {
+ let variant = id.enum_variants(db).variants[variant_idx as usize].0;
+ FieldId { parent: variant.into(), local_id: local_field_id }
+ }
+ };
+ ProjectionElem::Field(Either::Left(field))
+ }
+ _ => panic!("unexpected type"),
+ }
+ }
+ _ => panic!("unexpected projection"),
+ })
+}
+
fn cast_kind<'db>(
db: &'db dyn HirDatabase,
source_ty: Ty<'db>,
@@ -2110,7 +2145,7 @@ pub fn mir_body_for_closure_query<'db>(
db: &'db dyn HirDatabase,
closure: InternedClosureId,
) -> Result<'db, Arc<MirBody>> {
- let InternedClosure(owner, expr) = db.lookup_intern_closure(closure);
+ 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);
@@ -2121,20 +2156,22 @@ pub fn mir_body_for_closure_query<'db>(
let crate::next_solver::TyKind::Closure(_, substs) = infer.expr_ty(expr).kind() else {
implementation_error!("closure expression is not closure");
};
- let (captures, kind) = infer.closure_info(closure);
+ 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);
+
// 0 is return local
ctx.result.locals.alloc(Local { ty: infer.expr_ty(*root).store() });
let closure_local = ctx.result.locals.alloc(Local {
ty: match kind {
- FnTrait::FnOnce | FnTrait::AsyncFnOnce => infer.expr_ty(expr),
- FnTrait::FnMut | FnTrait::AsyncFnMut => Ty::new_ref(
+ rustc_type_ir::ClosureKind::FnOnce => infer.expr_ty(expr),
+ rustc_type_ir::ClosureKind::FnMut => Ty::new_ref(
ctx.interner(),
Region::error(ctx.interner()),
infer.expr_ty(expr),
Mutability::Mut,
),
- FnTrait::Fn | FnTrait::AsyncFn => Ty::new_ref(
+ rustc_type_ir::ClosureKind::Fn => Ty::new_ref(
ctx.interner(),
Region::error(ctx.interner()),
infer.expr_ty(expr),
@@ -2144,6 +2181,7 @@ pub fn mir_body_for_closure_query<'db>(
.store(),
});
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 current = ctx.lower_params_and_bindings(
@@ -2151,60 +2189,101 @@ pub fn mir_body_for_closure_query<'db>(
None,
|_| true,
)?;
+
+ // Push local for every upvar in the closure. rustc doesn't do that, but we have to so we have locals
+ // to associate with upvars for borrowck.
+ let is_by_ref_closure = match kind {
+ rustc_type_ir::ClosureKind::Fn | rustc_type_ir::ClosureKind::FnMut => true,
+ rustc_type_ir::ClosureKind::FnOnce => false,
+ };
+ let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedPlace, LocalId)>> = FxHashMap::default();
+ for (capture_idx, capture) in captures.enumerate() {
+ let capture_local = ctx.result.locals.alloc(Local { ty: capture.captured_ty(db).store() });
+ ctx.push_storage_live_for_local(capture_local, current, MirSpan::Unknown)?;
+ let mut projections = Vec::with_capacity(usize::from(is_by_ref_closure) + 1);
+ if is_by_ref_closure {
+ projections.push(ProjectionElem::Deref);
+ }
+ projections.push(ProjectionElem::ClosureField(capture_idx));
+ let capture_param_place = Place {
+ local: closure_local,
+ projection: ctx.result.projection_store.intern(projections.into_boxed_slice()),
+ };
+ let capture_local_place = Place {
+ local: capture_local,
+ projection: ctx.result.projection_store.intern(Box::new([])),
+ };
+ let capture_local_rvalue =
+ Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None });
+ ctx.push_assignment(current, capture_local_place, capture_local_rvalue, MirSpan::Unknown);
+
+ let local = capture.captured_local();
+ let local = ctx.binding_local(local)?;
+ upvar_map.entry(local).or_default().push((capture, capture_local));
+
+ ctx.result
+ .upvar_locals
+ .entry(capture.captured_local())
+ .or_default()
+ .push((capture_local, capture.place.clone()));
+ }
+
ctx.resolver.reset_to_guard(resolver_guard);
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
}
- let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
- for (i, capture) in captures.iter().enumerate() {
- let local = ctx.binding_local(capture.place.local)?;
- upvar_map.entry(local).or_default().push((capture, i));
- }
+
let mut err = None;
- let closure_local = ctx.result.locals.iter().nth(1).unwrap().0;
- let closure_projection = match kind {
- FnTrait::FnOnce | FnTrait::AsyncFnOnce => vec![],
- FnTrait::FnMut | FnTrait::Fn | FnTrait::AsyncFnMut | FnTrait::AsyncFn => {
- vec![ProjectionElem::Deref]
- }
- };
- ctx.result.walk_places(|p, store| {
- if let Some(it) = upvar_map.get(&p.local) {
- let r = it.iter().find(|it| {
- if p.projection.lookup(store).len() < it.0.place.projections.len() {
- return false;
- }
- for (it, y) in p.projection.lookup(store).iter().zip(it.0.place.projections.iter())
- {
- match (it, y) {
- (ProjectionElem::Deref, HirPlaceProjection::Deref) => (),
- (ProjectionElem::Field(Either::Left(it)), HirPlaceProjection::Field(y))
- if it == y => {}
- (
- ProjectionElem::Field(Either::Right(it)),
- HirPlaceProjection::TupleField(y),
- ) if it.index == *y => (),
- _ => return false,
+ ctx.result.walk_places(|mir_place, store| {
+ let mir_projections = mir_place.projection.lookup(store);
+ if let Some(hir_places) = upvar_map.get(&mir_place.local) {
+ let projections = hir_places.iter().find_map(|hir_place| {
+ let iter = mir_projections
+ .iter()
+ .cloned()
+ .zip_longest(convert_closure_capture_projections(db, &hir_place.0.place))
+ .enumerate();
+
+ for (idx, item) in iter {
+ match item {
+ EitherOrBoth::Both(mir, hir) => {
+ if mir != hir {
+ // Not this place.
+ return None;
+ }
+ }
+ EitherOrBoth::Right(_) => {
+ // FIXME: This can happen in fake reads. I believe this is a bug. So we change the fake read's meaning.
+ // never!(
+ // "mir upvar place shorter than hir upvar place; this should not happen, \
+ // capture analysis should have picked the shorter place"
+ // );
+ // return None;
+ return Some((mir_projections.len(), hir_place));
+ }
+ // This place, but truncated.
+ EitherOrBoth::Left(_) => return Some((idx, hir_place)),
}
}
- true
+ // Exactly this place.
+ Some((hir_place.0.place.projections.len(), hir_place))
});
- match r {
- Some(it) => {
- p.local = closure_local;
- let mut next_projs = closure_projection.clone();
- next_projs.push(PlaceElem::ClosureField(it.1));
- let prev_projs = p.projection;
- if it.0.kind != CaptureKind::ByValue {
- next_projs.push(ProjectionElem::Deref);
- }
- next_projs.extend(
- prev_projs.lookup(store).iter().skip(it.0.place.projections.len()).cloned(),
+ match projections {
+ Some((skip_projections_up_to, (hir_place, upvar_local))) => {
+ mir_place.local = *upvar_local;
+ let mut result_projections = Vec::with_capacity(
+ usize::from(hir_place.is_by_ref())
+ + (mir_projections.len() - skip_projections_up_to),
);
- p.projection = store.intern(next_projs.into());
+ if hir_place.is_by_ref() {
+ result_projections.push(ProjectionElem::Deref);
+ }
+ result_projections
+ .extend(mir_projections[skip_projections_up_to..].iter().cloned());
+ mir_place.projection = store.intern(result_projections.into());
}
- None => err = Some(*p),
+ None => err = Some(*mir_place),
}
}
});
diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs
index 17dc95fb24..fb4a9add81 100644
--- a/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/crates/hir-ty/src/mir/lower/as_place.rs
@@ -10,12 +10,6 @@ use crate::{
next_solver::Region,
};
-macro_rules! not_supported {
- ($it: expr) => {
- return Err(MirLowerError::NotSupported(format!($it)))
- };
-}
-
impl<'db> MirLowerCtx<'_, 'db> {
fn lower_expr_to_some_place_without_adjust(
&mut self,
@@ -98,11 +92,8 @@ impl<'db> MirLowerCtx<'_, 'db> {
last.target.as_ref(),
expr_id.into(),
match od.0 {
- Some(Mutability::Mut) => true,
- Some(Mutability::Not) => false,
- None => {
- not_supported!("implicit overloaded deref with unknown mutability")
- }
+ Mutability::Mut => true,
+ Mutability::Not => false,
},
)
}
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index 5752a3d7fa..41044f00c2 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -16,7 +16,10 @@ use triomphe::Arc;
use crate::{
ParamEnvAndCrate,
- next_solver::{Const, ConstKind, Region, RegionKind, StoredConst, StoredGenericArgs, StoredTy},
+ next_solver::{
+ Allocation, AllocationData, Const, ConstKind, Region, RegionKind, StoredConst,
+ StoredGenericArgs, StoredTy,
+ },
traits::StoredParamEnvAndCrate,
};
use crate::{
@@ -138,6 +141,18 @@ impl<'db> Filler<'db> {
self.fill_const(konst)?;
self.fill_ty(ty)?;
}
+ OperandKind::Allocation { allocation } => {
+ let alloc = allocation.as_ref();
+ let mut ty = alloc.ty.store();
+ self.fill_ty(&mut ty)?;
+ *allocation = Allocation::new(AllocationData {
+ ty: ty.as_ref(),
+ memory: alloc.memory.clone(),
+ // FIXME: Do we need to fill the memory map too?
+ memory_map: alloc.memory_map.clone(),
+ })
+ .store();
+ }
OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (),
}
Ok(())
diff --git a/crates/hir-ty/src/mir/pretty.rs b/crates/hir-ty/src/mir/pretty.rs
index 4b654a0fbe..de5ee223a1 100644
--- a/crates/hir-ty/src/mir/pretty.rs
+++ b/crates/hir-ty/src/mir/pretty.rs
@@ -387,6 +387,9 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> {
w!(self, "Const({})", self.hir_display(&konst.as_ref()))
}
OperandKind::Static(s) => w!(self, "Static({:?})", s),
+ OperandKind::Allocation { allocation } => {
+ w!(self, "Allocation({})", self.hir_display(&allocation.as_ref()))
+ }
}
}
diff --git a/crates/hir-ty/src/next_solver.rs b/crates/hir-ty/src/next_solver.rs
index 605e31404c..161a3142df 100644
--- a/crates/hir-ty/src/next_solver.rs
+++ b/crates/hir-ty/src/next_solver.rs
@@ -5,6 +5,7 @@
// incorrect lifetime here.
pub mod abi;
+mod allocation;
mod binder;
mod consts;
mod def_id;
@@ -29,6 +30,7 @@ pub mod util;
use std::{mem::ManuallyDrop, sync::OnceLock};
+pub use allocation::*;
pub use binder::*;
pub use consts::*;
pub use def_id::*;
@@ -89,6 +91,7 @@ pub struct DefaultTypes<'db> {
pub struct DefaultConsts<'db> {
pub error: Const<'db>,
+ pub u8_values: [Const<'db>; 256],
}
pub struct DefaultRegions<'db> {
@@ -101,7 +104,7 @@ pub struct DefaultEmpty<'db> {
pub tys: Tys<'db>,
pub generic_args: GenericArgs<'db>,
pub bound_var_kinds: BoundVarKinds<'db>,
- pub canonical_vars: CanonicalVars<'db>,
+ pub canonical_vars: CanonicalVarKinds<'db>,
pub variances: VariancesOf<'db>,
pub pat_list: PatList<'db>,
pub predefined_opaques: PredefinedOpaques<'db>,
@@ -109,6 +112,7 @@ pub struct DefaultEmpty<'db> {
pub bound_existential_predicates: BoundExistentialPredicates<'db>,
pub clauses: Clauses<'db>,
pub region_assumptions: RegionAssumptions<'db>,
+ pub consts: Consts<'db>,
}
pub struct DefaultAny<'db> {
@@ -167,7 +171,7 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
ty.as_ref()
};
let create_canonical_vars = |slice| {
- let ty = CanonicalVars::new_from_slice(slice);
+ let ty = CanonicalVarKinds::new_from_slice(slice);
// We need to increase the refcount (forever), so that the types won't be freed.
let ty = ManuallyDrop::new(ty.store());
ty.as_ref()
@@ -220,15 +224,22 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
let ty = ManuallyDrop::new(ty.store());
ty.as_ref()
};
+ let create_consts = |slice| {
+ let ty = Consts::new_from_slice(slice);
+ // We need to increase the refcount (forever), so that the types won't be freed.
+ let ty = ManuallyDrop::new(ty.store());
+ ty.as_ref()
+ };
let str = create_ty(TyKind::Str);
let statik = create_region(RegionKind::ReStatic);
let empty_tys = create_tys(&[]);
let unit = create_ty(TyKind::Tuple(empty_tys));
+ let u8 = create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8));
DefaultAny {
types: DefaultTypes {
usize: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::Usize)),
- u8: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U8)),
+ u8,
u16: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U16)),
u32: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U32)),
u64: create_ty(TyKind::Uint(rustc_ast_ir::UintTy::U64)),
@@ -252,7 +263,15 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
static_str_ref: create_ty(TyKind::Ref(statik, str, rustc_ast_ir::Mutability::Not)),
mut_unit_ptr: create_ty(TyKind::RawPtr(unit, rustc_ast_ir::Mutability::Mut)),
},
- consts: DefaultConsts { error: create_const(ConstKind::Error(ErrorGuaranteed)) },
+ consts: DefaultConsts {
+ error: create_const(ConstKind::Error(ErrorGuaranteed)),
+ u8_values: std::array::from_fn(|u8_value| {
+ create_const(ConstKind::Value(ValueConst {
+ ty: u8,
+ value: ValTree::new(ValTreeKind::Leaf(ScalarInt::from(u8_value as u8))),
+ }))
+ }),
+ },
regions: DefaultRegions {
error: create_region(RegionKind::ReError(ErrorGuaranteed)),
statik,
@@ -270,11 +289,12 @@ pub fn default_types<'a, 'db>(db: &'db dyn HirDatabase) -> &'a DefaultAny<'db> {
bound_existential_predicates: create_bound_existential_predicates(&[]),
clauses: create_clauses(&[]),
region_assumptions: create_region_assumptions(&[]),
+ consts: create_consts(&[]),
},
one_invariant: create_variances_of(&[rustc_type_ir::Variance::Invariant]),
one_covariant: create_variances_of(&[rustc_type_ir::Variance::Covariant]),
coroutine_captures_by_ref_bound_var_kinds: create_bound_var_kinds(&[
- BoundVarKind::Region(BoundRegionKind::ClosureEnv),
+ BoundVariableKind::Region(BoundRegionKind::ClosureEnv),
]),
}
})
diff --git a/crates/hir-ty/src/next_solver/allocation.rs b/crates/hir-ty/src/next_solver/allocation.rs
new file mode 100644
index 0000000000..d299c89c12
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/allocation.rs
@@ -0,0 +1,73 @@
+use std::{fmt, hash::Hash};
+
+use intern::{Interned, InternedRef, impl_internable};
+use macros::GenericTypeVisitable;
+use rustc_type_ir::GenericTypeVisitable;
+
+use crate::{
+ MemoryMap,
+ next_solver::{Ty, impl_stored_interned},
+};
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Allocation<'db> {
+ interned: InternedRef<'db, AllocationInterned>,
+}
+
+impl<'db> Allocation<'db> {
+ pub fn new(data: AllocationData<'db>) -> Self {
+ let data =
+ unsafe { std::mem::transmute::<AllocationData<'db>, AllocationData<'static>>(data) };
+ Self { interned: Interned::new_gc(AllocationInterned(data)) }
+ }
+}
+
+impl<'db> std::ops::Deref for Allocation<'db> {
+ type Target = AllocationData<'db>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ let inner = &self.interned.0;
+ unsafe { std::mem::transmute::<&AllocationData<'static>, &AllocationData<'db>>(inner) }
+ }
+}
+
+impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Allocation<'db> {
+ fn generic_visit_with(&self, visitor: &mut V) {
+ if visitor.on_interned(self.interned).is_continue() {
+ (**self).generic_visit_with(visitor);
+ }
+ }
+}
+
+impl fmt::Debug for Allocation<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let AllocationData { ty, memory, memory_map } = &**self;
+ f.debug_struct("Allocation")
+ .field("ty", ty)
+ .field("memory", memory)
+ .field("memory_map", memory_map)
+ .finish()
+ }
+}
+
+#[derive(PartialEq, Eq, Hash, GenericTypeVisitable)]
+pub(super) struct AllocationInterned(AllocationData<'static>);
+
+#[derive(Debug, PartialEq, Eq, GenericTypeVisitable)]
+pub struct AllocationData<'db> {
+ pub ty: Ty<'db>,
+ pub memory: Box<[u8]>,
+ pub memory_map: MemoryMap<'db>,
+}
+
+impl<'db> Hash for AllocationData<'db> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ let Self { ty, memory, memory_map: _ } = self;
+ ty.hash(state);
+ memory.hash(state);
+ }
+}
+
+impl_internable!(gc; AllocationInterned);
+impl_stored_interned!(AllocationInterned, Allocation, StoredAllocation);
diff --git a/crates/hir-ty/src/next_solver/consts.rs b/crates/hir-ty/src/next_solver/consts.rs
index 9643f1ba4c..fa90e3d8a0 100644
--- a/crates/hir-ty/src/next_solver/consts.rs
+++ b/crates/hir-ty/src/next_solver/consts.rs
@@ -1,5 +1,7 @@
//! Things related to consts in the next-trait-solver.
+mod valtree;
+
use std::hash::Hash;
use hir_def::ConstParamId;
@@ -9,17 +11,19 @@ use rustc_ast_ir::visit::VisitorResult;
use rustc_type_ir::{
BoundVar, BoundVarIndexKind, ConstVid, DebruijnIndex, FlagComputation, Flags,
GenericTypeVisitable, InferConst, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
- TypeVisitable, TypeVisitableExt, WithCachedTypeInfo,
- inherent::{IntoKind, ParamEnv as _, PlaceholderLike, SliceLike},
- relate::Relate,
+ TypeVisitable, WithCachedTypeInfo, inherent::IntoKind, relate::Relate,
};
use crate::{
- MemoryMap,
- next_solver::{ClauseKind, ParamEnv, impl_stored_interned},
+ ParamEnvAndCrate,
+ next_solver::{
+ AllocationData, impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
+ },
};
-use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder, Ty};
+use super::{DbInterner, ErrorGuaranteed, GenericArgs, Ty};
+
+pub use self::valtree::*;
pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
@@ -71,26 +75,33 @@ impl<'db> Const<'db> {
Const::new(interner, ConstKind::Param(param))
}
- pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst) -> Self {
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderConst<'db>) -> Self {
Const::new(interner, ConstKind::Placeholder(placeholder))
}
- pub fn new_bound(interner: DbInterner<'db>, index: DebruijnIndex, bound: BoundConst) -> Self {
+ pub fn new_bound(
+ interner: DbInterner<'db>,
+ index: DebruijnIndex,
+ bound: BoundConst<'db>,
+ ) -> Self {
Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(index), bound))
}
- pub fn new_valtree(
+ pub fn new_valtree(interner: DbInterner<'db>, ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
+ Const::new(interner, ConstKind::Value(ValueConst { ty, value: ValTree::new(kind) }))
+ }
+
+ pub fn new_from_allocation(
interner: DbInterner<'db>,
- ty: Ty<'db>,
- memory: Box<[u8]>,
- memory_map: MemoryMap<'db>,
+ allocation: &AllocationData<'db>,
+ param_env: ParamEnvAndCrate<'db>,
) -> Self {
- Const::new(
+ allocation_to_const(
interner,
- ConstKind::Value(ValueConst {
- ty,
- value: Valtree::new(ConstBytes { memory, memory_map }),
- }),
+ allocation.ty,
+ &allocation.memory,
+ &allocation.memory_map,
+ param_env,
)
}
@@ -120,7 +131,7 @@ impl<'db> std::fmt::Debug for Const<'db> {
}
}
-pub type PlaceholderConst = Placeholder<BoundConst>;
+pub type PlaceholderConst<'db> = rustc_type_ir::PlaceholderConst<DbInterner<'db>>;
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
pub struct ParamConst {
@@ -135,126 +146,6 @@ 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.caller_bounds().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
- }
-}
-
-/// A type-level constant value.
-///
-/// Represents a typed, fully evaluated constant.
-#[derive(
- Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable,
-)]
-pub struct ValueConst<'db> {
- pub ty: Ty<'db>,
- // FIXME: Should we ignore this for TypeVisitable, TypeFoldable?
- #[type_visitable(ignore)]
- #[type_foldable(identity)]
- pub value: Valtree<'db>,
-}
-
-impl<'db> ValueConst<'db> {
- pub fn new(ty: Ty<'db>, bytes: ConstBytes<'db>) -> Self {
- let value = Valtree::new(bytes);
- ValueConst { ty, value }
- }
-}
-
-impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
- fn ty(self) -> Ty<'db> {
- self.ty
- }
-
- fn valtree(self) -> Valtree<'db> {
- self.value
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, GenericTypeVisitable)]
-pub struct ConstBytes<'db> {
- pub memory: Box<[u8]>,
- pub memory_map: MemoryMap<'db>,
-}
-
-impl Hash for ConstBytes<'_> {
- fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
- self.memory.hash(state)
- }
-}
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-pub struct Valtree<'db> {
- interned: InternedRef<'db, ValtreeInterned>,
-}
-
-impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Valtree<'db> {
- fn generic_visit_with(&self, visitor: &mut V) {
- if visitor.on_interned(self.interned).is_continue() {
- self.inner().generic_visit_with(visitor);
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)]
-pub(super) struct ValtreeInterned(ConstBytes<'static>);
-
-impl_internable!(gc; ValtreeInterned);
-
-const _: () = {
- const fn is_copy<T: Copy>() {}
- is_copy::<Valtree<'static>>();
-};
-
-impl<'db> Valtree<'db> {
- #[inline]
- pub fn new(bytes: ConstBytes<'db>) -> Self {
- let bytes = unsafe { std::mem::transmute::<ConstBytes<'db>, ConstBytes<'static>>(bytes) };
- Self { interned: Interned::new_gc(ValtreeInterned(bytes)) }
- }
-
- #[inline]
- pub fn inner(&self) -> &ConstBytes<'db> {
- let inner = &self.interned.0;
- unsafe { std::mem::transmute::<&ConstBytes<'static>, &ConstBytes<'db>>(inner) }
- }
-}
-
-impl std::fmt::Debug for Valtree<'_> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.interned.fmt(f)
- }
-}
-
#[derive(
Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable, GenericTypeVisitable,
)]
@@ -388,25 +279,22 @@ impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
Const::new(interner, ConstKind::Infer(InferConst::Var(var)))
}
- fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst) -> Self {
+ fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundConst<'db>) -> Self {
Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
}
fn new_anon_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundVar) -> Self {
Const::new(
interner,
- ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst { var }),
+ ConstKind::Bound(BoundVarIndexKind::Bound(debruijn), BoundConst::new(var)),
)
}
fn new_canonical_bound(interner: DbInterner<'db>, var: BoundVar) -> Self {
- Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst { var }))
+ Const::new(interner, ConstKind::Bound(BoundVarIndexKind::Canonical, BoundConst::new(var)))
}
- fn new_placeholder(
- interner: DbInterner<'db>,
- param: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderConst,
- ) -> Self {
+ fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderConst<'db>) -> Self {
Const::new(interner, ConstKind::Placeholder(param))
}
@@ -426,43 +314,7 @@ impl<'db> rustc_type_ir::inherent::Const<DbInterner<'db>> for Const<'db> {
}
}
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-pub struct BoundConst {
- pub var: BoundVar,
-}
-
-impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundConst {
- fn var(self) -> BoundVar {
- self.var
- }
-
- fn assert_eq(self, var: BoundVarKind) {
- var.expect_const()
- }
-}
-
-impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst {
- type Bound = BoundConst;
-
- fn universe(self) -> rustc_type_ir::UniverseIndex {
- self.universe
- }
-
- fn var(self) -> rustc_type_ir::BoundVar {
- self.bound.var
- }
-
- fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
- Placeholder { universe: ui, bound: self.bound }
- }
-
- fn new(ui: rustc_type_ir::UniverseIndex, var: BoundConst) -> Self {
- Placeholder { universe: ui, bound: var }
- }
- fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
- Placeholder { universe: ui, bound: BoundConst { var } }
- }
-}
+pub type BoundConst<'db> = rustc_type_ir::BoundConst<DbInterner<'db>>;
impl<'db> Relate<DbInterner<'db>> for ExprConst {
fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
@@ -483,3 +335,6 @@ impl<'db> rustc_type_ir::inherent::ExprConst<DbInterner<'db>> for ExprConst {
GenericArgs::default()
}
}
+
+interned_slice!(ConstsStorage, Consts, StoredConsts, consts, Const<'db>, Const<'static>);
+impl_foldable_for_interned_slice!(Consts);
diff --git a/crates/hir-ty/src/next_solver/consts/valtree.rs b/crates/hir-ty/src/next_solver/consts/valtree.rs
new file mode 100644
index 0000000000..b856ee5a85
--- /dev/null
+++ b/crates/hir-ty/src/next_solver/consts/valtree.rs
@@ -0,0 +1,712 @@
+use std::{fmt, hash::Hash, num::NonZero};
+
+use intern::{Interned, InternedRef, impl_internable};
+use macros::{GenericTypeVisitable, TypeFoldable, TypeVisitable};
+use rustc_abi::{Size, TargetDataLayout};
+use rustc_type_ir::{GenericTypeVisitable, TypeFoldable, TypeVisitable, inherent::IntoKind};
+use stdx::never;
+
+use crate::{
+ MemoryMap, ParamEnvAndCrate, consteval,
+ mir::pad16,
+ next_solver::{Const, Consts, TyKind, WorldExposer},
+};
+
+use super::{DbInterner, Ty};
+
+pub type ValTreeKind<'db> = rustc_type_ir::ValTreeKind<DbInterner<'db>>;
+
+/// A type-level constant value.
+///
+/// Represents a typed, fully evaluated constant.
+#[derive(
+ Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable, GenericTypeVisitable,
+)]
+pub struct ValueConst<'db> {
+ pub ty: Ty<'db>,
+ pub value: ValTree<'db>,
+}
+
+impl<'db> ValueConst<'db> {
+ pub fn new(ty: Ty<'db>, kind: ValTreeKind<'db>) -> Self {
+ let value = ValTree::new(kind);
+ ValueConst { ty, value }
+ }
+}
+
+pub(super) fn allocation_to_const<'db>(
+ interner: DbInterner<'db>,
+ ty: Ty<'db>,
+ memory: &[u8],
+ memory_map: &MemoryMap<'db>,
+ param_env: ParamEnvAndCrate<'db>,
+) -> Const<'db> {
+ let Ok(data_layout) = interner.db.target_data_layout(param_env.krate) else {
+ return Const::error(interner);
+ };
+ let valtree = match ty.kind() {
+ TyKind::Bool => ValTreeKind::Leaf(ScalarInt::from(memory[0] != 0)),
+ TyKind::Char => {
+ let it = u128::from_le_bytes(pad16(memory, false)) as u32;
+ let Ok(c) = char::try_from(it) else {
+ return Const::error(interner);
+ };
+ ValTreeKind::Leaf(ScalarInt::from(c))
+ }
+ TyKind::Int(int) => {
+ let it = i128::from_le_bytes(pad16(memory, true));
+ let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_int(it, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Uint(uint) => {
+ let it = u128::from_le_bytes(pad16(memory, false));
+ let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size());
+ let scalar = ScalarInt::try_from_uint(it, size).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Float(float) => {
+ let scalar = match float {
+ rustc_ast_ir::FloatTy::F16 => {
+ ScalarInt::from(u16::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F32 => {
+ ScalarInt::from(u32::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F64 => {
+ ScalarInt::from(u64::from_le_bytes(memory.try_into().unwrap()))
+ }
+ rustc_ast_ir::FloatTy::F128 => {
+ ScalarInt::from(u128::from_le_bytes(memory.try_into().unwrap()))
+ }
+ };
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Ref(_, t, _) => match t.kind() {
+ TyKind::Str => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let size = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ let u8_values = &interner.default_types().consts.u8_values;
+ ValTreeKind::Branch(Consts::new_from_iter(
+ interner,
+ bytes.iter().map(|&byte| u8_values[usize::from(byte)]),
+ ))
+ }
+ TyKind::Slice(ty) => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let count = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size_one = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size_one * count) else {
+ return Const::error(interner);
+ };
+ let expected_len = count * size_one;
+ if bytes.len() < expected_len {
+ never!(
+ "Memory map size is too small. Expected {expected_len}, got {}",
+ bytes.len(),
+ );
+ return Const::error(interner);
+ }
+ let items = (0..count).map(|i| {
+ let offset = size_one * i;
+ let bytes = &bytes[offset..offset + size_one];
+ allocation_to_const(interner, ty, bytes, memory_map, param_env)
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Dynamic(_, _) => {
+ let addr = usize::from_le_bytes(memory[0..memory.len() / 2].try_into().unwrap());
+ let ty_id = usize::from_le_bytes(memory[memory.len() / 2..].try_into().unwrap());
+ let Ok(t) = memory_map.vtable_ty(ty_id) else {
+ return Const::error(interner);
+ };
+ let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ return allocation_to_const(interner, t, bytes, memory_map, param_env);
+ }
+ TyKind::Adt(..) if memory.len() == 2 * size_of::<usize>() => {
+ // FIXME: Unsized ADT.
+ return Const::error(interner);
+ }
+ _ => {
+ let addr = usize::from_le_bytes(match memory.try_into() {
+ Ok(b) => b,
+ Err(_) => {
+ never!(
+ "tried rendering ty {:?} in const ref with incorrect byte count {}",
+ t,
+ memory.len()
+ );
+ return Const::error(interner);
+ }
+ });
+ let Ok(layout) = interner.db.layout_of_ty(t.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ let Some(bytes) = memory_map.get(addr, size) else {
+ return Const::error(interner);
+ };
+ return allocation_to_const(interner, t, bytes, memory_map, param_env);
+ }
+ },
+ TyKind::Tuple(tys) => {
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let items = tys.iter().enumerate().map(|(id, ty)| {
+ let offset = layout.fields.offset(id).bytes_usize();
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size = layout.size.bytes_usize();
+ allocation_to_const(
+ interner,
+ ty,
+ &memory[offset..offset + size],
+ memory_map,
+ param_env,
+ )
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Adt(..) => {
+ // FIXME: This requires `adt_const_params`.
+ return Const::error(interner);
+ }
+ TyKind::FnDef(..) => {
+ // FIXME: Fn items.
+ return Const::error(interner);
+ }
+ TyKind::FnPtr(_, _) | TyKind::RawPtr(_, _) => {
+ let it = u128::from_le_bytes(pad16(memory, false));
+ // FIXME: Unsized pointers.
+ let scalar = ScalarInt::try_from_uint(it, data_layout.pointer_size()).unwrap();
+ ValTreeKind::Leaf(scalar)
+ }
+ TyKind::Array(ty, len) => {
+ let Some(len) = consteval::try_const_usize(interner.db, len) else {
+ return Const::error(interner);
+ };
+ let Ok(layout) = interner.db.layout_of_ty(ty.store(), param_env.store()) else {
+ return Const::error(interner);
+ };
+ let size_one = layout.size.bytes_usize();
+ let items = (0..len as usize).map(|i| {
+ let offset = size_one * i;
+ allocation_to_const(
+ interner,
+ ty,
+ &memory[offset..offset + size_one],
+ memory_map,
+ param_env,
+ )
+ });
+ ValTreeKind::Branch(Consts::new_from_iter(interner, items))
+ }
+ TyKind::Never => return Const::error(interner),
+ // FIXME:
+ TyKind::Closure(_, _)
+ | TyKind::Coroutine(_, _)
+ | TyKind::CoroutineWitness(_, _)
+ | TyKind::CoroutineClosure(_, _)
+ | TyKind::UnsafeBinder(_) => return Const::error(interner),
+ // The below arms are unreachable, since const eval will bail out before here.
+ TyKind::Foreign(_) => return Const::error(interner),
+ TyKind::Pat(_, _) => return Const::error(interner),
+ TyKind::Error(..)
+ | TyKind::Placeholder(_)
+ | TyKind::Alias(..)
+ | TyKind::Param(_)
+ | TyKind::Bound(_, _)
+ | TyKind::Infer(_) => return Const::error(interner),
+ // The below arms are unreachable, since we handled them in ref case.
+ TyKind::Slice(_) | TyKind::Str | TyKind::Dynamic(_, _) => {
+ return Const::error(interner);
+ }
+ };
+ Const::new_valtree(interner, ty, valtree)
+}
+
+impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'db> {
+ fn ty(self) -> Ty<'db> {
+ self.ty
+ }
+
+ fn valtree(self) -> ValTree<'db> {
+ self.value
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ValTree<'db> {
+ interned: InternedRef<'db, ValTreeInterned>,
+}
+
+impl<'db, V: WorldExposer> GenericTypeVisitable<V> for ValTree<'db> {
+ fn generic_visit_with(&self, visitor: &mut V) {
+ if visitor.on_interned(self.interned).is_continue() {
+ self.inner().generic_visit_with(visitor);
+ }
+ }
+}
+
+impl<'db> TypeVisitable<DbInterner<'db>> for ValTree<'db> {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.inner().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for ValTree<'db> {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ self.inner().try_fold_with(folder).map(ValTree::new)
+ }
+
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ ValTree::new(self.inner().fold_with(folder))
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, GenericTypeVisitable)]
+pub(in super::super) struct ValTreeInterned(ValTreeKind<'static>);
+
+impl_internable!(gc; ValTreeInterned);
+
+const _: () = {
+ const fn is_copy<T: Copy>() {}
+ is_copy::<ValTree<'static>>();
+};
+
+impl<'db> IntoKind for ValTree<'db> {
+ type Kind = ValTreeKind<'db>;
+
+ fn kind(self) -> Self::Kind {
+ *self.inner()
+ }
+}
+
+impl<'db> ValTree<'db> {
+ #[inline]
+ pub fn new(kind: ValTreeKind<'db>) -> Self {
+ let kind = unsafe { std::mem::transmute::<ValTreeKind<'db>, ValTreeKind<'static>>(kind) };
+ Self { interned: Interned::new_gc(ValTreeInterned(kind)) }
+ }
+
+ #[inline]
+ pub fn inner(&self) -> &ValTreeKind<'db> {
+ let inner = &self.interned.0;
+ unsafe { std::mem::transmute::<&ValTreeKind<'static>, &ValTreeKind<'db>>(inner) }
+ }
+}
+
+impl std::fmt::Debug for ValTree<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.interned.fmt(f)
+ }
+}
+
+/// The raw bytes of a simple value.
+///
+/// This is a packed struct in order to allow this type to be optimally embedded in enums
+/// (like Scalar).
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+#[repr(Rust, packed)]
+pub struct ScalarInt {
+ /// The first `size` bytes of `data` are the value.
+ /// Do not try to read less or more bytes than that. The remaining bytes must be 0.
+ data: u128,
+ size: NonZero<u8>,
+}
+
+impl ScalarInt {
+ pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
+ pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
+
+ fn raw(data: u128, size: Size) -> Self {
+ Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
+ }
+
+ #[inline]
+ pub fn size(self) -> Size {
+ Size::from_bytes(self.size.get())
+ }
+
+ /// Make sure the `data` fits in `size`.
+ /// This is guaranteed by all constructors here, but having had this check saved us from
+ /// bugs many times in the past, so keeping it around is definitely worth it.
+ #[inline(always)]
+ fn check_data(self) {
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `debug_assert_eq` takes references to its arguments and formatting
+ // arguments and would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ debug_assert_eq!(
+ self.size().truncate(self.data),
+ { self.data },
+ "Scalar value {:#x} exceeds size of {} bytes",
+ { self.data },
+ self.size
+ );
+ }
+
+ #[inline]
+ pub fn null(size: Size) -> Self {
+ Self::raw(0, size)
+ }
+
+ #[inline]
+ pub fn is_null(self) -> bool {
+ self.data == 0
+ }
+
+ #[inline]
+ pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
+ let (r, overflow) = Self::truncate_from_uint(i, size);
+ if overflow { None } else { Some(r) }
+ }
+
+ /// Returns the truncated result, and whether truncation changed the value.
+ #[inline]
+ pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
+ let data = i.into();
+ let r = Self::raw(size.truncate(data), size);
+ (r, r.data != data)
+ }
+
+ #[inline]
+ pub fn try_from_int(i: impl Into<i128>, size: Size) -> Option<Self> {
+ let (r, overflow) = Self::truncate_from_int(i, size);
+ if overflow { None } else { Some(r) }
+ }
+
+ /// Returns the truncated result, and whether truncation changed the value.
+ #[inline]
+ pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
+ let data = i.into();
+ // `into` performed sign extension, we have to truncate
+ let r = Self::raw(size.truncate(data as u128), size);
+ (r, size.sign_extend(r.data) != data)
+ }
+
+ #[inline]
+ pub fn try_from_target_usize(
+ i: impl Into<u128>,
+ data_layout: &TargetDataLayout,
+ ) -> Option<Self> {
+ Self::try_from_uint(i, data_layout.pointer_size())
+ }
+
+ /// Try to convert this ScalarInt to the raw underlying bits.
+ /// Fails if the size is wrong. Generally a wrong size should lead to a panic,
+ /// but Miri sometimes wants to be resilient to size mismatches,
+ /// so the interpreter will generally use this `try` method.
+ #[inline]
+ pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
+ assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
+ if target_size.bytes() == u64::from(self.size.get()) {
+ self.check_data();
+ Ok(self.data)
+ } else {
+ Err(self.size())
+ }
+ }
+
+ #[inline]
+ pub fn to_bits(self, target_size: Size) -> u128 {
+ self.try_to_bits(target_size).unwrap_or_else(|size| {
+ panic!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
+ })
+ }
+
+ /// Extracts the bits from the scalar without checking the size.
+ #[inline]
+ pub fn to_bits_unchecked(self) -> u128 {
+ self.check_data();
+ self.data
+ }
+
+ /// Converts the `ScalarInt` to an unsigned integer of the given size.
+ /// Panics if the size of the `ScalarInt` is not equal to `size`.
+ #[inline]
+ pub fn to_uint(self, size: Size) -> u128 {
+ self.to_bits(size)
+ }
+
+ #[inline]
+ pub fn to_uint_unchecked(self) -> u128 {
+ self.data
+ }
+
+ /// Converts the `ScalarInt` to `u8`.
+ /// Panics if the `size` of the `ScalarInt`in not equal to 1 byte.
+ #[inline]
+ pub fn to_u8(self) -> u8 {
+ self.to_uint(Size::from_bits(8)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u16`.
+ /// Panics if the size of the `ScalarInt` in not equal to 2 bytes.
+ #[inline]
+ pub fn to_u16(self) -> u16 {
+ self.to_uint(Size::from_bits(16)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u32`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 4 bytes.
+ #[inline]
+ pub fn to_u32(self) -> u32 {
+ self.to_uint(Size::from_bits(32)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u64`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 8 bytes.
+ #[inline]
+ pub fn to_u64(self) -> u64 {
+ self.to_uint(Size::from_bits(64)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `u128`.
+ /// Panics if the `size` of the `ScalarInt` in not equal to 16 bytes.
+ #[inline]
+ pub fn to_u128(self) -> u128 {
+ self.to_uint(Size::from_bits(128))
+ }
+
+ #[inline]
+ pub fn to_target_usize(&self, data_layout: &TargetDataLayout) -> u64 {
+ self.to_uint(data_layout.pointer_size()).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to `bool`.
+ /// Panics if the `size` of the `ScalarInt` is not equal to 1 byte.
+ /// Errors if it is not a valid `bool`.
+ #[inline]
+ pub fn try_to_bool(self) -> Result<bool, ()> {
+ match self.to_u8() {
+ 0 => Ok(false),
+ 1 => Ok(true),
+ _ => Err(()),
+ }
+ }
+
+ /// Converts the `ScalarInt` to a signed integer of the given size.
+ /// Panics if the size of the `ScalarInt` is not equal to `size`.
+ #[inline]
+ pub fn to_int(self, size: Size) -> i128 {
+ let b = self.to_bits(size);
+ size.sign_extend(b)
+ }
+
+ #[inline]
+ pub fn to_int_unchecked(self) -> i128 {
+ self.size().sign_extend(self.data)
+ }
+
+ /// Converts the `ScalarInt` to i8.
+ /// Panics if the size of the `ScalarInt` is not equal to 1 byte.
+ pub fn to_i8(self) -> i8 {
+ self.to_int(Size::from_bits(8)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i16.
+ /// Panics if the size of the `ScalarInt` is not equal to 2 bytes.
+ pub fn to_i16(self) -> i16 {
+ self.to_int(Size::from_bits(16)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i32.
+ /// Panics if the size of the `ScalarInt` is not equal to 4 bytes.
+ pub fn to_i32(self) -> i32 {
+ self.to_int(Size::from_bits(32)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i64.
+ /// Panics if the size of the `ScalarInt` is not equal to 8 bytes.
+ pub fn to_i64(self) -> i64 {
+ self.to_int(Size::from_bits(64)).try_into().unwrap()
+ }
+
+ /// Converts the `ScalarInt` to i128.
+ /// Panics if the size of the `ScalarInt` is not equal to 16 bytes.
+ pub fn to_i128(self) -> i128 {
+ self.to_int(Size::from_bits(128))
+ }
+
+ #[inline]
+ pub fn to_target_isize(&self, data_layout: &TargetDataLayout) -> i64 {
+ self.to_int(data_layout.pointer_size()).try_into().unwrap()
+ }
+}
+
+macro_rules! from_x_for_scalar_int {
+ ($($ty:ty),*) => {
+ $(
+ impl From<$ty> for ScalarInt {
+ #[inline]
+ fn from(u: $ty) -> Self {
+ Self {
+ data: u128::from(u),
+ size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
+ }
+ }
+ }
+ )*
+ }
+}
+
+macro_rules! from_scalar_int_for_x {
+ ($($ty:ty),*) => {
+ $(
+ impl From<ScalarInt> for $ty {
+ #[inline]
+ fn from(int: ScalarInt) -> Self {
+ // The `unwrap` cannot fail because to_uint (if it succeeds)
+ // is guaranteed to return a value that fits into the size.
+ int.to_uint(Size::from_bytes(size_of::<$ty>()))
+ .try_into().unwrap()
+ }
+ }
+ )*
+ }
+}
+
+from_x_for_scalar_int!(u8, u16, u32, u64, u128, bool);
+from_scalar_int_for_x!(u8, u16, u32, u64, u128);
+
+impl TryFrom<ScalarInt> for bool {
+ type Error = ();
+ #[inline]
+ fn try_from(int: ScalarInt) -> Result<Self, ()> {
+ int.try_to_bool()
+ }
+}
+
+impl From<char> for ScalarInt {
+ #[inline]
+ fn from(c: char) -> Self {
+ (c as u32).into()
+ }
+}
+
+macro_rules! from_x_for_scalar_int_signed {
+ ($($ty:ty),*) => {
+ $(
+ impl From<$ty> for ScalarInt {
+ #[inline]
+ fn from(u: $ty) -> Self {
+ Self {
+ data: u128::from(u.cast_unsigned()), // go via the unsigned type of the same size
+ size: NonZero::new(size_of::<$ty>() as u8).unwrap(),
+ }
+ }
+ }
+ )*
+ }
+}
+
+macro_rules! from_scalar_int_for_x_signed {
+ ($($ty:ty),*) => {
+ $(
+ impl From<ScalarInt> for $ty {
+ #[inline]
+ fn from(int: ScalarInt) -> Self {
+ // The `unwrap` cannot fail because to_int (if it succeeds)
+ // is guaranteed to return a value that fits into the size.
+ int.to_int(Size::from_bytes(size_of::<$ty>()))
+ .try_into().unwrap()
+ }
+ }
+ )*
+ }
+}
+
+from_x_for_scalar_int_signed!(i8, i16, i32, i64, i128);
+from_scalar_int_for_x_signed!(i8, i16, i32, i64, i128);
+
+impl From<std::cmp::Ordering> for ScalarInt {
+ #[inline]
+ fn from(c: std::cmp::Ordering) -> Self {
+ // Here we rely on `cmp::Ordering` having the same values in host and target!
+ ScalarInt::from(c as i8)
+ }
+}
+
+/// Error returned when a conversion from ScalarInt to char fails.
+#[derive(Debug)]
+pub struct CharTryFromScalarInt;
+
+impl TryFrom<ScalarInt> for char {
+ type Error = CharTryFromScalarInt;
+
+ #[inline]
+ fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
+ match char::from_u32(int.to_u32()) {
+ Some(c) => Ok(c),
+ None => Err(CharTryFromScalarInt),
+ }
+ }
+}
+
+impl fmt::Debug for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // Dispatch to LowerHex below.
+ write!(f, "0x{self:x}")
+ }
+}
+
+impl fmt::LowerHex for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ if f.alternate() {
+ // Like regular ints, alternate flag adds leading `0x`.
+ write!(f, "0x")?;
+ }
+ // Format as hex number wide enough to fit any value of the given `size`.
+ // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `write!` takes references to its formatting arguments and
+ // would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ write!(f, "{:01$x}", { self.data }, self.size.get() as usize * 2)
+ }
+}
+
+impl fmt::UpperHex for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ // Format as hex number wide enough to fit any value of the given `size`.
+ // So data=20, size=1 will be "0x14", but with size=4 it'll be "0x00000014".
+ // Using a block `{self.data}` here to force a copy instead of using `self.data`
+ // directly, because `write!` takes references to its formatting arguments and
+ // would thus borrow `self.data`. Since `Self`
+ // is a packed struct, that would create a possibly unaligned reference, which
+ // is UB.
+ write!(f, "{:01$X}", { self.data }, self.size.get() as usize * 2)
+ }
+}
+
+impl fmt::Display for ScalarInt {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.check_data();
+ write!(f, "{}", { self.data })
+ }
+}
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs
index 00161d6d08..542eca3ded 100644
--- a/crates/hir-ty/src/next_solver/def_id.rs
+++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -12,7 +12,9 @@ use hir_def::{
use rustc_type_ir::inherent;
use stdx::impl_from;
-use crate::db::{InternedClosureId, InternedCoroutineId, InternedOpaqueTyId};
+use crate::db::{
+ InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId, InternedOpaqueTyId,
+};
use super::DbInterner;
@@ -35,6 +37,7 @@ pub enum SolverDefId {
TypeAliasId(TypeAliasId),
InternedClosureId(InternedClosureId),
InternedCoroutineId(InternedCoroutineId),
+ InternedCoroutineClosureId(InternedCoroutineClosureId),
InternedOpaqueTyId(InternedOpaqueTyId),
EnumVariantId(EnumVariantId),
Ctor(Ctor),
@@ -80,6 +83,9 @@ impl std::fmt::Debug for SolverDefId {
SolverDefId::InternedCoroutineId(id) => {
f.debug_tuple("InternedCoroutineId").field(&id).finish()
}
+ SolverDefId::InternedCoroutineClosureId(id) => {
+ f.debug_tuple("InternedCoroutineClosureId").field(&id).finish()
+ }
SolverDefId::InternedOpaqueTyId(id) => {
f.debug_tuple("InternedOpaqueTyId").field(&id).finish()
}
@@ -123,6 +129,7 @@ impl_from!(
TypeAliasId,
InternedClosureId,
InternedCoroutineId,
+ InternedCoroutineClosureId,
InternedOpaqueTyId,
EnumVariantId,
Ctor
@@ -206,6 +213,7 @@ impl TryFrom<SolverDefId> for AttrDefId {
SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
| SolverDefId::InternedOpaqueTyId(_)
| SolverDefId::AnonConstId(_) => Err(()),
}
@@ -229,6 +237,7 @@ impl TryFrom<SolverDefId> for DefWithBodyId {
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
| SolverDefId::Ctor(Ctor::Struct(_))
| SolverDefId::AnonConstId(_)
| SolverDefId::AdtId(_) => return Err(()),
@@ -251,6 +260,7 @@ impl TryFrom<SolverDefId> for GenericDefId {
SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id),
SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
| SolverDefId::InternedOpaqueTyId(_)
| SolverDefId::EnumVariantId(_)
| SolverDefId::BuiltinDeriveImplId(_)
@@ -348,6 +358,7 @@ 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);
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/hir-ty/src/next_solver/fold.rs b/crates/hir-ty/src/next_solver/fold.rs
index 7836419e8b..af823aa005 100644
--- a/crates/hir-ty/src/next_solver/fold.rs
+++ b/crates/hir-ty/src/next_solver/fold.rs
@@ -17,28 +17,28 @@ use super::{
/// gets mapped to the same result. `BoundVarReplacer` caches by using
/// a `DelayedMap` which does not cache the first few types it encounters.
pub trait BoundVarReplacerDelegate<'db> {
- fn replace_region(&mut self, br: BoundRegion) -> Region<'db>;
- fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db>;
- fn replace_const(&mut self, bv: BoundConst) -> Const<'db>;
+ fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db>;
+ fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db>;
+ fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db>;
}
/// A simple delegate taking 3 mutable functions. The used functions must
/// always return the same result for each bound variable, no matter how
/// frequently they are called.
pub struct FnMutDelegate<'db, 'a> {
- pub regions: &'a mut (dyn FnMut(BoundRegion) -> Region<'db> + 'a),
- pub types: &'a mut (dyn FnMut(BoundTy) -> Ty<'db> + 'a),
- pub consts: &'a mut (dyn FnMut(BoundConst) -> Const<'db> + 'a),
+ pub regions: &'a mut (dyn FnMut(BoundRegion<'db>) -> Region<'db> + 'a),
+ pub types: &'a mut (dyn FnMut(BoundTy<'db>) -> Ty<'db> + 'a),
+ pub consts: &'a mut (dyn FnMut(BoundConst<'db>) -> Const<'db> + 'a),
}
impl<'db, 'a> BoundVarReplacerDelegate<'db> for FnMutDelegate<'db, 'a> {
- fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> {
(self.regions)(br)
}
- fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> {
(self.types)(bt)
}
- fn replace_const(&mut self, bv: BoundConst) -> Const<'db> {
+ fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> {
(self.consts)(bv)
}
}
@@ -177,13 +177,13 @@ impl<'db> DbInterner<'db> {
self,
value: Binder<'db, T>,
mut fld_r: F,
- ) -> (T, FxIndexMap<BoundRegion, Region<'db>>)
+ ) -> (T, FxIndexMap<BoundRegion<'db>, Region<'db>>)
where
- F: FnMut(BoundRegion) -> Region<'db>,
+ F: FnMut(BoundRegion<'db>) -> Region<'db>,
T: TypeFoldable<DbInterner<'db>>,
{
let mut region_map = FxIndexMap::default();
- let real_fld_r = |br: BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
+ let real_fld_r = |br: BoundRegion<'db>| *region_map.entry(br).or_insert_with(|| fld_r(br));
let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
(value, region_map)
}
@@ -194,7 +194,7 @@ impl<'db> DbInterner<'db> {
mut replace_regions: F,
) -> T
where
- F: FnMut(BoundRegion) -> Region<'db>,
+ F: FnMut(BoundRegion<'db>) -> Region<'db>,
T: TypeFoldable<DbInterner<'db>>,
{
let value = value.skip_binder();
diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs
index a8bff44a02..6739795a00 100644
--- a/crates/hir-ty/src/next_solver/fulfill.rs
+++ b/crates/hir-ty/src/next_solver/fulfill.rs
@@ -1,7 +1,5 @@
//! Fulfill loop for next-solver.
-mod errors;
-
use std::ops::ControlFlow;
use rustc_hash::FxHashSet;
diff --git a/crates/hir-ty/src/next_solver/fulfill/errors.rs b/crates/hir-ty/src/next_solver/fulfill/errors.rs
deleted file mode 100644
index 0e8218b33a..0000000000
--- a/crates/hir-ty/src/next_solver/fulfill/errors.rs
+++ /dev/null
@@ -1,1173 +0,0 @@
-//! Trait solving error diagnosis and reporting.
-//!
-//! This code isn't used by rust-analyzer (it should, but then it'll probably be better to re-port it from rustc).
-//! It's only there because without it, debugging trait solver errors is a nightmare.
-
-use std::{fmt::Debug, ops::ControlFlow};
-
-use rustc_next_trait_solver::solve::{GoalEvaluation, SolverDelegateEvalExt};
-use rustc_type_ir::{
- AliasRelationDirection, AliasTermKind, HostEffectPredicate, Interner, PredicatePolarity,
- error::ExpectedFound,
- inherent::{IntoKind, Span as _},
- lang_items::SolverTraitLangItem,
- solve::{Certainty, GoalSource, MaybeCause, NoSolution},
-};
-use tracing::{instrument, trace};
-
-use crate::next_solver::{
- AliasTerm, Binder, ClauseKind, Const, ConstKind, DbInterner, PolyTraitPredicate, PredicateKind,
- SolverContext, Span, 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(Debug, 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>,
- },
-}
-
-#[derive(Debug, Clone)]
-pub struct MismatchedProjectionTypes<'db> {
- pub err: TypeError<'db>,
-}
-
-pub(super) fn fulfillment_error_for_no_solution<'db>(
- infcx: &InferCtxt<'db>,
- root_obligation: PredicateObligation<'db>,
-) -> FulfillmentError<'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) => {
- infcx.interner.type_of(uv.def.into()).instantiate(infcx.interner, uv.args)
- }
- 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 }
-}
-
-pub(super) 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(),
- Span::dummy(),
- None,
- ) {
- Ok(GoalEvaluation {
- certainty: Certainty::Maybe { cause: MaybeCause::Ambiguity, .. },
- ..
- }) => (FulfillmentErrorCode::Ambiguity { overflow: None }, true),
- Ok(GoalEvaluation {
- certainty:
- Certainty::Maybe {
- 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,
- }
-}
-
-pub(super) 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(())
- })
- .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().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 infcx = candidate.goal().infcx();
- let param_env = candidate.goal().goal().param_env;
-
- for obligation in wf::unnormalized_obligations(infcx, param_env, term).into_iter().flatten()
- {
- let nested_goal = candidate
- .instantiate_proof_tree_for_nested_goal(GoalSource::Misc, obligation.as_goal());
- // 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();
- let pred = PredicateKind::AliasRelate(
- self_ty.into(),
- infer_term.into(),
- AliasRelationDirection::Equate,
- );
- let obligation = Obligation::new(
- interner,
- self.obligation.cause.clone(),
- 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.clone(),
- 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.clone(),
- 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>>;
-
- #[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 { 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()
- // && interner.do_not_recommend_impl(impl_def_id)
- // {
- // trace!("#[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();
-
- // 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()
- && interner
- .is_trait_lang_item(poly_trait_pred.def_id(), SolverTraitLangItem::FnPtrTrait)
- && let Err(NoSolution) = nested_goal.result()
- {
- return ControlFlow::Break(self.obligation.clone());
- }
- }
-
- 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 = || Obligation {
- cause: ObligationCause::dummy(),
- 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) => {
- make_obligation()
- }
- (
- ChildMode::Host(_parent_host_pred),
- GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
- ) => make_obligation(),
- (ChildMode::PassThrough, _)
- | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
- make_obligation()
- }
- };
-
- 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<DbInterner<'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,
-}
-
-impl<'db> NextSolverError<'db> {
- pub fn to_debuggable_error(&self, infcx: &InferCtxt<'db>) -> FulfillmentError<'db> {
- match self {
- NextSolverError::TrueError(obligation) => {
- fulfillment_error_for_no_solution(infcx, obligation.clone())
- }
- NextSolverError::Ambiguity(obligation) => {
- fulfillment_error_for_stalled(infcx, obligation.clone())
- }
- NextSolverError::Overflow(obligation) => {
- fulfillment_error_for_overflow(infcx, obligation.clone())
- }
- }
- }
-}
-
-mod wf {
- use hir_def::signatures::ImplSignature;
- use hir_def::{GeneralConstId, ItemContainerId};
- use rustc_type_ir::inherent::{
- AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Term as _,
- Ty as _,
- };
- use rustc_type_ir::lang_items::SolverTraitLangItem;
- use rustc_type_ir::{
- Interner, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
- };
- use tracing::{debug, instrument};
-
- use crate::next_solver::infer::InferCtxt;
- use crate::next_solver::infer::traits::{Obligation, ObligationCause, PredicateObligations};
- use crate::next_solver::{
- Binder, ClauseKind, Const, ConstKind, Ctor, DbInterner, ExistentialPredicate, GenericArgs,
- ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind,
- };
-
- /// Compute the predicates that are required for a type to be well-formed.
- ///
- /// This is only intended to be used in the new solver, since it does not
- /// take into account recursion depth or proper error-reporting spans.
- pub(crate) fn unnormalized_obligations<'db>(
- infcx: &InferCtxt<'db>,
- param_env: ParamEnv<'db>,
- term: Term<'db>,
- ) -> Option<PredicateObligations<'db>> {
- debug_assert_eq!(term, infcx.resolve_vars_if_possible(term));
-
- // However, if `arg` IS an unresolved inference variable, returns `None`,
- // because we are not able to make any progress at all. This is to prevent
- // cycles where we say "?0 is WF if ?0 is WF".
- if term.is_infer() {
- return None;
- }
-
- let mut wf =
- WfPredicates { infcx, param_env, out: PredicateObligations::new(), recursion_depth: 0 };
- wf.add_wf_preds_for_term(term);
- Some(wf.out)
- }
-
- struct WfPredicates<'a, 'db> {
- infcx: &'a InferCtxt<'db>,
- param_env: ParamEnv<'db>,
- out: PredicateObligations<'db>,
- recursion_depth: usize,
- }
-
- impl<'a, 'db> WfPredicates<'a, 'db> {
- fn interner(&self) -> DbInterner<'db> {
- self.infcx.interner
- }
-
- fn require_sized(&mut self, subty: Ty<'db>) {
- if !subty.has_escaping_bound_vars() {
- let cause = ObligationCause::new();
- let trait_ref = TraitRef::new(
- self.interner(),
- self.interner().require_trait_lang_item(SolverTraitLangItem::Sized),
- [subty],
- );
- self.out.push(Obligation::with_depth(
- self.interner(),
- cause,
- self.recursion_depth,
- self.param_env,
- Binder::dummy(trait_ref),
- ));
- }
- }
-
- /// Pushes all the predicates needed to validate that `term` is WF into `out`.
- #[instrument(level = "debug", skip(self))]
- fn add_wf_preds_for_term(&mut self, term: Term<'db>) {
- term.visit_with(self);
- debug!(?self.out);
- }
-
- #[instrument(level = "debug", skip(self))]
- fn nominal_obligations(
- &mut self,
- def_id: SolverDefId,
- args: GenericArgs<'db>,
- ) -> PredicateObligations<'db> {
- // PERF: `Sized`'s predicates include `MetaSized`, but both are compiler implemented marker
- // traits, so `MetaSized` will always be WF if `Sized` is WF and vice-versa. Determining
- // the nominal obligations of `Sized` would in-effect just elaborate `MetaSized` and make
- // the compiler do a bunch of work needlessly.
- if let SolverDefId::TraitId(def_id) = def_id
- && self.interner().is_trait_lang_item(def_id.into(), SolverTraitLangItem::Sized)
- {
- return Default::default();
- }
-
- self.interner()
- .predicates_of(def_id)
- .iter_instantiated(self.interner(), args)
- .map(|pred| {
- let cause = ObligationCause::new();
- Obligation::with_depth(
- self.interner(),
- cause,
- self.recursion_depth,
- self.param_env,
- pred,
- )
- })
- .filter(|pred| !pred.has_escaping_bound_vars())
- .collect()
- }
-
- fn add_wf_preds_for_dyn_ty(
- &mut self,
- _ty: Ty<'db>,
- data: &[Binder<'db, ExistentialPredicate<'db>>],
- region: Region<'db>,
- ) {
- // Imagine a type like this:
- //
- // trait Foo { }
- // trait Bar<'c> : 'c { }
- //
- // &'b (Foo+'c+Bar<'d>)
- // ^
- //
- // In this case, the following relationships must hold:
- //
- // 'b <= 'c
- // 'd <= 'c
- //
- // The first conditions is due to the normal region pointer
- // rules, which say that a reference cannot outlive its
- // referent.
- //
- // The final condition may be a bit surprising. In particular,
- // you may expect that it would have been `'c <= 'd`, since
- // usually lifetimes of outer things are conservative
- // approximations for inner things. However, it works somewhat
- // differently with trait objects: here the idea is that if the
- // user specifies a region bound (`'c`, in this case) it is the
- // "master bound" that *implies* that bounds from other traits are
- // all met. (Remember that *all bounds* in a type like
- // `Foo+Bar+Zed` must be met, not just one, hence if we write
- // `Foo<'x>+Bar<'y>`, we know that the type outlives *both* 'x and
- // 'y.)
- //
- // Note: in fact we only permit builtin traits, not `Bar<'d>`, I
- // am looking forward to the future here.
- if !data.has_escaping_bound_vars() && !region.has_escaping_bound_vars() {
- let implicit_bounds = object_region_bounds(self.interner(), data);
-
- let explicit_bound = region;
-
- self.out.reserve(implicit_bounds.len());
- for implicit_bound in implicit_bounds {
- let cause = ObligationCause::new();
- let outlives = Binder::dummy(rustc_type_ir::OutlivesPredicate(
- explicit_bound,
- implicit_bound,
- ));
- self.out.push(Obligation::with_depth(
- self.interner(),
- cause,
- self.recursion_depth,
- self.param_env,
- outlives,
- ));
- }
-
- // We don't add any wf predicates corresponding to the trait ref's generic arguments
- // which allows code like this to compile:
- // ```rust
- // trait Trait<T: Sized> {}
- // fn foo(_: &dyn Trait<[u32]>) {}
- // ```
- }
- }
- }
-
- impl<'a, 'db> TypeVisitor<DbInterner<'db>> for WfPredicates<'a, 'db> {
- type Result = ();
-
- fn visit_ty(&mut self, t: Ty<'db>) -> Self::Result {
- debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
-
- let tcx = self.interner();
-
- match t.kind() {
- TyKind::Bool
- | TyKind::Char
- | TyKind::Int(..)
- | TyKind::Uint(..)
- | TyKind::Float(..)
- | TyKind::Error(_)
- | TyKind::Str
- | TyKind::CoroutineWitness(..)
- | TyKind::Never
- | TyKind::Param(_)
- | TyKind::Bound(..)
- | TyKind::Placeholder(..)
- | TyKind::Foreign(..) => {
- // WfScalar, WfParameter, etc
- }
-
- // Can only infer to `TyKind::Int(_) | TyKind::Uint(_)`.
- TyKind::Infer(rustc_type_ir::IntVar(_)) => {}
-
- // Can only infer to `TyKind::Float(_)`.
- TyKind::Infer(rustc_type_ir::FloatVar(_)) => {}
-
- TyKind::Slice(subty) => {
- self.require_sized(subty);
- }
-
- TyKind::Array(subty, len) => {
- self.require_sized(subty);
- // Note that the len being WF is implicitly checked while visiting.
- // Here we just check that it's of type usize.
- let cause = ObligationCause::new();
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- Binder::dummy(PredicateKind::Clause(ClauseKind::ConstArgHasType(
- len,
- Ty::new_unit(self.interner()),
- ))),
- ));
- }
-
- TyKind::Pat(base_ty, _pat) => {
- self.require_sized(base_ty);
- }
-
- TyKind::Tuple(tys) => {
- if let Some((_last, rest)) = tys.split_last() {
- for &elem in rest {
- self.require_sized(elem);
- }
- }
- }
-
- TyKind::RawPtr(_, _) => {
- // Simple cases that are WF if their type args are WF.
- }
-
- TyKind::Alias(
- rustc_type_ir::Projection | rustc_type_ir::Opaque | rustc_type_ir::Free,
- data,
- ) => {
- let obligations = self.nominal_obligations(data.def_id, data.args);
- self.out.extend(obligations);
- }
- TyKind::Alias(rustc_type_ir::Inherent, _data) => {
- return;
- }
-
- TyKind::Adt(def, args) => {
- // WfNominalType
- let obligations = self.nominal_obligations(def.def_id().0.into(), args);
- self.out.extend(obligations);
- }
-
- TyKind::FnDef(did, args) => {
- // HACK: Check the return type of function definitions for
- // well-formedness to mostly fix #84533. This is still not
- // perfect and there may be ways to abuse the fact that we
- // ignore requirements with escaping bound vars. That's a
- // more general issue however.
- let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
- fn_sig.output().skip_binder().visit_with(self);
-
- let did = match did.0 {
- hir_def::CallableDefId::FunctionId(id) => id.into(),
- hir_def::CallableDefId::StructId(id) => SolverDefId::Ctor(Ctor::Struct(id)),
- hir_def::CallableDefId::EnumVariantId(id) => {
- SolverDefId::Ctor(Ctor::Enum(id))
- }
- };
- let obligations = self.nominal_obligations(did, args);
- self.out.extend(obligations);
- }
-
- TyKind::Ref(r, rty, _) => {
- // WfReference
- if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
- let cause = ObligationCause::new();
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- Binder::dummy(PredicateKind::Clause(ClauseKind::TypeOutlives(
- rustc_type_ir::OutlivesPredicate(rty, r),
- ))),
- ));
- }
- }
-
- TyKind::Coroutine(did, args, ..) => {
- // Walk ALL the types in the coroutine: this will
- // include the upvar types as well as the yield
- // type. Note that this is mildly distinct from
- // the closure case, where we have to be careful
- // about the signature of the closure. We don't
- // have the problem of implied bounds here since
- // coroutines don't take arguments.
- let obligations = self.nominal_obligations(did.0.into(), args);
- self.out.extend(obligations);
- }
-
- TyKind::Closure(did, args) => {
- // Note that we cannot skip the generic types
- // types. Normally, within the fn
- // body where they are created, the generics will
- // always be WF, and outside of that fn body we
- // are not directly inspecting closure types
- // anyway, except via auto trait matching (which
- // only inspects the upvar types).
- // But when a closure is part of a type-alias-impl-trait
- // then the function that created the defining site may
- // have had more bounds available than the type alias
- // specifies. This may cause us to have a closure in the
- // hidden type that is not actually well formed and
- // can cause compiler crashes when the user abuses unsafe
- // code to procure such a closure.
- // See tests/ui/type-alias-impl-trait/wf_check_closures.rs
- let obligations = self.nominal_obligations(did.0.into(), args);
- self.out.extend(obligations);
- // Only check the upvar types for WF, not the rest
- // of the types within. This is needed because we
- // capture the signature and it may not be WF
- // without the implied bounds. Consider a closure
- // like `|x: &'a T|` -- it may be that `T: 'a` is
- // not known to hold in the creator's context (and
- // indeed the closure may not be invoked by its
- // creator, but rather turned to someone who *can*
- // verify that).
- //
- // The special treatment of closures here really
- // ought not to be necessary either; the problem
- // is related to #25860 -- there is no way for us
- // to express a fn type complete with the implied
- // bounds that it is assuming. I think in reality
- // the WF rules around fn are a bit messed up, and
- // that is the rot problem: `fn(&'a T)` should
- // probably always be WF, because it should be
- // shorthand for something like `where(T: 'a) {
- // fn(&'a T) }`, as discussed in #25860.
- let upvars = args.as_closure().tupled_upvars_ty();
- return upvars.visit_with(self);
- }
-
- TyKind::CoroutineClosure(did, args) => {
- // See the above comments. The same apply to coroutine-closures.
- let obligations = self.nominal_obligations(did.0.into(), args);
- self.out.extend(obligations);
- let upvars = args.as_coroutine_closure().tupled_upvars_ty();
- return upvars.visit_with(self);
- }
-
- TyKind::FnPtr(..) => {
- // Let the visitor iterate into the argument/return
- // types appearing in the fn signature.
- }
- TyKind::UnsafeBinder(_ty) => {}
-
- TyKind::Dynamic(data, r) => {
- // WfObject
- //
- // Here, we defer WF checking due to higher-ranked
- // regions. This is perhaps not ideal.
- self.add_wf_preds_for_dyn_ty(t, data.as_slice(), r);
-
- // FIXME(#27579) RFC also considers adding trait
- // obligations that don't refer to Self and
- // checking those
- if let Some(principal) = data.principal_def_id() {
- self.out.push(Obligation::with_depth(
- tcx,
- ObligationCause::new(),
- self.recursion_depth,
- self.param_env,
- Binder::dummy(PredicateKind::DynCompatible(principal)),
- ));
- }
- }
-
- // Inference variables are the complicated case, since we don't
- // know what type they are. We do two things:
- //
- // 1. Check if they have been resolved, and if so proceed with
- // THAT type.
- // 2. If not, we've at least simplified things (e.g., we went
- // from `Vec?0>: WF` to `?0: WF`), so we can
- // register a pending obligation and keep
- // moving. (Goal is that an "inductive hypothesis"
- // is satisfied to ensure termination.)
- // See also the comment on `fn obligations`, describing cycle
- // prevention, which happens before this can be reached.
- TyKind::Infer(_) => {
- let cause = ObligationCause::new();
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(t.into()))),
- ));
- }
- }
-
- t.super_visit_with(self)
- }
-
- fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
- let tcx = self.interner();
-
- match c.kind() {
- ConstKind::Unevaluated(uv) => {
- if !c.has_escaping_bound_vars() {
- let predicate =
- Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c)));
- let cause = ObligationCause::new();
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- predicate,
- ));
-
- if let GeneralConstId::ConstId(uv_def) = uv.def.0
- && let ItemContainerId::ImplId(impl_) =
- uv_def.loc(self.interner().db).container
- && ImplSignature::of(self.interner().db, impl_).target_trait.is_none()
- {
- return; // Subtree is handled by above function
- } else {
- let obligations = self.nominal_obligations(uv.def.into(), uv.args);
- self.out.extend(obligations);
- }
- }
- }
- ConstKind::Infer(_) => {
- let cause = ObligationCause::new();
-
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(c.into()))),
- ));
- }
- ConstKind::Expr(_) => {
- // FIXME(generic_const_exprs): this doesn't verify that given `Expr(N + 1)` the
- // trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
- // as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
- // which means that the `DefId` would have been typeck'd elsewhere. However in
- // the future we may allow directly lowering to `ConstKind::Expr` in which case
- // we would not be proving bounds we should.
-
- let predicate =
- Binder::dummy(PredicateKind::Clause(ClauseKind::ConstEvaluatable(c)));
- let cause = ObligationCause::new();
- self.out.push(Obligation::with_depth(
- tcx,
- cause,
- self.recursion_depth,
- self.param_env,
- predicate,
- ));
- }
-
- ConstKind::Error(_)
- | ConstKind::Param(_)
- | ConstKind::Bound(..)
- | ConstKind::Placeholder(..) => {
- // These variants are trivially WF, so nothing to do here.
- }
- ConstKind::Value(..) => {
- // FIXME: Enforce that values are structurally-matchable.
- }
- }
-
- c.super_visit_with(self)
- }
-
- fn visit_predicate(&mut self, _p: Predicate<'db>) -> Self::Result {
- panic!("predicate should not be checked for well-formedness");
- }
- }
-
- /// Given an object type like `SomeTrait + Send`, computes the lifetime
- /// bounds that must hold on the elided self type. These are derived
- /// from the declarations of `SomeTrait`, `Send`, and friends -- if
- /// they declare `trait SomeTrait : 'static`, for example, then
- /// `'static` would appear in the list.
- ///
- /// N.B., in some cases, particularly around higher-ranked bounds,
- /// this function returns a kind of conservative approximation.
- /// That is, all regions returned by this function are definitely
- /// required, but there may be other region bounds that are not
- /// returned, as well as requirements like `for<'a> T: 'a`.
- ///
- /// Requires that trait definitions have been processed so that we can
- /// elaborate predicates and walk supertraits.
- pub(crate) fn object_region_bounds<'db>(
- interner: DbInterner<'db>,
- existential_predicates: &[Binder<'db, ExistentialPredicate<'db>>],
- ) -> Vec<Region<'db>> {
- let erased_self_ty = Ty::new_unit(interner);
-
- let predicates = existential_predicates
- .iter()
- .map(|predicate| predicate.with_self_ty(interner, erased_self_ty));
-
- rustc_type_ir::elaborate::elaborate(interner, predicates)
- .filter_map(|pred| {
- debug!(?pred);
- match pred.kind().skip_binder() {
- ClauseKind::TypeOutlives(rustc_type_ir::OutlivesPredicate(ref t, ref r)) => {
- // Search for a bound of the form `erased_self_ty
- // : 'a`, but be wary of something like `for<'a>
- // erased_self_ty : 'a` (we interpret a
- // higher-ranked bound like that as 'static,
- // though at present the code in `fulfill.rs`
- // considers such bounds to be unsatisfiable, so
- // it's kind of a moot point since you could never
- // construct such an object, but this seems
- // correct even if that code changes).
- if t == &erased_self_ty && !r.has_escaping_bound_vars() {
- Some(*r)
- } else {
- None
- }
- }
- ClauseKind::Trait(_)
- | ClauseKind::HostEffect(..)
- | ClauseKind::RegionOutlives(_)
- | ClauseKind::Projection(_)
- | ClauseKind::ConstArgHasType(_, _)
- | ClauseKind::WellFormed(_)
- | ClauseKind::UnstableFeature(_)
- | ClauseKind::ConstEvaluatable(_) => None,
- }
- })
- .collect()
- }
-}
diff --git a/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs b/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
index ccd9359010..33e4c175d0 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/canonicalizer.rs
@@ -16,11 +16,11 @@ use rustc_type_ir::{
use smallvec::SmallVec;
use tracing::debug;
-use crate::next_solver::infer::InferCtxt;
use crate::next_solver::{
- Binder, Canonical, CanonicalVarKind, CanonicalVars, Const, ConstKind, DbInterner, GenericArg,
- Placeholder, Region, Ty, TyKind,
+ Binder, Canonical, CanonicalVarKind, CanonicalVarKinds, Const, ConstKind, DbInterner,
+ GenericArg, PlaceholderConst, PlaceholderRegion, Region, Ty, TyKind,
};
+use crate::next_solver::{PlaceholderType, infer::InferCtxt};
/// When we canonicalize a value to form a query, we wind up replacing
/// various parts of it with canonical variables. This struct stores
@@ -498,7 +498,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
{
let base = Canonical {
max_universe: UniverseIndex::ROOT,
- variables: CanonicalVars::empty(tcx),
+ var_kinds: CanonicalVarKinds::empty(tcx),
value: (),
};
Canonicalizer::canonicalize_with_base(
@@ -539,7 +539,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
tcx,
canonicalize_mode: canonicalize_region_mode,
needs_canonical_flags,
- variables: SmallVec::from_slice(base.variables.as_slice()),
+ variables: SmallVec::from_slice(base.var_kinds.as_slice()),
query_state,
indices: FxHashMap::default(),
sub_root_lookup_table: Default::default(),
@@ -562,7 +562,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
debug_assert!(!out_value.has_infer() && !out_value.has_placeholders());
let canonical_variables =
- CanonicalVars::new_from_slice(&canonicalizer.universe_canonicalized_variables());
+ CanonicalVarKinds::new_from_slice(&canonicalizer.universe_canonicalized_variables());
let max_universe = canonical_variables
.iter()
@@ -570,7 +570,7 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
.max()
.unwrap_or(UniverseIndex::ROOT);
- Canonical { max_universe, variables: canonical_variables, value: (base.value, out_value) }
+ Canonical { max_universe, var_kinds: canonical_variables, value: (base.value, out_value) }
}
/// Creates a canonical variable replacing `kind` from the input,
@@ -670,22 +670,22 @@ impl<'cx, 'db> Canonicalizer<'cx, 'db> {
CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
- CanonicalVarKind::PlaceholderTy(Placeholder {
- universe: reverse_universe_map[&placeholder.universe],
- ..placeholder
- })
+ CanonicalVarKind::PlaceholderTy(PlaceholderType::new(
+ reverse_universe_map[&placeholder.universe],
+ placeholder.bound,
+ ))
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
- CanonicalVarKind::PlaceholderRegion(Placeholder {
- universe: reverse_universe_map[&placeholder.universe],
- ..placeholder
- })
+ CanonicalVarKind::PlaceholderRegion(PlaceholderRegion::new(
+ reverse_universe_map[&placeholder.universe],
+ placeholder.bound,
+ ))
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
- CanonicalVarKind::PlaceholderConst(Placeholder {
- universe: reverse_universe_map[&placeholder.universe],
- ..placeholder
- })
+ CanonicalVarKind::PlaceholderConst(PlaceholderConst::new(
+ reverse_universe_map[&placeholder.universe],
+ placeholder.bound,
+ ))
}
})
.collect()
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 61d1e97746..1738552a8e 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -69,7 +69,7 @@ impl<'db, V> CanonicalExt<'db, V> for Canonical<'db, V> {
where
T: TypeFoldable<DbInterner<'db>>,
{
- assert_eq!(self.variables.len(), var_values.len());
+ assert_eq!(self.var_kinds.len(), var_values.len());
let value = projection_fn(&self.value);
instantiate_value(tcx, var_values, value)
}
@@ -90,15 +90,15 @@ where
value
} else {
let delegate = FnMutDelegate {
- regions: &mut |br: BoundRegion| match var_values[br.var].kind() {
+ regions: &mut |br: BoundRegion<'db>| match var_values[br.var].kind() {
GenericArgKind::Lifetime(l) => l,
r => panic!("{br:?} is a region but value is {r:?}"),
},
- types: &mut |bound_ty: BoundTy| match var_values[bound_ty.var].kind() {
+ types: &mut |bound_ty: BoundTy<'db>| match var_values[bound_ty.var].kind() {
GenericArgKind::Type(ty) => ty,
r => panic!("{bound_ty:?} is a type but value is {r:?}"),
},
- consts: &mut |bound_ct: BoundConst| match var_values[bound_ct.var].kind() {
+ consts: &mut |bound_ct: BoundConst<'db>| match var_values[bound_ct.var].kind() {
GenericArgKind::Const(ct) => ct,
c => panic!("{bound_ct:?} is a const but value is {c:?}"),
},
@@ -356,7 +356,7 @@ impl<'db> InferCtxt<'db> {
// result, then we can type the corresponding value from the
// input. See the example above.
let mut opt_values: IndexVec<BoundVar, Option<GenericArg<'db>>> =
- IndexVec::from_elem_n(None, query_response.variables.len());
+ IndexVec::from_elem_n(None, query_response.var_kinds.len());
for (original_value, result_value) in iter::zip(&original_values.var_values, result_values)
{
@@ -368,7 +368,7 @@ impl<'db> InferCtxt<'db> {
// more involved. They are also a lot rarer than region variables.
if let TyKind::Bound(index_kind, b) = result_value.kind()
&& !matches!(
- query_response.variables.as_slice()[b.var.as_usize()],
+ query_response.var_kinds.as_slice()[b.var.as_usize()],
CanonicalVarKind::Ty { .. }
)
{
@@ -398,7 +398,7 @@ impl<'db> InferCtxt<'db> {
// given variable in the loop above, use that. Otherwise, use
// a fresh inference variable.
let interner = self.interner;
- let variables = query_response.variables;
+ let variables = query_response.var_kinds;
let var_values =
CanonicalVarValues::instantiate(interner, variables, |var_values, kind| {
if kind.universe() != UniverseIndex::ROOT {
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 a0420a5a00..1fefc0f265 100644
--- a/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/canonical/mod.rs
@@ -23,7 +23,7 @@
use crate::next_solver::{
ArgOutlivesPredicate, Canonical, CanonicalVarValues, Const, DbInterner, GenericArg,
- OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderTy, Region, Ty, TyKind,
+ OpaqueTypeKey, PlaceholderConst, PlaceholderRegion, PlaceholderType, Region, Ty, TyKind,
infer::InferCtxt,
};
use instantiate::CanonicalExt;
@@ -70,7 +70,7 @@ impl<'db> InferCtxt<'db> {
let var_values = CanonicalVarValues::instantiate(
self.interner,
- canonical.variables,
+ canonical.var_kinds,
|var_values, info| self.instantiate_canonical_var(info, var_values, |ui| universes[ui]),
);
let result = canonical.instantiate(self.interner, &var_values);
@@ -110,9 +110,9 @@ impl<'db> InferCtxt<'db> {
CanonicalVarKind::Float => self.next_float_var().into(),
- CanonicalVarKind::PlaceholderTy(PlaceholderTy { universe, bound }) => {
+ CanonicalVarKind::PlaceholderTy(PlaceholderType { universe, bound, .. }) => {
let universe_mapped = universe_map(universe);
- let placeholder_mapped = PlaceholderTy { universe: universe_mapped, bound };
+ let placeholder_mapped = PlaceholderType::new(universe_mapped, bound);
Ty::new_placeholder(self.interner, placeholder_mapped).into()
}
@@ -120,18 +120,16 @@ impl<'db> InferCtxt<'db> {
self.next_region_var_in_universe(universe_map(ui)).into()
}
- CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound }) => {
+ CanonicalVarKind::PlaceholderRegion(PlaceholderRegion { universe, bound, .. }) => {
let universe_mapped = universe_map(universe);
- let placeholder_mapped: crate::next_solver::Placeholder<
- crate::next_solver::BoundRegion,
- > = PlaceholderRegion { universe: universe_mapped, bound };
+ let placeholder_mapped = PlaceholderRegion::new(universe_mapped, bound);
Region::new_placeholder(self.interner, placeholder_mapped).into()
}
CanonicalVarKind::Const(ui) => self.next_const_var_in_universe(universe_map(ui)).into(),
- CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound }) => {
+ CanonicalVarKind::PlaceholderConst(PlaceholderConst { universe, bound, .. }) => {
let universe_mapped = universe_map(universe);
- let placeholder_mapped = PlaceholderConst { universe: universe_mapped, bound };
+ let placeholder_mapped = PlaceholderConst::new(universe_mapped, bound);
Const::new_placeholder(self.interner, placeholder_mapped).into()
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
index 21baacb116..a6352c7899 100644
--- a/crates/hir-ty/src/next_solver/infer/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -30,7 +30,7 @@ use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
pub use crate::next_solver::infer::traits::ObligationInspector;
use crate::next_solver::{
- ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, Predicate,
+ ArgOutlivesPredicate, BoundConst, BoundRegion, BoundTy, BoundVariableKind, Goal, Predicate,
SolverContext,
fold::BoundVarReplacerDelegate,
infer::{at::ToTrace, select::EvaluationResult, traits::PredicateObligation},
@@ -53,7 +53,7 @@ mod outlives;
pub mod region_constraints;
pub mod relate;
pub mod resolve;
-pub(crate) mod select;
+pub mod select;
pub(crate) mod snapshot;
pub(crate) mod traits;
mod type_variable;
@@ -366,12 +366,16 @@ impl<'db> InferCtxtBuilder<'db> {
where
T: TypeFoldable<DbInterner<'db>>,
{
- let infcx = self.build(input.typing_mode);
+ let infcx = self.build(input.typing_mode.0);
let (value, args) = infcx.instantiate_canonical(&input.canonical);
(infcx, value, args)
}
pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> {
+ // We do not allow creating an InferCtxt for an Interner without a crate, because this means
+ // an interner without a crate cannot access the cache, therefore constructing it doesn't need
+ // to reinit the cache, and we construct a lot of no-crate interners.
+ self.interner.expect_crate();
let InferCtxtBuilder { interner } = *self;
InferCtxt {
interner,
@@ -557,6 +561,16 @@ impl<'db> InferCtxt<'db> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_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);
+
+ let Some(use_cloned_def_id) = self.interner.lang_items().UseCloned else {
+ return false;
+ };
+
+ traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, use_cloned_def_id)
+ }
+
pub fn unresolved_variables(&self) -> Vec<Ty<'db>> {
let mut inner = self.inner.borrow_mut();
let mut vars: Vec<Ty<'db>> = inner
@@ -1098,9 +1112,9 @@ impl<'db> InferCtxt<'db> {
for bound_var_kind in bound_vars {
let arg: GenericArg<'db> = match bound_var_kind {
- BoundVarKind::Ty(_) => self.next_ty_var().into(),
- BoundVarKind::Region(_) => self.next_region_var().into(),
- BoundVarKind::Const => self.next_const_var().into(),
+ BoundVariableKind::Ty(_) => self.next_ty_var().into(),
+ BoundVariableKind::Region(_) => self.next_region_var().into(),
+ BoundVariableKind::Const => self.next_const_var().into(),
};
args.push(arg);
}
@@ -1110,13 +1124,13 @@ impl<'db> InferCtxt<'db> {
}
impl<'db> BoundVarReplacerDelegate<'db> for ToFreshVars<'db> {
- fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> {
self.args[br.var.index()].expect_region()
}
- fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> {
self.args[bt.var.index()].expect_ty()
}
- fn replace_const(&mut self, bv: BoundConst) -> Const<'db> {
+ fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> {
self.args[bv.var.index()].expect_const()
}
}
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 ae5930d55c..7bb39519f5 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
@@ -17,7 +17,7 @@ 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, PlaceholderTy, Region, Ty};
+use crate::next_solver::{AliasTy, Binder, DbInterner, ParamTy, PlaceholderType, Region, Ty};
#[derive(Debug, Clone, Default)]
pub struct RegionConstraintStorage<'db> {
@@ -122,7 +122,7 @@ pub struct Verify<'db> {
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum GenericKind<'db> {
Param(ParamTy),
- Placeholder(PlaceholderTy),
+ Placeholder(PlaceholderType<'db>),
Alias(AliasTy<'db>),
}
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 0f7ae99fa4..d621dd4906 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
@@ -531,7 +531,7 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
}
}
- TyKind::Alias(_, data) => match self.structurally_relate_aliases {
+ TyKind::Alias(data) => match self.structurally_relate_aliases {
StructurallyRelateAliases::No => self.generalize_alias_ty(data),
StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t),
},
diff --git a/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
index c523751e03..cfa864406c 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/higher_ranked.rs
@@ -8,7 +8,7 @@ use crate::next_solver::fold::FnMutDelegate;
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::{
Binder, BoundConst, BoundRegion, BoundTy, Const, DbInterner, PlaceholderConst,
- PlaceholderRegion, PlaceholderTy, Region, Ty,
+ PlaceholderRegion, PlaceholderType, Region, Ty,
};
impl<'db> InferCtxt<'db> {
@@ -35,23 +35,14 @@ impl<'db> InferCtxt<'db> {
let next_universe = self.create_next_universe();
let delegate = FnMutDelegate {
- regions: &mut |br: BoundRegion| {
- Region::new_placeholder(
- self.interner,
- PlaceholderRegion { universe: next_universe, bound: br },
- )
+ regions: &mut |br: BoundRegion<'db>| {
+ Region::new_placeholder(self.interner, PlaceholderRegion::new(next_universe, br))
},
- types: &mut |bound_ty: BoundTy| {
- Ty::new_placeholder(
- self.interner,
- PlaceholderTy { universe: next_universe, bound: bound_ty },
- )
+ types: &mut |bound_ty: BoundTy<'db>| {
+ Ty::new_placeholder(self.interner, PlaceholderType::new(next_universe, bound_ty))
},
- consts: &mut |bound: BoundConst| {
- Const::new_placeholder(
- self.interner,
- PlaceholderConst { universe: next_universe, bound },
- )
+ consts: &mut |bound: BoundConst<'db>| {
+ Const::new_placeholder(self.interner, PlaceholderConst::new(next_universe, bound))
},
};
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 1abe6a93f4..3522827a9e 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/lattice.rs
@@ -165,8 +165,8 @@ impl<'db> TypeRelation<DbInterner<'db>> for LatticeOp<'_, 'db> {
}
(
- TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: a_def_id, .. }),
- TyKind::Alias(rustc_type_ir::Opaque, AliasTy { def_id: b_def_id, .. }),
+ TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id: a_def_id }, .. }),
+ TyKind::Alias(AliasTy { kind: rustc_type_ir::Opaque { def_id: b_def_id }, .. }),
) if a_def_id == b_def_id => super_combine_tys(infcx, self, a, b),
_ => super_combine_tys(infcx, self, a, b),
diff --git a/crates/hir-ty/src/next_solver/inspect.rs b/crates/hir-ty/src/next_solver/inspect.rs
index 5286977549..63a225b98f 100644
--- a/crates/hir-ty/src/next_solver/inspect.rs
+++ b/crates/hir-ty/src/next_solver/inspect.rs
@@ -1,5 +1,3 @@
-pub(crate) use rustc_next_trait_solver::solve::inspect::*;
-
use rustc_ast_ir::try_visit;
use rustc_next_trait_solver::{
canonical::instantiate_canonical_state,
@@ -329,10 +327,6 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
self.result
}
- pub(crate) fn source(&self) -> GoalSource {
- self.source
- }
-
pub(crate) fn depth(&self) -> usize {
self.depth
}
@@ -464,9 +458,10 @@ impl<'a, 'db> InspectGoal<'a, 'db> {
pub(crate) fn visit_with<V: ProofTreeVisitor<'db>>(&self, visitor: &mut V) -> V::Result {
if self.depth < visitor.config().max_depth {
try_visit!(visitor.visit_goal(self));
+ V::Result::output()
+ } else {
+ visitor.on_recursion_limit()
}
-
- V::Result::output()
}
}
@@ -479,6 +474,10 @@ pub(crate) trait ProofTreeVisitor<'db> {
}
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'db>) -> Self::Result;
+
+ fn on_recursion_limit(&mut self) -> Self::Result {
+ Self::Result::output()
+ }
}
impl<'db> InferCtxt<'db> {
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 5b81c7675d..4095dbe47d 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -25,27 +25,28 @@ use rustc_abi::{ReprFlags, ReprOptions};
use rustc_hash::FxHashSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_type_ir::{
- AliasTermKind, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex, EarlyBinder,
- FlagComputation, Flags, GenericArgKind, GenericTypeVisitable, ImplPolarity, InferTy, Interner,
- TraitRef, TypeFlags, TypeVisitableExt, UniverseIndex, Upcast, Variance,
+ AliasTermKind, AliasTy, AliasTyKind, BoundVar, CoroutineWitnessTypes, DebruijnIndex,
+ EarlyBinder, FlagComputation, Flags, 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},
- solve::SizedTraitKind,
+ solve::{AdtDestructorKind, SizedTraitKind},
};
use crate::{
FnAbi,
- db::{HirDatabase, InternedCoroutine, InternedCoroutineId},
+ db::{HirDatabase, InternedClosure, InternedCoroutineId},
lower::GenericPredicates,
method_resolution::TraitImpls,
next_solver::{
AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
- CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey,
- RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper,
- TypeAliasIdWrapper, UnevaluatedConst,
+ Consts, CoroutineClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap,
+ GeneralConstIdWrapper, LateParamRegion, OpaqueTypeKey, RegionAssumptions, ScalarInt,
+ SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
+ UnevaluatedConst,
util::{explicit_item_bounds, explicit_item_self_bounds},
},
};
@@ -53,14 +54,11 @@ use crate::{
use super::{
Binder, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause, ClauseKind, Clauses, Const,
ErrorGuaranteed, ExprConst, ExternalConstraints, GenericArg, GenericArgs, ParamConst, ParamEnv,
- ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty,
- TyKind, Tys, Valtree, ValueConst,
+ ParamTy, PredefinedOpaques, Predicate, SolverDefId, Term, Ty, TyKind, Tys, ValTree, ValueConst,
abi::Safety,
fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
generics::{Generics, generics},
- region::{
- BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region,
- },
+ region::{BoundRegion, BoundRegionKind, EarlyParamRegion, Region},
util::sizedness_constraint_for_ty,
};
@@ -329,6 +327,7 @@ unsafe impl Sync for DbInterner<'_> {}
impl<'db> DbInterner<'db> {
// FIXME(next-solver): remove this method
pub fn conjure() -> DbInterner<'db> {
+ // Here we can not reinit the cache since we do that when we attach the db.
crate::with_attached_db(|db| DbInterner {
db: unsafe { std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db) },
krate: None,
@@ -341,10 +340,13 @@ impl<'db> DbInterner<'db> {
///
/// Elaboration is a special kind: it needs lang items (for `Sized`), therefore it needs `new_with()`.
pub fn new_no_crate(db: &'db dyn HirDatabase) -> Self {
+ // We do not reinit the cache here, since anything accessing the cache needs an InferCtxt,
+ // and we panic when trying to construct an InferCtxt for an Interner without a crate.
DbInterner { db, krate: None, lang_items: None }
}
pub fn new_with(db: &'db dyn HirDatabase, krate: Crate) -> DbInterner<'db> {
+ tls_cache::reinit_cache(db);
DbInterner {
db,
krate: Some(krate),
@@ -373,6 +375,11 @@ impl<'db> DbInterner<'db> {
pub fn default_types<'a>(&self) -> &'a crate::next_solver::DefaultAny<'db> {
crate::next_solver::default_types(self.db)
}
+
+ #[inline]
+ pub(crate) fn expect_crate(&self) -> Crate {
+ self.krate.expect("should have a crate")
+ }
}
// This is intentionally left as `()`
@@ -390,43 +397,15 @@ interned_slice!(
BoundVarKinds,
StoredBoundVarKinds,
bound_var_kinds,
- BoundVarKind,
- BoundVarKind,
+ BoundVariableKind<'db>,
+ BoundVariableKind<'static>,
);
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum BoundVarKind {
- Ty(BoundTyKind),
- Region(BoundRegionKind),
- Const,
-}
-
-impl BoundVarKind {
- pub fn expect_region(self) -> BoundRegionKind {
- match self {
- BoundVarKind::Region(lt) => lt,
- _ => panic!("expected a region, but found another kind"),
- }
- }
-
- pub fn expect_ty(self) -> BoundTyKind {
- match self {
- BoundVarKind::Ty(ty) => ty,
- _ => panic!("expected a type, but found another kind"),
- }
- }
-
- pub fn expect_const(self) {
- match self {
- BoundVarKind::Const => (),
- _ => panic!("expected a const, but found another kind"),
- }
- }
-}
+pub type BoundVariableKind<'db> = rustc_type_ir::BoundVariableKind<DbInterner<'db>>;
interned_slice!(
CanonicalVarsStorage,
- CanonicalVars,
+ CanonicalVarKinds,
StoredCanonicalVars,
canonical_vars,
CanonicalVarKind<'db>,
@@ -438,22 +417,6 @@ pub struct DepNodeIndex;
#[derive(Debug)]
pub struct Tracked<T: fmt::Debug + Clone>(T);
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct Placeholder<T> {
- pub universe: UniverseIndex,
- pub bound: T,
-}
-
-impl<T: std::fmt::Debug> std::fmt::Debug for Placeholder<T> {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
- if self.universe == UniverseIndex::ROOT {
- write!(f, "!{:?}", self.bound)
- } else {
- write!(f, "!{}_{:?}", self.universe.index(), self.bound)
- }
- }
-}
-
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct AllocId;
@@ -762,17 +725,26 @@ impl<'db> inherent::AdtDef<DbInterner<'db>> for AdtDef {
.transpose()
}
- fn destructor(
- self,
- _interner: DbInterner<'db>,
- ) -> Option<rustc_type_ir::solve::AdtDestructorKind> {
- // FIXME(next-solver)
- None
+ 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()
+ }
+
+ fn field_representing_type_info(
+ self,
+ _interner: DbInterner<'db>,
+ _args: GenericArgs<'db>,
+ ) -> Option<rustc_type_ir::FieldInfo<DbInterner<'db>>> {
+ // FIXME
+ None
+ }
}
impl fmt::Debug for AdtDef {
@@ -806,11 +778,16 @@ impl<'db> inherent::Features<DbInterner<'db>> for Features {
false
}
- fn associated_const_equality(self) -> bool {
+ fn feature_bound_holds_in_crate(self, _symbol: Symbol) -> bool {
false
}
+}
- fn feature_bound_holds_in_crate(self, _symbol: ()) -> bool {
+#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, GenericTypeVisitable)]
+pub struct Symbol;
+
+impl<'db> inherent::Symbol<DbInterner<'db>> for Symbol {
+ fn is_kw_underscore_lifetime(self) -> bool {
false
}
}
@@ -1022,7 +999,7 @@ impl<'db> Interner for DbInterner<'db> {
type ForeignId = TypeAliasIdWrapper;
type FunctionId = CallableIdWrapper;
type ClosureId = ClosureIdWrapper;
- type CoroutineClosureId = CoroutineIdWrapper;
+ type CoroutineClosureId = CoroutineClosureIdWrapper;
type CoroutineId = CoroutineIdWrapper;
type AdtId = AdtIdWrapper;
type ImplId = AnyImplId;
@@ -1036,7 +1013,6 @@ impl<'db> Interner for DbInterner<'db> {
type Term = Term<'db>;
type BoundVarKinds = BoundVarKinds<'db>;
- type BoundVarKind = BoundVarKind;
type PredefinedOpaques = PredefinedOpaques<'db>;
@@ -1047,13 +1023,13 @@ impl<'db> Interner for DbInterner<'db> {
PredefinedOpaques::new_from_slice(data)
}
- type CanonicalVarKinds = CanonicalVars<'db>;
+ type CanonicalVarKinds = CanonicalVarKinds<'db>;
fn mk_canonical_var_kinds(
self,
kinds: &[rustc_type_ir::CanonicalVarKind<Self>],
) -> Self::CanonicalVarKinds {
- CanonicalVars::new_from_slice(kinds)
+ CanonicalVarKinds::new_from_slice(kinds)
}
type ExternalConstraints = ExternalConstraints<'db>;
@@ -1073,9 +1049,7 @@ impl<'db> Interner for DbInterner<'db> {
type Tys = Tys<'db>;
type FnInputTys = &'db [Ty<'db>];
type ParamTy = ParamTy;
- type BoundTy = BoundTy;
- type PlaceholderTy = PlaceholderTy;
- type Symbol = ();
+ type Symbol = Symbol;
type ErrorGuaranteed = ErrorGuaranteed;
type BoundExistentialPredicates = BoundExistentialPredicates<'db>;
@@ -1086,18 +1060,16 @@ impl<'db> Interner for DbInterner<'db> {
type Abi = FnAbi;
type Const = Const<'db>;
- type PlaceholderConst = PlaceholderConst;
type ParamConst = ParamConst;
- type BoundConst = BoundConst;
type ValueConst = ValueConst<'db>;
- type ValTree = Valtree<'db>;
+ type ValTree = ValTree<'db>;
+ type Consts = Consts<'db>;
+ type ScalarInt = ScalarInt;
type ExprConst = ExprConst;
type Region = Region<'db>;
type EarlyParamRegion = EarlyParamRegion;
- type LateParamRegion = LateParamRegion;
- type BoundRegion = BoundRegion;
- type PlaceholderRegion = PlaceholderRegion;
+ type LateParamRegion = LateParamRegion<'db>;
type RegionAssumptions = RegionAssumptions<'db>;
@@ -1148,7 +1120,8 @@ impl<'db> Interner for DbInterner<'db> {
self,
f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache<Self>) -> R,
) -> R {
- tls_cache::with_cache(self.db, f)
+ // We make sure to reinit the cache when constructing the Interner.
+ tls_cache::borrow_assume_valid(self.db, f)
}
fn canonical_param_env_cache_get_or_insert<R>(
@@ -1198,6 +1171,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::BuiltinDeriveImplId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
| SolverDefId::AnonConstId(_) => {
return VariancesOf::empty(self);
}
@@ -1230,22 +1204,6 @@ impl<'db> Interner for DbInterner<'db> {
AdtDef::new(def_id.0, self)
}
- fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy<Self>) -> AliasTyKind {
- match alias.def_id {
- SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque,
- SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
- ItemContainerId::ImplId(impl_)
- if ImplSignature::of(self.db, impl_).target_trait.is_none() =>
- {
- AliasTyKind::Inherent
- }
- ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => AliasTyKind::Projection,
- _ => AliasTyKind::Free,
- },
- _ => unimplemented!("Unexpected alias: {:?}", alias.def_id),
- }
- }
-
fn alias_term_kind(
self,
alias: rustc_type_ir::AliasTerm<Self>,
@@ -1315,10 +1273,13 @@ impl<'db> Interner for DbInterner<'db> {
SolverDefId::TypeAliasId(it) => it.lookup(self.db()).container,
SolverDefId::ConstId(it) => it.lookup(self.db()).container,
SolverDefId::InternedClosureId(it) => {
- return self.db().lookup_intern_closure(it).0.generic_def(self.db()).into();
+ return it.loc(self.db).0.generic_def(self.db()).into();
}
SolverDefId::InternedCoroutineId(it) => {
- return self.db().lookup_intern_coroutine(it).0.generic_def(self.db()).into();
+ 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(_)
@@ -1342,7 +1303,7 @@ impl<'db> Interner for DbInterner<'db> {
50
}
- fn features(self) -> Self::Features {
+ fn features(self) -> Features {
Features
}
@@ -1356,7 +1317,7 @@ 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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+ 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 {
@@ -1365,16 +1326,17 @@ impl<'db> Interner for DbInterner<'db> {
hir_def::hir::Movability::Static => rustc_ast_ir::Movability::Static,
hir_def::hir::Movability::Movable => rustc_ast_ir::Movability::Movable,
},
- hir_def::hir::ClosureKind::Async => rustc_ast_ir::Movability::Static,
+ hir_def::hir::ClosureKind::AsyncBlock { .. } => rustc_ast_ir::Movability::Static,
_ => panic!("unexpected expression for a coroutine: {expr:?}"),
},
- hir_def::hir::Expr::Async { .. } => rustc_ast_ir::Movability::Static,
_ => panic!("unexpected expression for a coroutine: {expr:?}"),
}
}
fn coroutine_for_closure(self, def_id: Self::CoroutineClosureId) -> Self::CoroutineId {
- def_id
+ let InternedClosure(owner, coroutine_closure_expr) = def_id.0.loc(self.db);
+ let coroutine_expr = ExpressionStore::coroutine_for_closure(coroutine_closure_expr);
+ InternedCoroutineId::new(self.db, InternedClosure(owner, coroutine_expr)).into()
}
fn generics_require_sized_self(self, def_id: Self::DefId) -> bool {
@@ -1439,81 +1401,57 @@ impl<'db> Interner for DbInterner<'db> {
}
}
- #[tracing::instrument(level = "debug", skip(self), ret)]
fn predicates_of(
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- predicates_of(self.db, def_id).all_predicates().map_bound(|it| it.iter().copied())
+ predicates_of(self.db, def_id).all_predicates()
}
- #[tracing::instrument(level = "debug", skip(self), ret)]
fn own_predicates_of(
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>> {
- predicates_of(self.db, def_id).own_predicates().map_bound(|it| it.iter().copied())
+ predicates_of(self.db, def_id).own_explicit_predicates()
}
- #[tracing::instrument(skip(self), ret)]
fn explicit_super_predicates_of(
self,
def_id: Self::TraitId,
) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> {
- let is_self = |ty: Ty<'db>| match ty.kind() {
- rustc_type_ir::TyKind::Param(param) => param.index == 0,
- _ => false,
- };
-
- GenericPredicates::query_explicit(self.db, def_id.0.into()).map_bound(move |predicates| {
- predicates
- .iter()
- .copied()
- .filter(move |p| match p.kind().skip_binder() {
- // rustc has the following assertion:
- // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608
- ClauseKind::Trait(it) => is_self(it.self_ty()),
- ClauseKind::TypeOutlives(it) => is_self(it.0),
- ClauseKind::Projection(it) => is_self(it.self_ty()),
- ClauseKind::HostEffect(it) => is_self(it.self_ty()),
- _ => false,
- })
- .map(|p| (p, Span::dummy()))
- })
+ GenericPredicates::query(self.db, def_id.0.into())
+ .explicit_non_assoc_types_predicates()
+ .map_bound(move |predicates| {
+ predicates.filter(|p| is_clause_at_ty(p, is_ty_self)).map(|p| (p, Span::dummy()))
+ })
}
- #[tracing::instrument(skip(self), ret)]
fn explicit_implied_predicates_of(
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> {
- fn is_self_or_assoc(ty: Ty<'_>) -> bool {
- match ty.kind() {
- rustc_type_ir::TyKind::Param(param) => param.index == 0,
- rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias) => {
- is_self_or_assoc(alias.self_ty())
- }
- _ => false,
+ fn is_ty_assoc_of_self(ty: Ty<'_>) -> bool {
+ // FIXME: Is this correct wrt. combined kind of assoc type bounds, i.e. `where Self::Assoc: Trait<Assoc2: Trait>`
+ // wrt. `Assoc2`, which we should exclude?
+ if let TyKind::Alias(alias @ AliasTy { kind: AliasTyKind::Projection { .. }, .. }) =
+ ty.kind()
+ {
+ is_ty_assoc_of_self(alias.self_ty())
+ } else {
+ is_ty_self(ty)
}
}
- predicates_of(self.db, def_id).explicit_implied_predicates().map_bound(|predicates| {
- predicates
- .iter()
- .copied()
- .filter(|p| match p.kind().skip_binder() {
- ClauseKind::Trait(it) => is_self_or_assoc(it.self_ty()),
- ClauseKind::TypeOutlives(it) => is_self_or_assoc(it.0),
- ClauseKind::Projection(it) => is_self_or_assoc(it.self_ty()),
- ClauseKind::HostEffect(it) => is_self_or_assoc(it.self_ty()),
- // FIXME: Not sure is this correct to allow other clauses but we might replace
- // `generic_predicates_ns` query here with something closer to rustc's
- // `implied_bounds_with_filter`, which is more granular lowering than this
- // "lower at once and then filter" implementation.
- _ => true,
- })
- .map(|p| (p, Span::dummy()))
- })
+ let predicates = predicates_of(self.db, def_id);
+ let non_assoc_types = predicates
+ .explicit_non_assoc_types_predicates()
+ .skip_binder()
+ .filter(|p| is_clause_at_ty(p, is_ty_self));
+ let assoc_types = predicates
+ .explicit_assoc_types_predicates()
+ .skip_binder()
+ .filter(|p| is_clause_at_ty(p, is_ty_assoc_of_self));
+ EarlyBinder::bind(non_assoc_types.chain(assoc_types).map(|it| (it, Span::dummy())))
}
fn impl_super_outlives(
@@ -1550,7 +1488,7 @@ impl<'db> Interner for DbInterner<'db> {
fn require_lang_item(self, lang_item: SolverLangItem) -> Self::DefId {
let lang_items = self.lang_items();
let lang_item = match lang_item {
- SolverLangItem::AsyncFnKindUpvars => unimplemented!(),
+ SolverLangItem::AsyncFnKindUpvars => lang_items.AsyncFnKindUpvars,
SolverLangItem::AsyncFnOnceOutput => lang_items.AsyncFnOnceOutput,
SolverLangItem::CallOnceFuture => lang_items.CallOnceFuture,
SolverLangItem::CallRefFuture => lang_items.CallRefFuture,
@@ -1561,6 +1499,8 @@ impl<'db> Interner for DbInterner<'db> {
SolverLangItem::DynMetadata => {
return lang_items.DynMetadata.expect("Lang item required but not found.").into();
}
+ SolverLangItem::FieldBase => lang_items.FieldBase,
+ SolverLangItem::FieldType => lang_items.FieldType,
};
lang_item.expect("Lang item required but not found.").into()
}
@@ -1569,13 +1509,13 @@ impl<'db> Interner for DbInterner<'db> {
let lang_items = self.lang_items();
let lang_item = match lang_item {
SolverTraitLangItem::AsyncFn => lang_items.AsyncFn,
- SolverTraitLangItem::AsyncFnKindHelper => unimplemented!(),
+ 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 => unimplemented!(),
+ SolverTraitLangItem::AsyncIterator => lang_items.AsyncIterator,
SolverTraitLangItem::Clone => lang_items.Clone,
SolverTraitLangItem::Copy => lang_items.Copy,
SolverTraitLangItem::Coroutine => lang_items.Coroutine,
@@ -1586,7 +1526,7 @@ impl<'db> Interner for DbInterner<'db> {
SolverTraitLangItem::FnMut => lang_items.FnMut,
SolverTraitLangItem::FnOnce => lang_items.FnOnce,
SolverTraitLangItem::FnPtrTrait => lang_items.FnPtrTrait,
- SolverTraitLangItem::FusedIterator => unimplemented!(),
+ SolverTraitLangItem::FusedIterator => lang_items.FusedIterator,
SolverTraitLangItem::Future => lang_items.Future,
SolverTraitLangItem::Iterator => lang_items.Iterator,
SolverTraitLangItem::PointeeTrait => lang_items.PointeeTrait,
@@ -1597,10 +1537,9 @@ impl<'db> Interner for DbInterner<'db> {
SolverTraitLangItem::Tuple => lang_items.Tuple,
SolverTraitLangItem::Unpin => lang_items.Unpin,
SolverTraitLangItem::Unsize => lang_items.Unsize,
- SolverTraitLangItem::BikeshedGuaranteedNoDrop => {
- unimplemented!()
- }
+ SolverTraitLangItem::BikeshedGuaranteedNoDrop => lang_items.BikeshedGuaranteedNoDrop,
SolverTraitLangItem::TrivialClone => lang_items.TrivialClone,
+ SolverTraitLangItem::Field => lang_items.Field,
};
lang_item.expect("Lang item required but not found.").into()
}
@@ -1628,6 +1567,7 @@ impl<'db> Interner for DbInterner<'db> {
AsyncIterator,
BikeshedGuaranteedNoDrop,
FusedIterator,
+ Field,
AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver.
}
@@ -1673,6 +1613,8 @@ impl<'db> Interner for DbInterner<'db> {
ignore = {
AsyncFnKindUpvars,
DynMetadata,
+ FieldBase,
+ FieldType,
}
Metadata,
@@ -1697,6 +1639,8 @@ impl<'db> Interner for DbInterner<'db> {
CallRefFuture,
CallOnceFuture,
AsyncFnOnceOutput,
+ FieldBase,
+ FieldType,
}
DynMetadata,
@@ -1715,6 +1659,7 @@ impl<'db> Interner for DbInterner<'db> {
AsyncIterator,
BikeshedGuaranteedNoDrop,
FusedIterator,
+ Field,
AsyncFnOnceOutput, // This is incorrectly marked as `SolverTraitLangItem`, and is not used by the solver.
}
@@ -1789,6 +1734,7 @@ impl<'db> Interner for DbInterner<'db> {
| SolverDefId::StaticId(_)
| SolverDefId::InternedClosureId(_)
| SolverDefId::InternedCoroutineId(_)
+ | SolverDefId::InternedCoroutineClosureId(_)
| SolverDefId::InternedOpaqueTyId(_)
| SolverDefId::EnumVariantId(_)
| SolverDefId::AnonConstId(_)
@@ -1891,7 +1837,7 @@ impl<'db> Interner for DbInterner<'db> {
//
// Impls which apply to an alias after normalization are handled by
// `assemble_candidates_after_normalizing_self_ty`.
- TyKind::Alias(_, _) | TyKind::Placeholder(..) | TyKind::Error(_) => (),
+ TyKind::Alias(..) | TyKind::Placeholder(..) | TyKind::Error(_) => (),
// FIXME: These should ideally not exist as a self type. It would be nice for
// the builtin auto trait impls of coroutines to instead directly recurse
@@ -1984,12 +1930,6 @@ impl<'db> Interner for DbInterner<'db> {
trait_data.flags.contains(TraitFlags::FUNDAMENTAL)
}
- fn trait_may_be_implemented_via_object(self, _trait_def_id: Self::TraitId) -> bool {
- // FIXME(next-solver): should check the `TraitFlags` for
- // the `#[rustc_do_not_implement_via_object]` flag
- true
- }
-
fn is_impl_trait_in_trait(self, _def_id: Self::DefId) -> bool {
// FIXME(next-solver)
false
@@ -2002,7 +1942,7 @@ 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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+ let InternedClosure(owner, expr_id) = def_id.0.loc(self.db);
let store = ExpressionStore::of(self.db, owner);
matches!(
store[expr_id],
@@ -2016,12 +1956,14 @@ impl<'db> Interner for DbInterner<'db> {
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 InternedCoroutine(owner, expr_id) = def_id.0.loc(self.db);
+ 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::Async, .. }
- | hir_def::hir::Expr::Async { .. }
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. },
+ ..
+ }
)
}
@@ -2086,32 +2028,33 @@ impl<'db> Interner for DbInterner<'db> {
) -> rustc_type_ir::Binder<Self, T> {
struct Anonymize<'a, 'db> {
interner: DbInterner<'db>,
- map: &'a mut FxIndexMap<BoundVar, BoundVarKind>,
+ map: &'a mut FxIndexMap<BoundVar, BoundVariableKind<'db>>,
}
impl<'db> BoundVarReplacerDelegate<'db> for Anonymize<'_, 'db> {
- fn replace_region(&mut self, br: BoundRegion) -> Region<'db> {
+ fn replace_region(&mut self, br: BoundRegion<'db>) -> Region<'db> {
let entry = self.map.entry(br.var);
let index = entry.index();
let var = BoundVar::from_usize(index);
- let kind = (*entry.or_insert_with(|| BoundVarKind::Region(BoundRegionKind::Anon)))
- .expect_region();
+ let kind = (*entry
+ .or_insert_with(|| BoundVariableKind::Region(BoundRegionKind::Anon)))
+ .expect_region();
let br = BoundRegion { var, kind };
Region::new_bound(self.interner, DebruijnIndex::ZERO, br)
}
- fn replace_ty(&mut self, bt: BoundTy) -> Ty<'db> {
+ fn replace_ty(&mut self, bt: BoundTy<'db>) -> Ty<'db> {
let entry = self.map.entry(bt.var);
let index = entry.index();
let var = BoundVar::from_usize(index);
- let kind =
- (*entry.or_insert_with(|| BoundVarKind::Ty(BoundTyKind::Anon))).expect_ty();
+ let kind = (*entry.or_insert_with(|| BoundVariableKind::Ty(BoundTyKind::Anon)))
+ .expect_ty();
Ty::new_bound(self.interner, DebruijnIndex::ZERO, BoundTy { var, kind })
}
- fn replace_const(&mut self, bv: BoundConst) -> Const<'db> {
+ fn replace_const(&mut self, bv: BoundConst<'db>) -> Const<'db> {
let entry = self.map.entry(bv.var);
let index = entry.index();
let var = BoundVar::from_usize(index);
- let () = (*entry.or_insert_with(|| BoundVarKind::Const)).expect_const();
- Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst { var })
+ let () = (*entry.or_insert_with(|| BoundVariableKind::Const)).expect_const();
+ Const::new_bound(self.interner, DebruijnIndex::ZERO, BoundConst::new(var))
}
}
@@ -2144,16 +2087,15 @@ impl<'db> Interner for DbInterner<'db> {
body.exprs().for_each(|(expr_id, expr)| {
if matches!(
expr,
- hir_def::hir::Expr::Async { .. }
- | hir_def::hir::Expr::Closure {
- closure_kind: hir_def::hir::ClosureKind::Async
- | hir_def::hir::ClosureKind::Coroutine(_),
- ..
- }
+ hir_def::hir::Expr::Closure {
+ closure_kind: hir_def::hir::ClosureKind::AsyncBlock { .. }
+ | hir_def::hir::ClosureKind::Coroutine(_),
+ ..
+ }
) {
let coroutine = InternedCoroutineId::new(
self.db,
- InternedCoroutine(ExpressionStoreOwnerId::Body(def_id), expr_id),
+ InternedClosure(ExpressionStoreOwnerId::Body(def_id), expr_id),
);
result.push(coroutine.into());
}
@@ -2292,6 +2234,52 @@ impl<'db> Interner for DbInterner<'db> {
UnevaluatedConst { def: GeneralConstIdWrapper(id), args: GenericArgs::empty(self) },
))
}
+
+ fn anon_const_kind(self, _def_id: Self::DefId) -> rustc_type_ir::AnonConstKind {
+ // FIXME
+ rustc_type_ir::AnonConstKind::GCE
+ }
+
+ fn alias_ty_kind_from_def_id(self, def_id: Self::DefId) -> AliasTyKind<DbInterner<'db>> {
+ match def_id {
+ SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
+ ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => {
+ AliasTyKind::Free { def_id }
+ }
+ ItemContainerId::ImplId(_) => AliasTyKind::Inherent { def_id },
+ ItemContainerId::TraitId(_) => AliasTyKind::Projection { def_id },
+ },
+ SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque { def_id },
+ _ => unreachable!(),
+ }
+ }
+
+ fn closure_is_const(self, _def_id: Self::ClosureId) -> bool {
+ // FIXME
+ false
+ }
+
+ fn item_name(self, _item_index: Self::DefId) -> Self::Symbol {
+ Symbol
+ }
+}
+
+fn is_ty_self(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ TyKind::Param(param) => param.index == 0,
+ _ => false,
+ }
+}
+fn is_clause_at_ty(p: &Clause<'_>, filter: impl FnOnce(Ty<'_>) -> bool) -> bool {
+ match p.kind().skip_binder() {
+ // rustc has the following assertion:
+ // https://github.com/rust-lang/rust/blob/52618eb338609df44978b0ca4451ab7941fd1c7a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs#L525-L608
+ ClauseKind::Trait(it) => filter(it.self_ty()),
+ ClauseKind::TypeOutlives(it) => filter(it.0),
+ ClauseKind::Projection(it) => filter(it.self_ty()),
+ ClauseKind::HostEffect(it) => filter(it.self_ty()),
+ _ => false,
+ }
}
impl<'db> DbInterner<'db> {
@@ -2303,14 +2291,14 @@ impl<'db> DbInterner<'db> {
self.replace_escaping_bound_vars_uncached(
value,
FnMutDelegate {
- regions: &mut |r: BoundRegion| {
+ regions: &mut |r: BoundRegion<'db>| {
Region::new_bound(
self,
DebruijnIndex::ZERO,
BoundRegion { var: shift_bv(r.var), kind: r.kind },
)
},
- types: &mut |t: BoundTy| {
+ types: &mut |t: BoundTy<'db>| {
Ty::new_bound(
self,
DebruijnIndex::ZERO,
@@ -2318,7 +2306,7 @@ impl<'db> DbInterner<'db> {
)
},
consts: &mut |c| {
- Const::new_bound(self, DebruijnIndex::ZERO, BoundConst { var: shift_bv(c.var) })
+ Const::new_bound(self, DebruijnIndex::ZERO, BoundConst::new(shift_bv(c.var)))
},
},
)
@@ -2366,6 +2354,22 @@ impl<'db> DbInterner<'db> {
abi,
}
}
+
+ /// `mk_fn_sig`, but with a safe Rust ABI, and no C-variadic argument.
+ pub fn mk_fn_sig_safe_rust_abi<I>(self, inputs: I, output: Ty<'db>) -> FnSig<'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,
+ }
+ }
}
fn predicates_of(db: &dyn HirDatabase, def_id: SolverDefId) -> &GenericPredicates {
@@ -2422,6 +2426,7 @@ TrivialTypeTraversalImpls! {
CallableIdWrapper,
ClosureIdWrapper,
CoroutineIdWrapper,
+ CoroutineClosureIdWrapper,
AdtIdWrapper,
AnyImplId,
GeneralConstIdWrapper,
@@ -2430,17 +2435,9 @@ TrivialTypeTraversalImpls! {
Span,
ParamConst,
ParamTy,
- BoundRegion,
- Placeholder<BoundRegion>,
- Placeholder<BoundTy>,
- Placeholder<BoundVar>,
- Placeholder<BoundConst>,
- BoundVarKind,
EarlyParamRegion,
- LateParamRegion,
AdtDef,
- BoundTy,
- BoundConst,
+ ScalarInt,
}
mod tls_db {
@@ -2493,6 +2490,7 @@ mod tls_db {
}
let _guard = DbGuard::new(self, db);
+ super::tls_cache::reinit_cache(db);
op()
}
@@ -2515,10 +2513,14 @@ mod tls_db {
#[inline]
fn drop(&mut self) {
self.state.database.set(self.prev);
+ if let Some(prev) = self.prev {
+ super::tls_cache::reinit_cache(unsafe { prev.as_ref() });
+ }
}
}
let _guard = DbGuard::new(self, db);
+ super::tls_cache::reinit_cache(db);
op()
}
@@ -2573,22 +2575,38 @@ mod tls_cache {
static GLOBAL_CACHE: RefCell<Option<Cache>> = const { RefCell::new(None) };
}
- pub(super) fn with_cache<'db, T>(
- db: &'db dyn HirDatabase,
- f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T,
- ) -> T {
+ pub(super) fn reinit_cache(db: &dyn HirDatabase) {
GLOBAL_CACHE.with_borrow_mut(|handle| {
let (db_nonce, revision) = db.nonce_and_revision();
- let handle = match handle {
+ match handle {
Some(handle) => {
if handle.revision != revision || db_nonce != handle.db_nonce {
*handle = Cache { cache: GlobalCache::default(), revision, db_nonce };
}
- handle
}
- None => handle.insert(Cache { cache: GlobalCache::default(), revision, db_nonce }),
+ None => *handle = Some(Cache { cache: GlobalCache::default(), revision, db_nonce }),
+ }
+ })
+ }
+
+ pub(super) fn borrow_assume_valid<'db, T>(
+ db: &'db dyn HirDatabase,
+ f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T,
+ ) -> T {
+ if cfg!(debug_assertions) {
+ let get_state = || {
+ GLOBAL_CACHE.with_borrow(|handle| {
+ handle.as_ref().map(|handle| (handle.db_nonce, handle.revision))
+ })
};
+ let old_state = get_state();
+ reinit_cache(db);
+ let new_state = get_state();
+ assert_eq!(old_state, new_state, "you assumed the cache is valid!");
+ }
+ GLOBAL_CACHE.with_borrow_mut(|handle| {
+ let handle = handle.as_mut().expect("you assumed the cache is valid!");
// SAFETY: No idea
f(unsafe {
std::mem::transmute::<
@@ -2633,13 +2651,15 @@ pub unsafe fn collect_ty_garbage() {
let mut gc = intern::GarbageCollector::default();
gc.add_storage::<super::consts::ConstInterned>();
- gc.add_storage::<super::consts::ValtreeInterned>();
+ gc.add_storage::<super::consts::ValTreeInterned>();
+ gc.add_storage::<super::allocation::AllocationInterned>();
gc.add_storage::<PatternInterned>();
gc.add_storage::<super::opaques::ExternalConstraintsInterned>();
gc.add_storage::<super::predicate::PredicateInterned>();
gc.add_storage::<super::region::RegionInterned>();
gc.add_storage::<super::ty::TyInterned>();
+ gc.add_slice_storage::<super::consts::ConstsStorage>();
gc.add_slice_storage::<super::predicate::ClausesStorage>();
gc.add_slice_storage::<super::generic_arg::GenericArgsStorage>();
gc.add_slice_storage::<BoundVarKindsStorage>();
@@ -2674,7 +2694,8 @@ macro_rules! impl_gc_visit {
impl_gc_visit!(
super::consts::ConstInterned,
- super::consts::ValtreeInterned,
+ super::consts::ValTreeInterned,
+ super::allocation::AllocationInterned,
PatternInterned,
super::opaques::ExternalConstraintsInterned,
super::predicate::PredicateInterned,
@@ -2713,4 +2734,5 @@ impl_gc_visit_slice!(
super::predicate::BoundExistentialPredicatesStorage,
super::region::RegionAssumptionsStorage,
super::ty::TysStorage,
+ super::consts::ConstsStorage,
);
diff --git a/crates/hir-ty/src/next_solver/ir_print.rs b/crates/hir-ty/src/next_solver/ir_print.rs
index e0732b3473..5dd372a367 100644
--- a/crates/hir-ty/src/next_solver/ir_print.rs
+++ b/crates/hir-ty/src/next_solver/ir_print.rs
@@ -12,7 +12,7 @@ impl<'db> IrPrint<ty::AliasTy<Self>> for DbInterner<'db> {
}
fn print_debug(t: &ty::AliasTy<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.kind.def_id() {
SolverDefId::TypeAliasId(id) => fmt.write_str(&format!(
"AliasTy({:?}[{:?}])",
TypeAliasSignature::of(db, id).name.as_str(),
diff --git a/crates/hir-ty/src/next_solver/normalize.rs b/crates/hir-ty/src/next_solver/normalize.rs
index bd678b3e78..5d8f3fe519 100644
--- a/crates/hir-ty/src/next_solver/normalize.rs
+++ b/crates/hir-ty/src/next_solver/normalize.rs
@@ -229,6 +229,7 @@ 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/region.rs b/crates/hir-ty/src/next_solver/region.rs
index dc2441f76e..3f0aebac2d 100644
--- a/crates/hir-ty/src/next_solver/region.rs
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -1,12 +1,12 @@
//! Things related to regions.
use hir_def::LifetimeParamId;
-use intern::{Interned, InternedRef, Symbol, impl_internable};
+use intern::{Interned, InternedRef, impl_internable};
use macros::GenericTypeVisitable;
use rustc_type_ir::{
- BoundVar, BoundVarIndexKind, DebruijnIndex, Flags, GenericTypeVisitable, INNERMOST, RegionVid,
- TypeFlags, TypeFoldable, TypeVisitable,
- inherent::{IntoKind, PlaceholderLike, SliceLike},
+ BoundVarIndexKind, DebruijnIndex, Flags, GenericTypeVisitable, INNERMOST, RegionVid, TypeFlags,
+ TypeFoldable, TypeVisitable,
+ inherent::{IntoKind, SliceLike},
relate::Relate,
};
@@ -15,10 +15,7 @@ use crate::next_solver::{
interned_slice,
};
-use super::{
- SolverDefId,
- interner::{BoundVarKind, DbInterner, Placeholder},
-};
+use super::{SolverDefId, interner::DbInterner};
pub type RegionKind<'db> = rustc_type_ir::RegionKind<DbInterner<'db>>;
@@ -57,7 +54,7 @@ impl<'db> Region<'db> {
Region::new(interner, RegionKind::ReEarlyParam(early_bound_region))
}
- pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion) -> Self {
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderRegion<'db>) -> Self {
Region::new(interner, RegionKind::RePlaceholder(placeholder))
}
@@ -72,7 +69,7 @@ impl<'db> Region<'db> {
pub fn new_bound(
interner: DbInterner<'db>,
index: DebruijnIndex,
- bound: BoundRegion,
+ bound: BoundRegion<'db>,
) -> Region<'db> {
Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(index), bound))
}
@@ -147,7 +144,7 @@ impl<'db> Region<'db> {
}
}
-pub type PlaceholderRegion = Placeholder<BoundRegion>;
+pub type PlaceholderRegion<'db> = rustc_type_ir::PlaceholderRegion<DbInterner<'db>>;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct EarlyParamRegion {
@@ -156,7 +153,7 @@ pub struct EarlyParamRegion {
pub index: u32,
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericTypeVisitable)]
/// The parameter representation of late-bound function parameters, "some region
/// at least as big as the scope `fr.scope`".
///
@@ -165,50 +162,19 @@ pub struct EarlyParamRegion {
/// between others we use the `DefId` of the parameter. For this reason the `bound_region` field
/// should basically always be `BoundRegionKind::Named` as otherwise there is no way of telling
/// different parameters apart.
-pub struct LateParamRegion {
+pub struct LateParamRegion<'db> {
pub scope: SolverDefId,
- pub bound_region: BoundRegionKind,
+ pub bound_region: BoundRegionKind<'db>,
}
-impl std::fmt::Debug for LateParamRegion {
+impl std::fmt::Debug for LateParamRegion<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ReLateParam({:?}, {:?})", self.scope, self.bound_region)
}
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub enum BoundRegionKind {
- /// An anonymous region parameter for a given fn (&T)
- Anon,
-
- /// Named region parameters for functions (a in &'a T)
- ///
- /// The `DefId` is needed to distinguish free regions in
- /// the event of shadowing.
- Named(SolverDefId),
-
- /// Anonymous region for the implicit env pointer parameter
- /// to a closure
- ClosureEnv,
-}
-
-impl std::fmt::Debug for BoundRegionKind {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match *self {
- BoundRegionKind::Anon => write!(f, "BrAnon"),
- BoundRegionKind::Named(did) => {
- write!(f, "BrNamed({did:?})")
- }
- BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct BoundRegion {
- pub var: BoundVar,
- pub kind: BoundRegionKind,
-}
+pub type BoundRegion<'db> = rustc_type_ir::BoundRegion<DbInterner<'db>>;
+pub type BoundRegionKind<'db> = rustc_type_ir::BoundRegionKind<DbInterner<'db>>;
impl rustc_type_ir::inherent::ParamLike for EarlyParamRegion {
fn index(self) -> u32 {
@@ -223,45 +189,6 @@ impl std::fmt::Debug for EarlyParamRegion {
}
}
-impl<'db> rustc_type_ir::inherent::BoundVarLike<DbInterner<'db>> for BoundRegion {
- fn var(self) -> BoundVar {
- self.var
- }
-
- fn assert_eq(self, var: BoundVarKind) {
- assert_eq!(self.kind, var.expect_region())
- }
-}
-
-impl core::fmt::Debug for BoundRegion {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match &self.kind {
- BoundRegionKind::Anon => write!(f, "{:?}", self.var),
- BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
- BoundRegionKind::Named(def) => {
- write!(f, "{:?}.Named({:?})", self.var, def)
- }
- }
- }
-}
-
-impl BoundRegionKind {
- pub fn is_named(&self) -> bool {
- matches!(self, BoundRegionKind::Named(_))
- }
-
- pub fn get_name(&self) -> Option<Symbol> {
- None
- }
-
- pub fn get_id(&self) -> Option<SolverDefId> {
- match self {
- BoundRegionKind::Named(id) => Some(*id),
- _ => None,
- }
- }
-}
-
impl std::fmt::Debug for Region<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind().fmt(f)
@@ -323,15 +250,15 @@ impl<'db> Flags for Region<'db> {
impl<'db> rustc_type_ir::inherent::Region<DbInterner<'db>> for Region<'db> {
fn new_bound(
interner: DbInterner<'db>,
- debruijn: rustc_type_ir::DebruijnIndex,
- var: BoundRegion,
+ debruijn: DebruijnIndex,
+ var: BoundRegion<'db>,
) -> Self {
Region::new(interner, RegionKind::ReBound(BoundVarIndexKind::Bound(debruijn), var))
}
fn new_anon_bound(
interner: DbInterner<'db>,
- debruijn: rustc_type_ir::DebruijnIndex,
+ debruijn: DebruijnIndex,
var: rustc_type_ir::BoundVar,
) -> Self {
Region::new(
@@ -357,38 +284,11 @@ impl<'db> rustc_type_ir::inherent::Region<DbInterner<'db>> for Region<'db> {
interner.default_types().regions.statik
}
- fn new_placeholder(
- interner: DbInterner<'db>,
- var: <DbInterner<'db> as rustc_type_ir::Interner>::PlaceholderRegion,
- ) -> Self {
+ fn new_placeholder(interner: DbInterner<'db>, var: PlaceholderRegion<'db>) -> Self {
Region::new(interner, RegionKind::RePlaceholder(var))
}
}
-impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderRegion {
- type Bound = BoundRegion;
-
- fn universe(self) -> rustc_type_ir::UniverseIndex {
- self.universe
- }
-
- fn var(self) -> rustc_type_ir::BoundVar {
- self.bound.var
- }
-
- fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
- Placeholder { universe: ui, bound: self.bound }
- }
-
- fn new(ui: rustc_type_ir::UniverseIndex, bound: Self::Bound) -> Self {
- Placeholder { universe: ui, bound }
- }
-
- fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
- Placeholder { universe: ui, bound: BoundRegion { var, kind: BoundRegionKind::Anon } }
- }
-}
-
impl<'db, V: super::WorldExposer> GenericTypeVisitable<V> for Region<'db> {
fn generic_visit_with(&self, visitor: &mut V) {
if visitor.on_interned(self.interned).is_continue() {
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index 848bb110af..d45ac6c959 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -14,10 +14,13 @@ use rustc_type_ir::{
};
use tracing::debug;
-use crate::next_solver::{
- AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
- ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
- util::sizedness_fast_path,
+use crate::{
+ ParamEnvAndCrate,
+ next_solver::{
+ AliasTy, AnyImplId, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs,
+ ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, UnevaluatedConst,
+ fold::fold_tys, util::sizedness_fast_path,
+ },
};
use super::{
@@ -155,10 +158,11 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fold_tys(interner, clause, |ty| match ty.kind() {
// Replace all other mentions of the same opaque type with the hidden type,
// as the bounds must hold on the hidden type after all.
- TyKind::Alias(
- AliasTyKind::Opaque,
- AliasTy { def_id: def_id2, args: args2, .. },
- ) if def_id == def_id2 && args == args2 => hidden_ty,
+ TyKind::Alias(AliasTy {
+ kind: AliasTyKind::Opaque { def_id: def_id2 },
+ args: args2,
+ ..
+ }) if def_id == def_id2 && args == args2 => hidden_ty,
_ => ty,
})
};
@@ -247,25 +251,26 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fn evaluate_const(
&self,
- _param_env: ParamEnv<'db>,
- uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
- ) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
- match uv.def.0 {
+ param_env: ParamEnv<'db>,
+ uv: UnevaluatedConst<'db>,
+ ) -> Option<Const<'db>> {
+ let ec = match uv.def.0 {
GeneralConstId::ConstId(c) => {
let subst = uv.args;
- let ec = self.cx().db.const_eval(c, subst, None).ok()?;
- Some(ec)
- }
- GeneralConstId::StaticId(c) => {
- let ec = self.cx().db.const_eval_static(c).ok()?;
- Some(ec)
+ 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(_) => Some(Const::error(self.cx())),
- }
+ GeneralConstId::AnonConstId(_) => return Some(Const::error(self.cx())),
+ };
+ Some(Const::new_from_allocation(
+ self.interner,
+ &ec,
+ ParamEnvAndCrate { param_env, krate: self.interner.expect_crate() },
+ ))
}
fn compute_goal_fast_path(
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index 192cdb70ae..39abdaf079 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -17,8 +17,8 @@ use rustc_type_ir::{
IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
inherent::{
- AdtDef as _, BoundExistentialPredicates, BoundVarLike, Const as _, GenericArgs as _,
- IntoKind, ParamLike, PlaceholderLike, Safety as _, SliceLike, Ty as _,
+ AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike,
+ Safety as _, SliceLike, Ty as _,
},
relate::Relate,
solve::SizedTraitKind,
@@ -27,11 +27,12 @@ use rustc_type_ir::{
use crate::{
FnAbi,
- db::{HirDatabase, InternedCoroutine},
+ db::{HirDatabase, InternedClosure},
lower::GenericPredicates,
next_solver::{
AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
- CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region, TraitRef, TypeAliasIdWrapper,
+ CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region,
+ TraitRef, TypeAliasIdWrapper,
abi::Safety,
impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
util::{CoroutineArgsExt, IntegerTypeExt},
@@ -39,7 +40,7 @@ use crate::{
};
use super::{
- BoundVarKind, DbInterner, GenericArgs, Placeholder, SolverDefId,
+ DbInterner, GenericArgs, SolverDefId,
util::{FloatExt, IntegerExt},
};
@@ -96,7 +97,7 @@ impl<'db> Ty<'db> {
Ty::new(interner, TyKind::Param(ParamTy { id, index }))
}
- pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderTy) -> Self {
+ pub fn new_placeholder(interner: DbInterner<'db>, placeholder: PlaceholderType<'db>) -> Self {
Ty::new(interner, TyKind::Placeholder(placeholder))
}
@@ -176,7 +177,10 @@ impl<'db> Ty<'db> {
def_id: SolverDefId,
args: GenericArgs<'db>,
) -> Self {
- Ty::new_alias(interner, AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args))
+ Ty::new_alias(
+ interner,
+ AliasTy::new_from_args(interner, AliasTyKind::Opaque { def_id }, args),
+ )
}
/// Returns the `Size` for primitive types (bool, uint, int, char, float).
@@ -388,6 +392,11 @@ impl<'db> Ty<'db> {
matches!(self.kind(), TyKind::Char)
}
+ #[inline]
+ pub fn is_coroutine_closure(self) -> bool {
+ matches!(self.kind(), TyKind::CoroutineClosure(..))
+ }
+
/// A scalar type is one that denotes an atomic datum, with no sub-components.
/// (A RawPtr is scalar because it represents a non-managed pointer, so its
/// contents are abstract to rustc.)
@@ -438,6 +447,11 @@ impl<'db> Ty<'db> {
}
#[inline]
+ pub fn is_ref(self) -> bool {
+ matches!(self.kind(), TyKind::Ref(..))
+ }
+
+ #[inline]
pub fn is_array(self) -> bool {
matches!(self.kind(), TyKind::Array(..))
}
@@ -503,6 +517,14 @@ impl<'db> Ty<'db> {
}
}
+ /// Returns the type of `ty[i]`.
+ pub fn builtin_index(self) -> Option<Ty<'db>> {
+ match self.kind() {
+ TyKind::Array(ty, _) | TyKind::Slice(ty) => Some(ty),
+ _ => None,
+ }
+ }
+
/// Whether the type contains some non-lifetime, aka. type or const, error type.
pub fn references_non_lt_error(self) -> bool {
references_non_lt_error(&self)
@@ -527,7 +549,7 @@ impl<'db> Ty<'db> {
let unit_ty = Ty::new_unit(interner);
let return_ty = Ty::new_coroutine(
interner,
- coroutine_id,
+ interner.coroutine_for_closure(coroutine_id),
CoroutineArgs::new(
interner,
CoroutineArgsParts {
@@ -680,12 +702,11 @@ impl<'db> Ty<'db> {
let interner = DbInterner::new_no_crate(db);
match self.kind() {
- TyKind::Alias(AliasTyKind::Opaque, opaque_ty) => Some(
- opaque_ty
- .def_id
+ TyKind::Alias(AliasTy { kind: AliasTyKind::Opaque { def_id }, args, .. }) => Some(
+ def_id
.expect_opaque_ty()
.predicates(db)
- .iter_instantiated_copied(interner, opaque_ty.args.as_slice())
+ .iter_instantiated_copied(interner, args.as_slice())
.collect(),
),
TyKind::Param(param) => {
@@ -696,7 +717,7 @@ impl<'db> Ty<'db> {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::ArgumentImplTrait => {
let predicates = GenericPredicates::query_all(db, param.id.parent())
- .iter_identity_copied()
+ .iter_identity()
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == self,
ClauseKind::Projection(pred) => pred.self_ty() == self,
@@ -713,7 +734,7 @@ impl<'db> Ty<'db> {
}
}
TyKind::Coroutine(coroutine_id, _args) => {
- let InternedCoroutine(owner, _) = coroutine_id.0.loc(db);
+ let InternedClosure(owner, _) = coroutine_id.0.loc(db);
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.
@@ -742,9 +763,7 @@ impl<'db> Ty<'db> {
true
}
(TyKind::FnDef(def_id, ..), TyKind::FnDef(def_id2, ..)) => def_id == def_id2,
- (TyKind::Alias(_, alias, ..), TyKind::Alias(_, alias2)) => {
- alias.def_id == alias2.def_id
- }
+ (TyKind::Alias(alias), TyKind::Alias(alias2)) => alias.kind == alias2.kind,
(TyKind::Foreign(ty_id, ..), TyKind::Foreign(ty_id2, ..)) => ty_id == ty_id2,
(TyKind::Closure(id1, _), TyKind::Closure(id2, _)) => id1 == id2,
(TyKind::Ref(.., mutability), TyKind::Ref(.., mutability2))
@@ -857,7 +876,7 @@ impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> {
TyKind::CoroutineWitness(_did, ref args) => args.visit_with(visitor),
TyKind::Closure(_did, ref args) => args.visit_with(visitor),
TyKind::CoroutineClosure(_did, ref args) => args.visit_with(visitor),
- TyKind::Alias(_, ref data) => data.visit_with(visitor),
+ TyKind::Alias(ref data) => data.visit_with(visitor),
TyKind::Pat(ty, pat) => {
try_visit!(ty.visit_with(visitor));
@@ -924,7 +943,7 @@ impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> {
TyKind::CoroutineClosure(did, args) => {
TyKind::CoroutineClosure(did, args.try_fold_with(folder)?)
}
- TyKind::Alias(kind, data) => TyKind::Alias(kind, data.try_fold_with(folder)?),
+ TyKind::Alias(data) => TyKind::Alias(data.try_fold_with(folder)?),
TyKind::Pat(ty, pat) => {
TyKind::Pat(ty.try_fold_with(folder)?, pat.try_fold_with(folder)?)
}
@@ -973,7 +992,7 @@ impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> {
TyKind::CoroutineClosure(did, args) => {
TyKind::CoroutineClosure(did, args.fold_with(folder))
}
- TyKind::Alias(kind, data) => TyKind::Alias(kind, data.fold_with(folder)),
+ TyKind::Alias(data) => TyKind::Alias(data.fold_with(folder)),
TyKind::Pat(ty, pat) => TyKind::Pat(ty.fold_with(folder), pat.fold_with(folder)),
TyKind::Bool
@@ -1044,11 +1063,11 @@ impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> {
Ty::new(interner, TyKind::Param(param))
}
- fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderTy) -> Self {
+ fn new_placeholder(interner: DbInterner<'db>, param: PlaceholderType<'db>) -> Self {
Ty::new(interner, TyKind::Placeholder(param))
}
- fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundTy) -> Self {
+ fn new_bound(interner: DbInterner<'db>, debruijn: DebruijnIndex, var: BoundTy<'db>) -> Self {
Ty::new(interner, TyKind::Bound(BoundVarIndexKind::Bound(debruijn), var))
}
@@ -1069,8 +1088,8 @@ impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> {
)
}
- fn new_alias(interner: DbInterner<'db>, kind: AliasTyKind, alias_ty: AliasTy<'db>) -> Self {
- Ty::new(interner, TyKind::Alias(kind, alias_ty))
+ fn new_alias(interner: DbInterner<'db>, alias_ty: AliasTy<'db>) -> Self {
+ Ty::new(interner, TyKind::Alias(alias_ty))
}
fn new_error(interner: DbInterner<'db>, guar: ErrorGuaranteed) -> Self {
@@ -1107,7 +1126,7 @@ impl<'db> rustc_type_ir::inherent::Ty<DbInterner<'db>> for Ty<'db> {
fn new_coroutine_closure(
interner: DbInterner<'db>,
- def_id: CoroutineIdWrapper,
+ def_id: CoroutineClosureIdWrapper,
args: <DbInterner<'db> as Interner>::GenericArgs,
) -> Self {
Ty::new(interner, TyKind::CoroutineClosure(def_id, args))
@@ -1351,7 +1370,7 @@ impl<'db> rustc_type_ir::inherent::Tys<DbInterner<'db>> for Tys<'db> {
}
}
-pub type PlaceholderTy = Placeholder<BoundTy>;
+pub type PlaceholderType<'db> = rustc_type_ir::PlaceholderType<DbInterner<'db>>;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ParamTy {
@@ -1374,27 +1393,8 @@ impl std::fmt::Debug for ParamTy {
}
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct BoundTy {
- pub var: BoundVar,
- // FIXME: This is for diagnostics in rustc, do we really need it?
- pub kind: BoundTyKind,
-}
-
-impl std::fmt::Debug for BoundTy {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self.kind {
- BoundTyKind::Anon => write!(f, "{:?}", self.var),
- BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum BoundTyKind {
- Anon,
- Param(SolverDefId),
-}
+pub type BoundTy<'db> = rustc_type_ir::BoundTy<DbInterner<'db>>;
+pub type BoundTyKind<'db> = rustc_type_ir::BoundTyKind<DbInterner<'db>>;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ErrorGuaranteed;
@@ -1430,40 +1430,6 @@ impl ParamLike for ParamTy {
}
}
-impl<'db> BoundVarLike<DbInterner<'db>> for BoundTy {
- fn var(self) -> BoundVar {
- self.var
- }
-
- fn assert_eq(self, var: BoundVarKind) {
- assert_eq!(self.kind, var.expect_ty())
- }
-}
-
-impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderTy {
- type Bound = BoundTy;
-
- fn universe(self) -> rustc_type_ir::UniverseIndex {
- self.universe
- }
-
- fn var(self) -> BoundVar {
- self.bound.var
- }
-
- fn with_updated_universe(self, ui: rustc_type_ir::UniverseIndex) -> Self {
- Placeholder { universe: ui, bound: self.bound }
- }
-
- fn new(ui: rustc_type_ir::UniverseIndex, bound: BoundTy) -> Self {
- Placeholder { universe: ui, bound }
- }
-
- fn new_anon(ui: rustc_type_ir::UniverseIndex, var: rustc_type_ir::BoundVar) -> Self {
- Placeholder { universe: ui, bound: BoundTy { var, kind: BoundTyKind::Anon } }
- }
-}
-
impl<'db> DbInterner<'db> {
/// Given a closure signature, returns an equivalent fn signature. Detuples
/// and so forth -- so e.g., if we have a sig with `Fn<(u32, i32)>` then
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs
index c175062bda..858233cb2c 100644
--- a/crates/hir-ty/src/next_solver/util.rs
+++ b/crates/hir-ty/src/next_solver/util.rs
@@ -15,7 +15,7 @@ use rustc_type_ir::{
use crate::{
next_solver::{
- BoundConst, FxIndexMap, ParamEnv, Placeholder, PlaceholderConst, PlaceholderRegion,
+ BoundConst, FxIndexMap, ParamEnv, PlaceholderConst, PlaceholderRegion, PlaceholderType,
PolyTraitRef,
infer::{
InferCtxt,
@@ -446,9 +446,10 @@ pub fn apply_args_to_binder<'db, T: TypeFoldable<DbInterner<'db>>>(
args: GenericArgs<'db>,
interner: DbInterner<'db>,
) -> T {
- let types = &mut |ty: BoundTy| args.as_slice()[ty.var.index()].expect_ty();
- let regions = &mut |region: BoundRegion| args.as_slice()[region.var.index()].expect_region();
- let consts = &mut |const_: BoundConst| args.as_slice()[const_.var.index()].expect_const();
+ let types = &mut |ty: BoundTy<'db>| args.as_slice()[ty.var.index()].expect_ty();
+ let regions =
+ &mut |region: BoundRegion<'db>| args.as_slice()[region.var.index()].expect_region();
+ let consts = &mut |const_: BoundConst<'db>| args.as_slice()[const_.var.index()].expect_const();
let mut instantiate = BoundVarReplacer::new(interner, FnMutDelegate { types, regions, consts });
b.skip_binder().fold_with(&mut instantiate)
}
@@ -497,9 +498,9 @@ impl<'db> TypeVisitor<DbInterner<'db>> for ContainsTypeErrors {
/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
pub struct PlaceholderReplacer<'a, 'db> {
infcx: &'a InferCtxt<'db>,
- mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
- mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
- mapped_consts: FxIndexMap<PlaceholderConst, BoundConst>,
+ mapped_regions: FxIndexMap<PlaceholderRegion<'db>, BoundRegion<'db>>,
+ mapped_types: FxIndexMap<PlaceholderType<'db>, BoundTy<'db>>,
+ mapped_consts: FxIndexMap<PlaceholderConst<'db>, BoundConst<'db>>,
universe_indices: &'a [Option<UniverseIndex>],
current_index: DebruijnIndex,
}
@@ -507,9 +508,9 @@ pub struct PlaceholderReplacer<'a, 'db> {
impl<'a, 'db> PlaceholderReplacer<'a, 'db> {
pub fn replace_placeholders<T: TypeFoldable<DbInterner<'db>>>(
infcx: &'a InferCtxt<'db>,
- mapped_regions: FxIndexMap<PlaceholderRegion, BoundRegion>,
- mapped_types: FxIndexMap<Placeholder<BoundTy>, BoundTy>,
- mapped_consts: FxIndexMap<PlaceholderConst, BoundConst>,
+ mapped_regions: FxIndexMap<PlaceholderRegion<'db>, BoundRegion<'db>>,
+ mapped_types: FxIndexMap<PlaceholderType<'db>, BoundTy<'db>>,
+ mapped_consts: FxIndexMap<PlaceholderConst<'db>, BoundConst<'db>>,
universe_indices: &'a [Option<UniverseIndex>],
value: T,
) -> T {
diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs
index ce93a33422..2e85beea91 100644
--- a/crates/hir-ty/src/opaques.rs
+++ b/crates/hir-ty/src/opaques.rs
@@ -55,10 +55,10 @@ pub(crate) fn opaque_types_defined_by(
.for_each(extend_with_taits);
};
let extend_with_atpit_from_container = |container| match container {
- ItemContainerId::ImplId(impl_id) => {
- if ImplSignature::of(db, impl_id).target_trait.is_some() {
- extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items);
- }
+ ItemContainerId::ImplId(impl_id)
+ if ImplSignature::of(db, impl_id).target_trait.is_some() =>
+ {
+ extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items);
}
ItemContainerId::TraitId(trait_id) => {
extend_with_atpit_from_assoc_items(&trait_id.trait_items(db).items);
@@ -196,10 +196,10 @@ fn tait_defining_bodies(
.collect()
};
match loc.container {
- ItemContainerId::ImplId(impl_id) => {
- if ImplSignature::of(db, impl_id).target_trait.is_some() {
- return from_assoc_items(&impl_id.impl_items(db).items);
- }
+ ItemContainerId::ImplId(impl_id)
+ if ImplSignature::of(db, impl_id).target_trait.is_some() =>
+ {
+ return from_assoc_items(&impl_id.impl_items(db).items);
}
ItemContainerId::TraitId(trait_id) => {
return from_assoc_items(&trait_id.trait_items(db).items);
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs
index 90cbcfea6a..8bc6c51fae 100644
--- a/crates/hir-ty/src/specialization.rs
+++ b/crates/hir-ty/src/specialization.rs
@@ -109,7 +109,7 @@ 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_copied(interner, parent_args.as_slice()),
+ .iter_instantiated(interner, parent_args.as_slice()),
cause.clone(),
param_env,
));
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index 243456c85f..e19e26ebc4 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -3,8 +3,8 @@
use std::{fmt, panic, sync::Mutex};
use base_db::{
- CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, RootQueryDb,
- SourceDatabase, SourceRoot, SourceRootId, SourceRootInput,
+ CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Nonce, SourceDatabase, SourceRoot,
+ SourceRootId, SourceRootInput, all_crates, relevant_crates, set_all_crates_with_durability,
};
use hir_def::{ModuleId, db::DefDatabase, nameres::crate_def_map};
@@ -45,7 +45,7 @@ impl Default for TestDB {
};
this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH);
// This needs to be here otherwise `CrateGraphBuilder` panics.
- this.set_all_crates(Arc::new(Box::new([])));
+ set_all_crates_with_durability(&mut this, std::iter::empty(), Durability::HIGH);
_ = base_db::LibraryRoots::builder(Default::default())
.durability(Durability::MEDIUM)
.new(&this);
@@ -142,7 +142,7 @@ impl panic::RefUnwindSafe for TestDB {}
impl TestDB {
pub(crate) fn module_for_file_opt(&self, file_id: impl Into<FileId>) -> Option<ModuleId> {
let file_id = file_id.into();
- for &krate in self.relevant_crates(file_id).iter() {
+ for &krate in relevant_crates(self, file_id).iter() {
let crate_def_map = crate_def_map(self, krate);
for (module_id, data) in crate_def_map.modules() {
if data.origin.file_id().map(|file_id| file_id.file_id(self)) == Some(file_id) {
@@ -161,7 +161,7 @@ impl TestDB {
&self,
) -> FxHashMap<EditionedFileId, Vec<(TextRange, String)>> {
let mut files = Vec::new();
- for &krate in self.all_crates().iter() {
+ for &krate in all_crates(self).iter() {
let crate_def_map = crate_def_map(self, krate);
for (module_id, _) in crate_def_map.modules() {
let file_id = crate_def_map[module_id].origin.file_id();
diff --git a/crates/hir-ty/src/tests/closure_captures.rs b/crates/hir-ty/src/tests/closure_captures.rs
index 9e68756821..5324d8c605 100644
--- a/crates/hir-ty/src/tests/closure_captures.rs
+++ b/crates/hir-ty/src/tests/closure_captures.rs
@@ -1,24 +1,68 @@
use expect_test::{Expect, expect};
use hir_def::{
- DefWithBodyId,
+ AdtId, DefWithBodyId, LocalFieldId, VariantId,
expr_store::{Body, ExpressionStore},
+ hir::{BindingId, ExprOrPatId},
};
use hir_expand::{HirFileId, files::InFileWrapper};
use itertools::Itertools;
-use span::TextRange;
+use rustc_type_ir::inherent::{AdtDef as _, IntoKind};
+use span::{Edition, TextRange};
+use stdx::{format_to, never};
use syntax::{AstNode, AstPtr};
use test_fixture::WithFixture;
use crate::{
InferenceResult,
- db::HirDatabase,
+ closure_analysis::Place,
display::{DisplayTarget, HirDisplay},
- mir::MirSpan,
+ next_solver::TyKind,
test_db::TestDB,
};
use super::{setup_tracing, visit_module};
+fn display_place(db: &TestDB, store: &ExpressionStore, place: &Place, local: BindingId) -> String {
+ let mut result = store[local].name.display(db, Edition::LATEST).to_string();
+ let mut last_was_deref = false;
+ for (i, proj) in place.projections.iter().enumerate() {
+ match proj.kind {
+ hir_ty::closure_analysis::ProjectionKind::Deref => {
+ result.insert(0, '*');
+ last_was_deref = true;
+ }
+ hir_ty::closure_analysis::ProjectionKind::Field { field_idx, variant_idx } => {
+ if last_was_deref {
+ result.insert(0, '(');
+ result.push(')');
+ last_was_deref = false;
+ }
+
+ let ty = place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => format_to!(result, ".{field_idx}"),
+ TyKind::Adt(adt_def, _) => {
+ let variant = match adt_def.def_id().0 {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(id) => {
+ // Can't really do that for an enum, unfortunately, so try to do something alike.
+ id.enum_variants(db).variants[variant_idx as usize].0.into()
+ }
+ };
+ let field = &variant.fields(db).fields()
+ [LocalFieldId::from_raw(la_arena::RawIdx::from_u32(field_idx))];
+ format_to!(result, ".{}", field.name.display(db, Edition::LATEST));
+ }
+ _ => never!("mismatching projection type"),
+ }
+ }
+ _ => never!("unexpected projection kind"),
+ }
+ }
+ result
+}
+
fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
let _tracing = setup_tracing();
let (db, file_id) = TestDB::with_single_file(ra_fixture);
@@ -40,84 +84,69 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec
};
let infer = InferenceResult::of(&db, def);
let db = &db;
- captures_info.extend(infer.closure_info.iter().flat_map(
- |(closure_id, (captures, _))| {
- let closure = db.lookup_intern_closure(*closure_id);
- let body_owner = closure.0;
- let source_map = ExpressionStore::with_source_map(db, body_owner).1;
- let closure_text_range = source_map
- .expr_syntax(closure.1)
- .expect("failed to map closure to SyntaxNode")
- .value
- .text_range();
- captures.iter().map(move |capture| {
- fn text_range<N: AstNode>(
- db: &TestDB,
- syntax: InFileWrapper<HirFileId, AstPtr<N>>,
- ) -> TextRange {
- let root = syntax.file_syntax(db);
- syntax.value.to_node(&root).syntax().text_range()
+ captures_info.extend(infer.closures_data.iter().flat_map(|(closure, closure_data)| {
+ let (body, source_map) = Body::with_source_map(db, def);
+ let closure_text_range = source_map
+ .expr_syntax(*closure)
+ .expect("failed to map closure to SyntaxNode")
+ .value
+ .text_range();
+ closure_data.min_captures.values().flatten().map(move |capture| {
+ fn text_range<N: AstNode>(
+ db: &TestDB,
+ syntax: InFileWrapper<HirFileId, AstPtr<N>>,
+ ) -> TextRange {
+ let root = syntax.file_syntax(db);
+ syntax.value.to_node(&root).syntax().text_range()
+ }
+
+ // 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())
+ {
+ Some((param, source)) if param == captured_local => {
+ format!("{:?}", text_range(db, source))
}
-
- // FIXME: Deduplicate this with hir::Local::sources().
- let (body, source_map) =
- Body::with_source_map(db, body_owner.as_def_with_body().unwrap());
- let local_text_range =
- match body.self_param.zip(source_map.self_param_syntax()) {
- Some((param, source)) if param == capture.local() => {
- format!("{:?}", text_range(db, source))
- }
- _ => source_map
- .patterns_for_binding(capture.local())
- .iter()
- .map(|&definition| {
- text_range(db, source_map.pat_syntax(definition).unwrap())
- })
- .map(|it| format!("{it:?}"))
- .join(", "),
- };
- let place = capture.display_place(body_owner, db);
- let capture_ty = capture
- .ty
- .get()
- .skip_binder()
- .display_test(db, DisplayTarget::from_crate(db, module.krate(db)))
- .to_string();
- let spans = capture
- .spans()
+ _ => source_map
+ .patterns_for_binding(captured_local)
.iter()
- .flat_map(|span| match *span {
- MirSpan::ExprId(expr) => {
- vec![text_range(db, source_map.expr_syntax(expr).unwrap())]
- }
- MirSpan::PatId(pat) => {
- vec![text_range(db, source_map.pat_syntax(pat).unwrap())]
- }
- MirSpan::BindingId(binding) => source_map
- .patterns_for_binding(binding)
- .iter()
- .map(|pat| text_range(db, source_map.pat_syntax(*pat).unwrap()))
- .collect(),
- MirSpan::SelfParam => {
- vec![text_range(db, source_map.self_param_syntax().unwrap())]
- }
- MirSpan::Unknown => Vec::new(),
+ .map(|&definition| {
+ text_range(db, source_map.pat_syntax(definition).unwrap())
})
- .sorted_by_key(|it| it.start())
.map(|it| format!("{it:?}"))
- .join(",");
-
- (
- closure_text_range,
- local_text_range,
- spans,
- place,
- capture_ty,
- capture.kind(),
- )
- })
- },
- ));
+ .join(", "),
+ };
+ let place = display_place(db, body, &capture.place, captured_local);
+ let capture_ty = capture
+ .captured_ty(db)
+ .display_test(db, DisplayTarget::from_crate(db, module.krate(db)))
+ .to_string();
+ let spans = capture
+ .info
+ .sources
+ .iter()
+ .flat_map(|span| match span.final_source() {
+ ExprOrPatId::ExprId(expr) => {
+ vec![text_range(db, source_map.expr_syntax(expr).unwrap())]
+ }
+ ExprOrPatId::PatId(pat) => {
+ vec![text_range(db, source_map.pat_syntax(pat).unwrap())]
+ }
+ })
+ .sorted_by_key(|it| it.start())
+ .map(|it| format!("{it:?}"))
+ .join(",");
+
+ (
+ closure_text_range,
+ local_text_range,
+ spans,
+ place,
+ capture_ty,
+ capture.info.capture_kind,
+ )
+ })
+ }));
}
captures_info.sort_unstable_by_key(|(closure_text_range, local_text_range, ..)| {
(closure_text_range.start(), local_text_range.clone())
@@ -146,7 +175,7 @@ fn main() {
let closure = || { let b = *a; };
}
"#,
- expect!["53..71;20..21;66..68 ByRef(Shared) *a &'? bool"],
+ expect!["53..71;20..21;66..68 ByRef(Immutable) *a &'<erased> bool"],
);
}
@@ -160,7 +189,7 @@ fn main() {
let closure = || { let &mut ref b = a; };
}
"#,
- expect!["53..79;20..21;67..72 ByRef(Shared) *a &'? bool"],
+ expect!["53..79;20..21;62..72 ByRef(Immutable) *a &'<erased> bool"],
);
check_closure_captures(
r#"
@@ -170,7 +199,7 @@ fn main() {
let closure = || { let &mut ref mut b = a; };
}
"#,
- expect!["53..83;20..21;67..76 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..83;20..21;62..76 ByRef(Mutable) *a &'<erased> mut bool"],
);
}
@@ -184,7 +213,7 @@ fn main() {
let closure = || { *a = false; };
}
"#,
- expect!["53..71;20..21;58..60 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..71;20..21;58..60 ByRef(Mutable) *a &'<erased> mut bool"],
);
}
@@ -198,7 +227,7 @@ fn main() {
let closure = || { let ref mut b = *a; };
}
"#,
- expect!["53..79;20..21;62..71 ByRef(Mut { kind: Default }) *a &'? mut bool"],
+ expect!["53..79;20..21;74..76 ByRef(Mutable) *a &'<erased> mut bool"],
);
}
@@ -212,7 +241,7 @@ fn main() {
let closure = || { let _ = *a else { return; }; };
}
"#,
- expect!["53..88;20..21;66..68 ByRef(Shared) *a &'? bool"],
+ expect![""],
);
}
@@ -244,8 +273,8 @@ fn main() {
}
"#,
expect![[r#"
- 71..89;36..41;84..86 ByRef(Shared) a &'? NonCopy
- 109..131;36..41;122..128 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+ 71..89;36..41;85..86 ByRef(Immutable) a &'<erased> NonCopy
+ 109..131;36..41;127..128 ByRef(Mutable) a &'<erased> mut NonCopy"#]],
);
}
@@ -260,7 +289,7 @@ fn main() {
let closure = || { let b = a.a; };
}
"#,
- expect!["92..111;50..51;105..108 ByRef(Shared) a.a &'? i32"],
+ expect!["92..111;50..51;105..108 ByRef(Immutable) a.a &'<erased> i32"],
);
}
@@ -281,8 +310,8 @@ fn main() {
}
"#,
expect![[r#"
- 133..212;87..92;154..158 ByRef(Shared) a.a &'? i32
- 133..212;87..92;176..184 ByRef(Mut { kind: Default }) a.b &'? mut i32
+ 133..212;87..92;155..158 ByRef(Immutable) a.a &'<erased> i32
+ 133..212;87..92;181..184 ByRef(Mutable) a.b &'<erased> mut i32
133..212;87..92;202..205 ByValue a.c NonCopy"#]],
);
}
@@ -304,8 +333,8 @@ fn main() {
}
"#,
expect![[r#"
- 123..133;92..97;126..127 ByRef(Shared) a &'? Foo
- 153..164;92..97;156..157 ByRef(Mut { kind: Default }) a &'? mut Foo"#]],
+ 123..133;92..97;126..127 ByRef(Immutable) a &'<erased> Foo
+ 153..164;92..97;156..157 ByRef(Mutable) a &'<erased> mut Foo"#]],
);
}
@@ -332,7 +361,7 @@ fn main() {
}
"#,
expect![[r#"
- 113..167;36..41;127..128,154..160 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool
+ 113..167;36..41;127..128,159..160 ByRef(Mutable) a &'<erased> mut &'? mut bool
231..304;196..201;252..253,276..277,296..297 ByValue a NonCopy"#]],
);
}
@@ -371,8 +400,8 @@ fn main() {
}
"#,
expect![[r#"
- 125..163;36..41;134..135 ByRef(Shared) a &'? NonCopy
- 183..225;36..41;192..193 ByRef(Mut { kind: Default }) a &'? mut NonCopy"#]],
+ 125..163;36..41;134..135 ByRef(Immutable) a &'<erased> NonCopy
+ 183..225;36..41;192..193 ByRef(Mutable) a &'<erased> mut NonCopy"#]],
);
}
@@ -386,7 +415,7 @@ fn main() {
let mut closure = || { let (b | b) = a; };
}
"#,
- expect!["57..80;20..25;76..77,76..77 ByRef(Shared) a &'? bool"],
+ expect!["57..80;20..25;76..77 ByRef(Immutable) a &'<erased> bool"],
);
}
@@ -406,7 +435,7 @@ fn main() {
}
"#,
expect![
- "57..149;20..25;78..80,98..100,118..124,134..135 ByRef(Mut { kind: Default }) a &'? mut bool"
+ "57..149;20..25;79..80,99..100,123..124,134..135 ByRef(Mutable) a &'<erased> mut bool"
],
);
}
@@ -421,7 +450,7 @@ fn main() {
let mut closure = || { let b = *&mut a; };
}
"#,
- expect!["57..80;20..25;71..77 ByRef(Mut { kind: Default }) a &'? mut bool"],
+ expect!["57..80;20..25;76..77 ByRef(Mutable) a &'<erased> mut bool"],
);
}
@@ -440,10 +469,10 @@ fn main() {
}
"#,
expect![[r#"
- 54..72;20..25;67..69 ByRef(Shared) a &'? &'? bool
- 92..114;20..25;105..111 ByRef(Mut { kind: Default }) a &'? mut &'? bool
- 158..176;124..125;171..173 ByRef(Shared) a &'? &'? mut bool
- 196..218;124..125;209..215 ByRef(Mut { kind: Default }) a &'? mut &'? mut bool"#]],
+ 54..72;20..25;68..69 ByRef(Immutable) a &'<erased> &'? bool
+ 92..114;20..25;110..111 ByRef(Mutable) a &'<erased> mut &'? bool
+ 158..176;124..125;172..173 ByRef(Immutable) a &'<erased> &'? mut bool
+ 196..218;124..125;214..215 ByRef(Mutable) a &'<erased> mut &'? mut bool"#]],
);
}
@@ -451,7 +480,7 @@ fn main() {
fn multiple_capture_usages() {
check_closure_captures(
r#"
-//- minicore:copy, fn
+//- minicore: copy, fn
struct A { a: i32, b: bool }
fn main() {
let mut a = A { a: 123, b: false };
@@ -462,7 +491,7 @@ fn main() {
closure();
}
"#,
- expect!["99..165;49..54;120..121,133..134 ByRef(Mut { kind: Default }) a &'? mut A"],
+ expect!["99..165;49..54;120..121,133..134 ByRef(Mutable) a &'<erased> mut A"],
);
}
@@ -485,8 +514,8 @@ fn main() {
}
"#,
expect![[r#"
- 129..225;49..54;149..155 ByRef(Shared) s_ref &'? &'? mut S
- 129..225;93..99;188..198 ByRef(Mut { kind: Default }) s_ref2 &'? mut &'? mut S"#]],
+ 129..225;49..54;158..163 ByRef(Immutable) s_ref &'<erased> &'? mut S
+ 129..225;93..99;201..207 ByRef(Mutable) s_ref2 &'<erased> mut &'? mut S"#]],
);
}
@@ -530,7 +559,7 @@ fn main() {
};
}
"#,
- expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"],
+ expect!["220..257;174..175;245..250 ByRef(Immutable) c.b.x &'<erased> i32"],
);
}
@@ -549,8 +578,8 @@ fn f() {
}
"#,
expect![[r#"
- 44..113;17..18;92..93 ByRef(Shared) a &'? i32
- 73..106;17..18;92..93 ByRef(Shared) a &'? i32"#]],
+ 44..113;17..18;92..93 ByRef(Immutable) a &'<erased> i32
+ 73..106;17..18;92..93 ByRef(Immutable) a &'<erased> i32"#]],
);
}
@@ -568,6 +597,6 @@ fn f() {
};
}
"#,
- expect!["77..110;46..47;96..97 ByRef(Shared) b &'? i32"],
+ expect!["77..110;46..47;96..97 ByRef(Immutable) b &'<erased> i32"],
);
}
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 438699b409..a80ce5002d 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -309,7 +309,7 @@ fn takes_ref_str(x: &str) {}
fn returns_string() -> String { loop {} }
fn test() {
takes_ref_str(&{ returns_string() });
- // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+ // ^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(None), Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
}
"#,
);
@@ -598,6 +598,10 @@ fn test() {
);
}
+// FIXME: rustc emits the following error here:
+// - error[E0277]: he size for values of type `impl Foo + ?Sized` cannot be known at compilation time
+// ...but we don't emit any error here for now
+#[ignore = "rustc emits E0277 here"]
#[test]
fn coerce_unsize_apit() {
check(
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs
index e806999cb4..960155a8e4 100644
--- a/crates/hir-ty/src/tests/incremental.rs
+++ b/crates/hir-ty/src/tests/incremental.rs
@@ -31,11 +31,11 @@ fn foo() -> i32 {
&[("InferenceResult::for_body_", 1)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"InferenceResult::for_body_",
"FunctionSignature::of_",
@@ -49,6 +49,7 @@ fn foo() -> i32 {
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
"ExprScopes::body_expr_scopes_",
+ "body_upvars_mentioned",
]
"#]],
);
@@ -76,7 +77,7 @@ fn foo() -> i32 {
&[("InferenceResult::for_body_", 0)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -119,11 +120,11 @@ fn baz() -> i32 {
&[("InferenceResult::for_body_", 3)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"InferenceResult::for_body_",
"FunctionSignature::of_",
@@ -137,6 +138,7 @@ fn baz() -> i32 {
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
"ExprScopes::body_expr_scopes_",
+ "body_upvars_mentioned",
"InferenceResult::for_body_",
"FunctionSignature::of_",
"FunctionSignature::with_source_map_",
@@ -147,6 +149,7 @@ fn baz() -> i32 {
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
"ExprScopes::body_expr_scopes_",
+ "body_upvars_mentioned",
"InferenceResult::for_body_",
"FunctionSignature::of_",
"FunctionSignature::with_source_map_",
@@ -157,6 +160,7 @@ fn baz() -> i32 {
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
"ExprScopes::body_expr_scopes_",
+ "body_upvars_mentioned",
]
"#]],
);
@@ -189,7 +193,7 @@ fn baz() -> i32 {
&[("InferenceResult::for_body_", 1)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -205,6 +209,7 @@ fn baz() -> i32 {
"Body::of_",
"InferenceResult::for_body_",
"ExprScopes::body_expr_scopes_",
+ "body_upvars_mentioned",
"AttrFlags::query_",
"FunctionSignature::with_source_map_",
"FunctionSignature::of_",
@@ -239,11 +244,11 @@ $0",
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
@@ -278,7 +283,7 @@ pub struct NewStruct {
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -314,11 +319,11 @@ $0",
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
@@ -354,7 +359,7 @@ pub enum SomeEnum {
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -390,11 +395,11 @@ $0",
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
@@ -427,7 +432,7 @@ fn bar() -> f32 {
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -467,11 +472,11 @@ $0",
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"TraitImpls::for_crate_",
"lang_items",
@@ -512,7 +517,7 @@ impl SomeStruct {
&[("TraitImpls::for_crate_", 1)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -568,11 +573,11 @@ fn main() {
&[("trait_solve_shim", 0)],
expect_test::expect![[r#"
[
- "source_root_crates_shim",
+ "source_root_crates",
"crate_local_def_map",
"file_item_tree_query",
"ast_id_map",
- "parse_shim",
+ "parse",
"real_span_map_shim",
"TraitItems::query_with_diagnostics_",
"Body::of_",
@@ -594,6 +599,7 @@ fn main() {
"GenericPredicates::query_with_diagnostics_",
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
+ "body_upvars_mentioned",
"InferenceResult::for_body_",
"FunctionSignature::of_",
"FunctionSignature::with_source_map_",
@@ -616,6 +622,7 @@ fn main() {
"impl_self_ty_with_diagnostics_query",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
+ "body_upvars_mentioned",
]
"#]],
);
@@ -664,7 +671,7 @@ fn main() {
&[("trait_solve_shim", 0)],
expect_test::expect![[r#"
[
- "parse_shim",
+ "parse",
"ast_id_map",
"file_item_tree_query",
"real_span_map_shim",
@@ -686,6 +693,7 @@ fn main() {
"GenericPredicates::query_with_diagnostics_",
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
+ "body_upvars_mentioned",
"InferenceResult::for_body_",
"FunctionSignature::with_source_map_",
"GenericPredicates::query_with_diagnostics_",
@@ -703,6 +711,7 @@ fn main() {
"impl_self_ty_with_diagnostics_query",
"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 c8ed8aa258..fc8c1f8164 100644
--- a/crates/hir-ty/src/tests/method_resolution.rs
+++ b/crates/hir-ty/src/tests/method_resolution.rs
@@ -1367,7 +1367,7 @@ mod a {
mod b {
fn foo() {
let x = super::a::Bar::new().0;
- // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not))))
+ // ^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not)))
// ^^^^^^^^^^^^^^^^^^^^^^ type: char
}
}
@@ -2129,7 +2129,7 @@ impl Foo {
use core::mem::ManuallyDrop;
fn test() {
ManuallyDrop::new(Foo).foo();
- //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Some(Not)))), Borrow(Ref(Not))
+ //^^^^^^^^^^^^^^^^^^^^^^ adjustments: Deref(Some(OverloadedDeref(Not))), Borrow(Ref(Not))
}
"#,
);
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index 42dc074309..d6bc03f57d 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -294,6 +294,7 @@ fn infer_pattern_match_ergonomics_ref() {
fn ref_pat_with_inference_variable() {
check_no_mismatches(
r#"
+//- minicore: fn
enum E { A }
fn test() {
let f = |e| match e {
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index e4fc7e56c6..e30fa779da 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2363,6 +2363,7 @@ fn test() {
}
"#,
expect![[r#"
+ 46..49 'Foo': Foo<N>
93..97 'self': Foo<N>
108..125 '{ ... }': usize
118..119 'N': usize
@@ -2841,3 +2842,74 @@ fn wrapped_abs<T: SelfAbs<Output = T>>(v: T) -> T {
"#,
);
}
+
+#[test]
+fn regression_21899() {
+ check_no_mismatches(
+ r#"
+trait B where
+ Self::T: B,
+{
+ type T;
+}
+
+fn foo<T: B>(v: T::T) {}
+ "#,
+ );
+}
+
+#[test]
+fn regression_22007() {
+ check_types(
+ r#"
+//- minicore: fn
+trait Super {
+ type Assoc;
+ fn foo(self) -> Self::Assoc
+ where
+ Self: Sub,
+ { loop {} }
+}
+trait Sub: Super {}
+
+struct Struct;
+impl Super for Struct {
+ type Assoc = u8;
+}
+impl Sub for Struct {}
+
+fn foo() {
+ Struct.foo();
+ // ^^^^^^^^^^^^ u8
+}
+ "#,
+ );
+}
+
+#[test]
+fn regression_21885() {
+ check_no_mismatches(
+ r#"
+//- minicore: coerce_unsized, future, result
+use core::future::Future;
+
+trait Foo {
+ type Assoc;
+
+ fn foo() -> &dyn Future<Output = Result<Self::Assoc, ()>>;
+}
+
+struct Bar;
+
+impl Foo for Bar {
+ type Assoc = NotFound;
+
+ fn foo() -> &dyn Future<Output = Result<Self::Assoc, ()>> {
+ &async {
+ Err(())
+ }
+ }
+}
+"#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index e6b3244cda..565360dc25 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -680,9 +680,9 @@ where
expect![[r#"
43..47 'self': &'? Self
168..172 'self': &'? F
- 205..227 '{ ... }': <F as AsyncFnMut<()>>::CallRefFuture<'<erased>>
+ 205..227 '{ ... }': <F as AsyncFnMut<()>>::CallRefFuture<'?>
215..219 'self': &'? F
- 215..221 'self()': <F as AsyncFnMut<()>>::CallRefFuture<'<erased>>
+ 215..221 'self()': <F as AsyncFnMut<()>>::CallRefFuture<'?>
"#]],
);
}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 22359d8f1f..278666ef35 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -87,7 +87,7 @@ async fn test() {
fn infer_async_closure() {
check_types(
r#"
-//- minicore: future, option
+//- minicore: future, option, async_fn
async fn test() {
let f = async move |x: i32| x + 42;
f;
@@ -3149,6 +3149,7 @@ impl<A: Step> core::iter::Iterator for core::ops::Range<A> {
fn infer_closure_arg() {
check_infer(
r#"
+//- minicore: fn
//- /lib.rs
enum Option<T> {
@@ -4449,14 +4450,14 @@ impl Trait for () {
let a = Self::Assoc { x };
// ^ S
let a = <Self>::Assoc { x }; // unstable
- // ^ {unknown}
+ // ^ S
// should be `Copy` but we don't track ownership anyway.
let value = S { x };
if let Self::Assoc { x } = value {}
// ^ u32
if let <Self>::Assoc { x } = value {} // unstable
- // ^ {unknown}
+ // ^ u32
}
}
"#,
@@ -4508,22 +4509,22 @@ impl Trait for () {
let a = Self::Assoc::Struct { x };
// ^ E
let a = <Self>::Assoc::Struct { x }; // unstable
- // ^ {unknown}
+ // ^ E
let a = <Self::Assoc>::Struct { x }; // unstable
- // ^ {unknown}
+ // ^ E
let a = <<Self>::Assoc>::Struct { x }; // unstable
- // ^ {unknown}
+ // ^ E
// should be `Copy` but we don't track ownership anyway.
let value = E::Struct { x: 42 };
if let Self::Assoc::Struct { x } = value {}
// ^ u32
if let <Self>::Assoc::Struct { x } = value {} // unstable
- // ^ {unknown}
+ // ^ u32
if let <Self::Assoc>::Struct { x } = value {} // unstable
- // ^ {unknown}
+ // ^ u32
if let <<Self>::Assoc>::Struct { x } = value {} // unstable
- // ^ {unknown}
+ // ^ u32
}
}
"#,
@@ -5148,3 +5149,98 @@ fn foo(v: Struct<f32>) {
"#,
);
}
+
+#[test]
+fn more_qualified_paths() {
+ check_infer(
+ r#"
+struct T;
+struct S {
+ a: u32,
+}
+
+trait Trait {
+ type Assoc;
+
+ fn foo();
+}
+
+impl Trait for T {
+ type Assoc = S;
+
+ fn foo() {
+ let <Self>::Assoc { a } = <Self>::Assoc { a: 0 };
+ }
+}
+
+enum E {
+ ES { a: u32 },
+ ET(u32),
+}
+
+impl Trait for E {
+ type Assoc = Self;
+
+ fn foo() {
+ let <Self>::Assoc::ES { a } = <Self>::Assoc::ES { a: 0 };
+ }
+}
+
+fn foo() {
+ let <T as Trait>::Assoc { a } = <T as Trait>::Assoc { a: 0 };
+
+ let <E>::ES { a } = (<E>::ES { a: 0 }) else { loop {} };
+ let <E>::ET(a) = <E>::ET(0) else { loop {} };
+ let <E as Trait>::Assoc::ES { a } = (<E as Trait>::Assoc::ES { a: 0 }) else { loop {} };
+ let <E as Trait>::Assoc::ET(a) = <E as Trait>::Assoc::ET(0) else { loop {} };
+}
+ "#,
+ expect![[r#"
+ 137..202 '{ ... }': ()
+ 151..170 '<Self>... { a }': S
+ 167..168 'a': u32
+ 173..195 '<Self>...a: 0 }': S
+ 192..193 '0': u32
+ 306..379 '{ ... }': ()
+ 320..343 '<Self>... { a }': E
+ 340..341 'a': u32
+ 346..372 '<Self>...a: 0 }': E
+ 369..370 '0': u32
+ 392..748 '{ ...} }; }': ()
+ 402..427 '<T as ... { a }': S
+ 424..425 'a': u32
+ 430..458 '<T as ...a: 0 }': S
+ 455..456 '0': u32
+ 469..482 '<E>::ES { a }': E
+ 479..480 'a': u32
+ 486..502 '<E>::E...a: 0 }': E
+ 499..500 '0': u32
+ 509..520 '{ loop {} }': !
+ 511..518 'loop {}': !
+ 516..518 '{}': ()
+ 530..540 '<E>::ET(a)': E
+ 538..539 'a': u32
+ 543..550 '<E>::ET': fn ET(u32) -> E
+ 543..553 '<E>::ET(0)': E
+ 551..552 '0': u32
+ 559..570 '{ loop {} }': !
+ 561..568 'loop {}': !
+ 566..568 '{}': ()
+ 580..609 '<E as ... { a }': E
+ 606..607 'a': u32
+ 613..645 '<E as ...a: 0 }': E
+ 642..643 '0': u32
+ 652..663 '{ loop {} }': !
+ 654..661 'loop {}': !
+ 659..661 '{}': ()
+ 673..699 '<E as ...:ET(a)': E
+ 697..698 'a': u32
+ 702..725 '<E as ...oc::ET': fn ET(u32) -> E
+ 702..728 '<E as ...:ET(0)': E
+ 726..727 '0': u32
+ 734..745 '{ loop {} }': !
+ 736..743 'loop {}': !
+ 741..743 '{}': ()
+ "#]],
+ );
+}
diff --git a/crates/hir-ty/src/upvars.rs b/crates/hir-ty/src/upvars.rs
index 489895fe3c..48f3c803d8 100644
--- a/crates/hir-ty/src/upvars.rs
+++ b/crates/hir-ty/src/upvars.rs
@@ -1,8 +1,8 @@
//! A simple query to collect tall locals (upvars) a closure use.
use hir_def::{
- DefWithBodyId,
- expr_store::{Body, path::Path},
+ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId,
+ expr_store::{ExpressionStore, path::Path},
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
resolver::{HasResolver, Resolver, ValueNs},
};
@@ -36,18 +36,89 @@ impl Upvars {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
+
+ #[inline]
+ pub fn as_ref(&self) -> UpvarsRef<'_> {
+ UpvarsRef(&self.0)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
+// Kept sorted.
+pub struct UpvarsRef<'db>(&'db [BindingId]);
+
+impl UpvarsRef<'_> {
+ #[inline]
+ pub fn contains(self, local: BindingId) -> bool {
+ self.0.binary_search(&local).is_ok()
+ }
+
+ #[inline]
+ pub fn iter(self) -> impl ExactSizeIterator<Item = BindingId> {
+ self.0.iter().copied()
+ }
+
+ #[inline]
+ pub fn is_empty(self) -> bool {
+ self.0.is_empty()
+ }
+
+ #[inline]
+ pub const fn empty() -> Self {
+ UpvarsRef(&[])
+ }
}
/// Returns a map from `Expr::Closure` to its upvars.
-#[salsa::tracked(returns(as_deref))]
pub fn upvars_mentioned(
db: &dyn HirDatabase,
- owner: DefWithBodyId,
+ owner: ExpressionStoreOwnerId,
+) -> Option<&FxHashMap<ExprId, Upvars>> {
+ return match owner {
+ ExpressionStoreOwnerId::Signature(owner) => signature_upvars_mentioned(db, owner),
+ ExpressionStoreOwnerId::Body(owner) => body_upvars_mentioned(db, owner),
+ ExpressionStoreOwnerId::VariantFields(owner) => variant_fields_upvars_mentioned(db, owner),
+ };
+
+ #[salsa::tracked(returns(as_deref))]
+ pub fn signature_upvars_mentioned(
+ db: &dyn HirDatabase,
+ owner: GenericDefId,
+ ) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
+ upvars_mentioned_impl(db, owner.into())
+ }
+
+ #[salsa::tracked(returns(as_deref))]
+ pub fn body_upvars_mentioned(
+ db: &dyn HirDatabase,
+ owner: DefWithBodyId,
+ ) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
+ upvars_mentioned_impl(db, owner.into())
+ }
+
+ #[salsa::tracked(returns(as_deref))]
+ pub fn variant_fields_upvars_mentioned(
+ db: &dyn HirDatabase,
+ owner: VariantId,
+ ) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
+ upvars_mentioned_impl(db, owner.into())
+ }
+}
+
+pub fn upvars_mentioned_impl(
+ db: &dyn HirDatabase,
+ owner: ExpressionStoreOwnerId,
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
- let body = Body::of(db, owner);
+ let store = ExpressionStore::of(db, owner);
+ if store.const_expr_origins().is_empty() {
+ // Save constructing a Resolver.
+ return None;
+ }
let mut resolver = owner.resolver(db);
let mut result = FxHashMap::default();
- handle_expr_outside_closure(db, &mut resolver, owner, body, body.root_expr(), &mut result);
+ for root_expr in store.expr_roots() {
+ handle_expr_outside_closure(db, &mut resolver, owner, store, root_expr, &mut result);
+ }
return if result.is_empty() {
None
} else {
@@ -58,8 +129,8 @@ pub fn upvars_mentioned(
fn handle_expr_outside_closure<'db>(
db: &'db dyn HirDatabase,
resolver: &mut Resolver<'db>,
- owner: DefWithBodyId,
- body: &Body,
+ owner: ExpressionStoreOwnerId,
+ body: &ExpressionStore,
expr: ExprId,
closures_map: &mut FxHashMap<ExprId, Upvars>,
) {
@@ -89,8 +160,8 @@ pub fn upvars_mentioned(
fn handle_expr_inside_closure<'db>(
db: &'db dyn HirDatabase,
resolver: &mut Resolver<'db>,
- owner: DefWithBodyId,
- body: &Body,
+ owner: ExpressionStoreOwnerId,
+ body: &ExpressionStore,
current_closure: ExprId,
expr: ExprId,
upvars: &mut FxHashSet<BindingId>,
@@ -170,8 +241,8 @@ pub fn upvars_mentioned(
fn resolve_maybe_upvar<'db>(
db: &'db dyn HirDatabase,
resolver: &mut Resolver<'db>,
- owner: DefWithBodyId,
- body: &Body,
+ owner: ExpressionStoreOwnerId,
+ body: &ExpressionStore,
current_closure: ExprId,
expr: ExprId,
id: ExprOrPatId,
@@ -179,8 +250,9 @@ fn resolve_maybe_upvar<'db>(
path: &Path,
) {
if let Path::BarePath(mod_path) = path
- && matches!(mod_path.kind, PathKind::Plain)
- && mod_path.segments().len() == 1
+ && matches!(mod_path.kind, PathKind::Plain | PathKind::SELF)
+ // `self` is length zero.
+ && mod_path.segments().len() <= 1
{
// Could be a variable.
let guard = resolver.update_to_inner_scope(db, owner, expr);
@@ -198,7 +270,9 @@ fn resolve_maybe_upvar<'db>(
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
- use hir_def::{ModuleDefId, expr_store::Body, nameres::crate_def_map};
+ use hir_def::{
+ AssocItemId, DefWithBodyId, ModuleDefId, expr_store::Body, nameres::crate_def_map,
+ };
use itertools::Itertools;
use span::Edition;
use test_fixture::WithFixture;
@@ -217,10 +291,18 @@ mod tests {
ModuleDefId::FunctionId(func) => Some(func),
_ => None,
})
+ .chain(def_map.modules().flat_map(|(_, module)| {
+ module.scope.impls().flat_map(|impl_| &*impl_.impl_items(&db).items).filter_map(
+ |&(_, item)| match item {
+ AssocItemId::FunctionId(it) => Some(it),
+ _ => None,
+ },
+ )
+ }))
.exactly_one()
.unwrap_or_else(|_| panic!("expected one function"));
let (body, source_map) = Body::with_source_map(&db, func.into());
- let Some(upvars) = upvars_mentioned(&db, func.into()) else {
+ let Some(upvars) = upvars_mentioned(&db, DefWithBodyId::from(func).into()) else {
expectation.assert_eq("");
return;
};
@@ -316,4 +398,19 @@ fn foo() {
49..110: a, b"#]],
);
}
+
+ #[test]
+ fn self_upvar() {
+ check(
+ r#"
+struct Foo(i32);
+impl Foo {
+ fn foo(&self) {
+ || self.0;
+ }
+}
+ "#,
+ expect!["56..65: self"],
+ );
+ }
}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 509109543c..ae9b2c4618 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -1,6 +1,8 @@
//! Helper functions for working with def, which don't need to be a separate
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
+use std::iter::Enumerate;
+
use base_db::target::{self, TargetData};
use hir_def::{
EnumId, EnumVariantId, FunctionId, Lookup, TraitId, attrs::AttrFlags, lang_item::LangItems,
@@ -163,3 +165,54 @@ pub(crate) fn detect_variant_from_bytes<'a>(
};
Some((var_id, var_layout))
}
+
+pub(crate) struct EnumerateAndAdjust<I> {
+ enumerate: Enumerate<I>,
+ gap_pos: usize,
+ gap_len: usize,
+}
+
+impl<I> Iterator for EnumerateAndAdjust<I>
+where
+ I: Iterator,
+{
+ type Item = (usize, <I as Iterator>::Item);
+
+ fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
+ self.enumerate
+ .next()
+ .map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.enumerate.size_hint()
+ }
+}
+
+pub(crate) trait EnumerateAndAdjustIterator {
+ fn enumerate_and_adjust(
+ self,
+ expected_len: usize,
+ gap_pos: Option<u32>,
+ ) -> EnumerateAndAdjust<Self>
+ where
+ Self: Sized;
+}
+
+impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
+ fn enumerate_and_adjust(
+ self,
+ expected_len: usize,
+ gap_pos: Option<u32>,
+ ) -> EnumerateAndAdjust<Self>
+ where
+ Self: Sized,
+ {
+ let actual_len = self.len();
+ EnumerateAndAdjust {
+ enumerate: self.enumerate(),
+ gap_pos: gap_pos.map(|it| it as usize).unwrap_or(expected_len),
+ gap_len: expected_len - actual_len,
+ }
+ }
+}
diff --git a/crates/hir-ty/src/variance.rs b/crates/hir-ty/src/variance.rs
index 1945b04bb3..a88457e3c7 100644
--- a/crates/hir-ty/src/variance.rs
+++ b/crates/hir-ty/src/variance.rs
@@ -216,7 +216,7 @@ impl<'db> Context<'db> {
TyKind::Adt(def, args) => {
self.add_constraints_from_args(def.def_id().0.into(), args, variance);
}
- TyKind::Alias(_, alias) => {
+ TyKind::Alias(alias) => {
// FIXME: Probably not correct wrt. opaques.
self.add_constraints_from_invariant_args(alias.args);
}
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index d20ee1546f..8902144189 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -21,6 +21,7 @@ serde_json.workspace = true
smallvec.workspace = true
tracing = { workspace = true, features = ["attributes"] }
triomphe.workspace = true
+la-arena.workspace = true
ra-ap-rustc_type_ir.workspace = true
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index 27e7985146..223103b6e5 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -38,7 +38,11 @@ pub enum AttrsOwner {
Field(FieldId),
LifetimeParam(LifetimeParamId),
TypeOrConstParam(TypeOrConstParamId),
- /// Things that do not have attributes. Used for builtin derives.
+ /// Things that do not have attributes.
+ ///
+ /// Used for:
+ /// - builtin derives
+ /// - builtin types (as those do not have attributes)
Dummy,
}
@@ -85,6 +89,19 @@ impl AttrsWithOwner {
self.attrs.contains(AttrFlags::IS_UNSTABLE)
}
+ /// Currently, it could be that `is_unstable() == true` but `unstable_feature == None`
+ /// (due to unstable features not being retrieved for fields etc.).
+ #[inline]
+ pub fn unstable_feature(&self, db: &dyn HirDatabase) -> Option<Symbol> {
+ match self.owner {
+ AttrsOwner::AttrDef(owner) => self.attrs.unstable_feature(db, owner),
+ AttrsOwner::Field(_)
+ | AttrsOwner::LifetimeParam(_)
+ | AttrsOwner::TypeOrConstParam(_)
+ | AttrsOwner::Dummy => None,
+ }
+ }
+
#[inline]
pub fn is_macro_export(&self) -> bool {
self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 7f672a697c..6cfb79d5a1 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -282,7 +282,7 @@ pub struct MissingFields {
pub file: HirFileId,
pub field_list_parent: AstPtr<Either<ast::RecordExpr, ast::RecordPat>>,
pub field_list_parent_path: Option<AstPtr<ast::Path>>,
- pub missed_fields: Vec<Name>,
+ pub missed_fields: Vec<(Name, Field)>,
}
#[derive(Debug)]
@@ -476,7 +476,12 @@ impl<'db> AnyDiagnostic<'db> {
let variant_data = variant.fields(db);
let missed_fields = missed_fields
.into_iter()
- .map(|idx| variant_data.fields()[idx].name.clone())
+ .map(|idx| {
+ (
+ variant_data.fields()[idx].name.clone(),
+ Field { parent: variant.into(), id: idx },
+ )
+ })
.collect();
let record = match record {
@@ -486,35 +491,35 @@ impl<'db> AnyDiagnostic<'db> {
let file = record.file_id;
let root = record.file_syntax(db);
match record.value.to_node(&root) {
- Either::Left(ast::Expr::RecordExpr(record_expr)) => {
- if record_expr.record_expr_field_list().is_some() {
- let field_list_parent_path =
- record_expr.path().map(|path| AstPtr::new(&path));
- return Some(
- MissingFields {
- file,
- field_list_parent: AstPtr::new(&Either::Left(record_expr)),
- field_list_parent_path,
- missed_fields,
- }
- .into(),
- );
- }
+ Either::Left(ast::Expr::RecordExpr(record_expr))
+ if record_expr.record_expr_field_list().is_some() =>
+ {
+ let field_list_parent_path =
+ record_expr.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file,
+ field_list_parent: AstPtr::new(&Either::Left(record_expr)),
+ field_list_parent_path,
+ missed_fields,
+ }
+ .into(),
+ );
}
- Either::Right(ast::Pat::RecordPat(record_pat)) => {
- if record_pat.record_pat_field_list().is_some() {
- let field_list_parent_path =
- record_pat.path().map(|path| AstPtr::new(&path));
- return Some(
- MissingFields {
- file,
- field_list_parent: AstPtr::new(&Either::Right(record_pat)),
- field_list_parent_path,
- missed_fields,
- }
- .into(),
- );
- }
+ Either::Right(ast::Pat::RecordPat(record_pat))
+ if record_pat.record_pat_field_list().is_some() =>
+ {
+ let field_list_parent_path =
+ record_pat.path().map(|path| AstPtr::new(&path));
+ return Some(
+ MissingFields {
+ file,
+ field_list_parent: AstPtr::new(&Either::Right(record_pat)),
+ field_list_parent_path,
+ missed_fields,
+ }
+ .into(),
+ );
}
_ => {}
}
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 4bfdd239f9..53f24713cd 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -76,7 +76,7 @@ fn write_builtin_derive_impl_method<'db>(
let predicates =
hir_ty::builtin_derive::predicates(db, impl_).explicit_predicates().skip_binder();
- write_params_bounds(f, predicates)?;
+ write_params_bounds(f, &Vec::from_iter(predicates))?;
}
Ok(())
@@ -578,7 +578,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
let ty = self.ty(f.db).ty;
let predicates = GenericPredicates::query_all(f.db, self.id.parent());
let predicates = predicates
- .iter_identity_copied()
+ .iter_identity()
.filter(|wc| match wc.kind().skip_binder() {
ClauseKind::Trait(tr) => tr.self_ty() == ty,
ClauseKind::Projection(proj) => proj.self_ty() == ty,
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index f9badc0b79..45c9811cc0 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -293,7 +293,7 @@ impl HasSource for Param<'_> {
.map(|value| InFile { file_id, value })
}
Callee::Closure(closure, _) => {
- let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure);
+ let InternedClosure(owner, expr_id) = closure.loc(db);
let (_, source_map) = ExpressionStore::with_source_map(db, owner);
let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
let root = db.parse_or_expand(file_id);
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index bc5e164830..d24e2c0cb5 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -45,7 +45,7 @@ use std::{
};
use arrayvec::ArrayVec;
-use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin};
+use base_db::{CrateDisplayName, CrateOrigin, LangCrateOrigin, all_crates};
use either::Either;
use hir_def::{
AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId,
@@ -85,14 +85,14 @@ use hir_ty::{
GenericPredicates, InferenceResult, ParamEnvAndCrate, TyDefId, TyLoweringDiagnostic,
ValueTyDefId, all_super_traits, autoderef, check_orphan_rules,
consteval::try_const_usize,
- db::{InternedClosureId, InternedCoroutineId},
+ db::{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,
},
- mir::{MutBorrowKind, interpret_mir},
+ mir::interpret_mir,
next_solver::{
AliasTy, AnyImplId, ClauseKind, ConstKind, DbInterner, EarlyBinder, EarlyParamRegion,
ErrorGuaranteed, GenericArg, GenericArgs, ParamConst, ParamEnv, PolyFnSig, Region,
@@ -108,9 +108,8 @@ use rustc_type_ir::{
TypeVisitor, fast_reject,
inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _},
};
-use smallvec::SmallVec;
use span::{AstIdNode, Edition, FileId};
-use stdx::{format_to, impl_from, never, variance::PhantomCovariantLifetime};
+use stdx::{format_to, impl_from, never};
use syntax::{
AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr,
ast::{self, HasName as _, HasVisibility as _},
@@ -243,7 +242,7 @@ impl Crate {
}
pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> {
- let all_crates = db.all_crates();
+ let all_crates = all_crates(db);
all_crates
.iter()
.copied()
@@ -310,7 +309,7 @@ impl Crate {
}
pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
- db.all_crates().iter().map(|&id| Crate { id }).collect()
+ all_crates(db).iter().map(|&id| Crate { id }).collect()
}
/// Try to get the root URL of the documentation of a crate.
@@ -334,7 +333,7 @@ impl Crate {
}
fn core(db: &dyn HirDatabase) -> Option<Crate> {
- db.all_crates()
+ all_crates(db)
.iter()
.copied()
.find(|&krate| {
@@ -342,6 +341,10 @@ impl Crate {
})
.map(Crate::from)
}
+
+ pub fn is_unstable_feature_enabled(self, db: &dyn HirDatabase, feature: &Symbol) -> bool {
+ crate_def_map(db, self.id).is_unstable_feature_enabled(feature)
+ }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -547,7 +550,24 @@ impl HasCrate for ModuleDef {
fn krate(&self, db: &dyn HirDatabase) -> Crate {
match self.module(db) {
Some(module) => module.krate(db),
- None => Crate::core(db).unwrap_or_else(|| db.all_crates()[0].into()),
+ None => Crate::core(db).unwrap_or_else(|| all_crates(db)[0].into()),
+ }
+ }
+}
+
+impl HasAttrs for ModuleDef {
+ fn attr_id(self, db: &dyn HirDatabase) -> attrs::AttrsOwner {
+ match self {
+ ModuleDef::Module(it) => it.attr_id(db),
+ ModuleDef::Function(it) => it.attr_id(db),
+ ModuleDef::Adt(it) => it.attr_id(db),
+ ModuleDef::EnumVariant(it) => it.attr_id(db),
+ ModuleDef::Const(it) => it.attr_id(db),
+ ModuleDef::Static(it) => it.attr_id(db),
+ ModuleDef::Trait(it) => it.attr_id(db),
+ ModuleDef::TypeAlias(it) => it.attr_id(db),
+ ModuleDef::Macro(it) => it.attr_id(db),
+ ModuleDef::BuiltinType(_) => attrs::AttrsOwner::Dummy,
}
}
}
@@ -1239,11 +1259,15 @@ fn emit_def_diagnostic_<'db>(
);
}
DefDiagnosticKind::InvalidDeriveTarget { ast, id } => {
- let derive = id.find_attr_range(db, krate, *ast).3.path_range();
+ let (_, attr) = id.find_attr_range(db, krate, *ast);
+ let derive = attr
+ .path()
+ .map(|path| path.syntax().text_range())
+ .unwrap_or_else(|| attr.syntax().text_range());
acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into());
}
DefDiagnosticKind::MalformedDerive { ast, id } => {
- let derive = id.find_attr_range(db, krate, *ast).2;
+ let derive = id.find_attr_range(db, krate, *ast).1.syntax().text_range();
acc.push(MalformedDerive { range: ast.with_value(derive) }.into());
}
DefDiagnosticKind::MacroDefError { ast, message } => {
@@ -1283,7 +1307,8 @@ fn precise_macro_call_location(
ast_id.with_value(range)
}
MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => {
- let attr_range = attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).2;
+ let attr_range =
+ attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range();
ast_id.with_value(attr_range)
}
}
@@ -2279,11 +2304,9 @@ impl DefWithBody {
}
}
(mir::MutabilityReason::Not, true) => {
- if !infer.mutated_bindings_in_closure.contains(&binding_id) {
- let should_ignore = body[binding_id].name.as_str().starts_with('_');
- if !should_ignore {
- acc.push(UnusedMut { local }.into())
- }
+ let should_ignore = body[binding_id].name.as_str().starts_with('_');
+ if !should_ignore {
+ acc.push(UnusedMut { local }.into())
}
}
}
@@ -2945,7 +2968,7 @@ impl<'db> Param<'db> {
}
}
Callee::Closure(closure, _) => {
- let c = db.lookup_intern_closure(closure);
+ let c = closure.loc(db);
let body_owner = c.0;
let store = ExpressionStore::of(db, c.0);
@@ -3119,7 +3142,7 @@ impl Const {
let interner = DbInterner::new_no_crate(db);
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
db.const_eval(self.id, GenericArgs::empty(interner), None).map(|it| EvaluatedConst {
- const_: it,
+ allocation: it,
def: self.id.into(),
ty,
})
@@ -3134,22 +3157,19 @@ impl HasVisibility for Const {
pub struct EvaluatedConst<'db> {
def: DefWithBodyId,
- const_: hir_ty::next_solver::Const<'db>,
+ allocation: hir_ty::next_solver::Allocation<'db>,
ty: Ty<'db>,
}
impl<'db> EvaluatedConst<'db> {
pub fn render(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
- format!("{}", self.const_.display(db, display_target))
+ format!("{}", self.allocation.display(db, display_target))
}
pub fn render_debug(&self, db: &'db dyn HirDatabase) -> Result<String, MirEvalError> {
- let kind = self.const_.kind();
- if let ConstKind::Value(c) = kind
- && let ty = c.ty.kind()
- && let TyKind::Int(_) | TyKind::Uint(_) = ty
- {
- let b = &c.value.inner().memory;
+ let ty = self.allocation.ty.kind();
+ if let TyKind::Int(_) | TyKind::Uint(_) = ty {
+ let b = &self.allocation.memory;
let value = u128::from_le_bytes(mir::pad16(b, false));
let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(ty, TyKind::Int(_))));
let mut result =
@@ -3161,7 +3181,7 @@ impl<'db> EvaluatedConst<'db> {
return Ok(result);
}
}
- mir::render_const_using_debug_impl(db, self.def, self.const_, self.ty)
+ mir::render_const_using_debug_impl(db, self.def, self.allocation, self.ty)
}
}
@@ -3202,7 +3222,7 @@ impl Static {
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst<'_>, ConstEvalError> {
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity();
db.const_eval_static(self.id).map(|it| EvaluatedConst {
- const_: it,
+ allocation: it,
def: self.id.into(),
ty,
})
@@ -3313,6 +3333,19 @@ impl Trait {
pub fn complete(self, db: &dyn HirDatabase) -> Complete {
Complete::extract(true, self.attrs(db).attrs)
}
+
+ // Feature: Prefer Underscore Import Attribute
+ // Crate authors can declare that their trait prefers to be imported `as _`. This can be used
+ // for example for extension traits. To do that, a trait has to include the attribute
+ // `#[rust_analyzer::prefer_underscore_import]`
+ //
+ // When a trait includes this attribute, flyimport will import it `as _`, and the quickfix
+ // to import it will prefer to import it `as _` (but allow to import it normally as well).
+ //
+ // Malformed attributes will be ignored without warnings.
+ pub fn prefer_underscore_import(self, db: &dyn HirDatabase) -> bool {
+ AttrFlags::query(db, self.id.into()).contains(AttrFlags::PREFER_UNDERSCORE_IMPORT)
+ }
}
impl HasVisibility for Trait {
@@ -3394,7 +3427,7 @@ impl BuiltinType {
}
pub fn ty<'db>(self, db: &'db dyn HirDatabase) -> Type<'db> {
- let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| db.all_crates()[0]);
+ let core = Crate::core(db).map(|core| core.id).unwrap_or_else(|| all_crates(db)[0]);
let interner = DbInterner::new_no_crate(db);
Type::new_for_crate(core, Ty::from_builtin_type(interner, self.inner))
}
@@ -4680,7 +4713,7 @@ impl TypeParam {
pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> {
let self_ty = self.ty(db).ty;
GenericPredicates::query_explicit(db, self.id.parent())
- .iter_identity_copied()
+ .iter_identity()
.filter_map(|pred| match &pred.kind().skip_binder() {
ClauseKind::Trait(trait_ref) if trait_ref.self_ty() == self_ty => {
Some(Trait::from(trait_ref.def_id().0))
@@ -4898,12 +4931,12 @@ impl Impl {
std::iter::successors(module.block(db), |block| block.loc(db).module.block(db))
.filter_map(|block| TraitImpls::for_block(db, block).as_deref())
.for_each(|impls| impls.for_self_ty(&simplified_ty, &mut extend_with_impls));
- for &krate in &**db.all_crates() {
+ for &krate in &*all_crates(db) {
TraitImpls::for_crate(db, krate)
.for_self_ty(&simplified_ty, &mut extend_with_impls);
}
} else {
- for &krate in &**db.all_crates() {
+ for &krate in &*all_crates(db) {
TraitImpls::for_crate(db, krate)
.for_self_ty(&simplified_ty, &mut extend_with_impls);
}
@@ -5087,7 +5120,7 @@ impl<'db> TraitRef<'db> {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum AnyClosureId {
ClosureId(InternedClosureId),
- CoroutineClosureId(InternedCoroutineId),
+ CoroutineClosureId(InternedCoroutineClosureId),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -5122,59 +5155,33 @@ impl<'db> Closure<'db> {
}
pub fn captured_items(&self, db: &'db dyn HirDatabase) -> Vec<ClosureCapture<'db>> {
- let AnyClosureId::ClosureId(id) = self.id else {
- // FIXME: Infer coroutine closures' captures.
- return Vec::new();
+ let closure = match self.id {
+ AnyClosureId::ClosureId(it) => it.loc(db),
+ AnyClosureId::CoroutineClosureId(it) => it.loc(db),
};
- let owner = db.lookup_intern_closure(id).0;
+ let InternedClosure(owner, closure) = closure;
let infer = InferenceResult::of(db, owner);
- let info = infer.closure_info(id);
- info.0
- .iter()
- .cloned()
- .map(|capture| ClosureCapture {
- owner,
- closure: id,
- capture,
- _marker: PhantomCovariantLifetime::new(),
- })
+ 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 })
.collect()
}
- pub fn capture_types(&self, db: &'db dyn HirDatabase) -> Vec<Type<'db>> {
- let AnyClosureId::ClosureId(id) = self.id else {
- // FIXME: Infer coroutine closures' captures.
- return Vec::new();
- };
- let owner = db.lookup_intern_closure(id).0;
- let Some(body_owner) = owner.as_def_with_body() else {
- return Vec::new();
- };
- let infer = InferenceResult::of(db, body_owner);
- let (captures, _) = infer.closure_info(id);
- let env = body_param_env_from_has_crate(db, body_owner);
- captures.iter().map(|capture| Type { env, ty: capture.ty(db, self.subst) }).collect()
- }
-
- pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait {
+ pub fn fn_trait(&self, _db: &dyn HirDatabase) -> FnTrait {
match self.id {
- AnyClosureId::ClosureId(id) => {
- let owner = db.lookup_intern_closure(id).0;
- let Some(body_owner) = owner.as_def_with_body() else {
- return FnTrait::FnOnce;
- };
- let infer = InferenceResult::of(db, body_owner);
- let info = infer.closure_info(id);
- info.1.into()
- }
- AnyClosureId::CoroutineClosureId(_id) => {
- // FIXME: Infer kind for coroutine closures.
- match self.subst.as_coroutine_closure().kind() {
- rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
- rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
- rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
- }
- }
+ AnyClosureId::ClosureId(_) => match self.subst.as_closure().kind() {
+ rustc_type_ir::ClosureKind::Fn => FnTrait::Fn,
+ rustc_type_ir::ClosureKind::FnMut => FnTrait::FnMut,
+ rustc_type_ir::ClosureKind::FnOnce => FnTrait::FnOnce,
+ },
+ AnyClosureId::CoroutineClosureId(_) => match self.subst.as_coroutine_closure().kind() {
+ rustc_type_ir::ClosureKind::Fn => FnTrait::AsyncFn,
+ rustc_type_ir::ClosureKind::FnMut => FnTrait::AsyncFnMut,
+ rustc_type_ir::ClosureKind::FnOnce => FnTrait::AsyncFnOnce,
+ },
}
}
}
@@ -5247,51 +5254,120 @@ impl FnTrait {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ClosureCapture<'db> {
owner: ExpressionStoreOwnerId,
- closure: InternedClosureId,
- capture: hir_ty::CapturedItem,
- _marker: PhantomCovariantLifetime<'db>,
+ closure: ExprId,
+ capture: &'db hir_ty::closure_analysis::CapturedPlace,
+ param_env: ParamEnvAndCrate<'db>,
}
impl<'db> ClosureCapture<'db> {
pub fn local(&self) -> Local {
- Local { parent: self.owner, binding_id: self.capture.local() }
+ Local { parent: self.owner, binding_id: self.capture.captured_local() }
}
/// Returns whether this place has any field (aka. non-deref) projections.
pub fn has_field_projections(&self) -> bool {
- self.capture.has_field_projections()
+ self.capture
+ .place
+ .projections
+ .iter()
+ .any(|proj| matches!(proj.kind, hir_ty::closure_analysis::ProjectionKind::Field { .. }))
}
- pub fn usages(&self) -> CaptureUsages {
- CaptureUsages { parent: self.owner, spans: self.capture.spans() }
+ pub fn usages(&self) -> CaptureUsages<'db> {
+ CaptureUsages { parent: self.owner, sources: &self.capture.info.sources }
}
pub fn kind(&self) -> CaptureKind {
- match self.capture.kind() {
- hir_ty::CaptureKind::ByRef(
- hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared,
+ match self.capture.info.capture_kind {
+ hir_ty::closure_analysis::UpvarCapture::ByValue => CaptureKind::Move,
+ hir_ty::closure_analysis::UpvarCapture::ByUse => CaptureKind::SharedRef, // Good enough?
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::Immutable,
) => CaptureKind::SharedRef,
- hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut {
- kind: MutBorrowKind::ClosureCapture,
- }) => CaptureKind::UniqueSharedRef,
- hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut {
- kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow,
- }) => CaptureKind::MutableRef,
- hir_ty::CaptureKind::ByValue => CaptureKind::Move,
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::UniqueImmutable,
+ ) => CaptureKind::UniqueSharedRef,
+ hir_ty::closure_analysis::UpvarCapture::ByRef(
+ hir_ty::closure_analysis::BorrowKind::Mutable,
+ ) => CaptureKind::MutableRef,
}
}
/// Converts the place to a name that can be inserted into source code.
- pub fn place_to_name(&self, db: &dyn HirDatabase) -> String {
- self.capture.place_to_name(self.owner, db)
+ pub fn place_to_name(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+ let mut result = self.local().name(db).display(db, edition).to_string();
+ for (i, proj) in self.capture.place.projections.iter().enumerate() {
+ match proj.kind {
+ hir_ty::closure_analysis::ProjectionKind::Deref => {}
+ hir_ty::closure_analysis::ProjectionKind::Field { field_idx, variant_idx } => {
+ let ty = self.capture.place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => format_to!(result, "_{field_idx}"),
+ TyKind::Adt(adt_def, _) => {
+ let variant = match adt_def.def_id().0 {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(id) => {
+ id.enum_variants(db).variants[variant_idx as usize].0.into()
+ }
+ };
+ let field = &variant.fields(db).fields()
+ [LocalFieldId::from_raw(la_arena::RawIdx::from_u32(field_idx))];
+ format_to!(result, "_{}", field.name.display(db, edition));
+ }
+ _ => never!("mismatching projection type"),
+ }
+ }
+ _ => never!("unexpected projection kind"),
+ }
+ }
+ result
}
- pub fn display_place_source_code(&self, db: &dyn HirDatabase) -> String {
- self.capture.display_place_source_code(self.owner, db)
+ pub fn display_place_source_code(&self, db: &dyn HirDatabase, edition: Edition) -> String {
+ let mut result = self.local().name(db).display(db, edition).to_string();
+ // We only need the derefs that have no field access after them, autoderef will do the rest.
+ let mut last_derefs = 0;
+ for (i, proj) in self.capture.place.projections.iter().enumerate() {
+ match proj.kind {
+ hir_ty::closure_analysis::ProjectionKind::Deref => last_derefs += 1,
+ hir_ty::closure_analysis::ProjectionKind::Field { field_idx, variant_idx } => {
+ last_derefs = 0;
+
+ let ty = self.capture.place.ty_before_projection(i);
+ match ty.kind() {
+ TyKind::Tuple(_) => format_to!(result, ".{field_idx}"),
+ TyKind::Adt(adt_def, _) => {
+ let variant = match adt_def.def_id().0 {
+ AdtId::StructId(id) => VariantId::from(id),
+ AdtId::UnionId(id) => id.into(),
+ AdtId::EnumId(id) => {
+ // Can't really do that for an enum, unfortunately, so try to do something alike.
+ id.enum_variants(db).variants[variant_idx as usize].0.into()
+ }
+ };
+ let field = &variant.fields(db).fields()
+ [LocalFieldId::from_raw(la_arena::RawIdx::from_u32(field_idx))];
+ format_to!(result, ".{}", field.name.display(db, edition));
+ }
+ _ => never!("mismatching projection type"),
+ }
+ }
+ _ => never!("unexpected projection kind"),
+ }
+ }
+ result.insert_str(0, &"*".repeat(last_derefs));
+ result
+ }
+
+ pub fn ty(&self, _db: &'db dyn HirDatabase) -> Type<'db> {
+ Type { env: self.param_env, ty: self.capture.place.ty() }
}
- pub fn display_place(&self, db: &dyn HirDatabase) -> String {
- self.capture.display_place(self.owner, db)
+ /// The type that is stored in the closure, which is different from [`Self::ty()`], representing
+ /// the place's type, when the capture is by ref.
+ pub fn captured_ty(&self, db: &'db dyn HirDatabase) -> Type<'db> {
+ Type { env: self.param_env, ty: self.capture.captured_ty(db) }
}
}
@@ -5304,38 +5380,43 @@ pub enum CaptureKind {
}
#[derive(Debug, Clone)]
-pub struct CaptureUsages {
+pub struct CaptureUsages<'db> {
parent: ExpressionStoreOwnerId,
- spans: SmallVec<[mir::MirSpan; 3]>,
-}
+ sources: &'db [hir_ty::closure_analysis::CaptureSourceStack],
+}
+
+impl CaptureUsages<'_> {
+ fn is_ref(store: &ExpressionStore, id: ExprOrPatId) -> bool {
+ match id {
+ ExprOrPatId::ExprId(expr) => matches!(store[expr], Expr::Ref { .. }),
+ // FIXME: Figure out if this is correct wrt. match ergonomics.
+ ExprOrPatId::PatId(pat) => match store[pat] {
+ Pat::Bind { id: binding, .. } => matches!(
+ store[binding].mode,
+ BindingAnnotation::Ref | BindingAnnotation::RefMut
+ ),
+ _ => false,
+ },
+ }
+ }
-impl CaptureUsages {
pub fn sources(&self, db: &dyn HirDatabase) -> Vec<CaptureUsageSource> {
- let (body, source_map) = ExpressionStore::with_source_map(db, self.parent);
- let mut result = Vec::with_capacity(self.spans.len());
- for &span in self.spans.iter() {
- let is_ref = span.is_ref_span(body);
- match span {
- mir::MirSpan::ExprId(expr) => {
+ let (store, source_map) = ExpressionStore::with_source_map(db, self.parent);
+ let mut result = Vec::with_capacity(self.sources.len());
+ for source in self.sources {
+ let source = source.final_source();
+ let is_ref = Self::is_ref(store, source);
+ match source {
+ ExprOrPatId::ExprId(expr) => {
if let Ok(expr) = source_map.expr_syntax(expr) {
result.push(CaptureUsageSource { is_ref, source: expr })
}
}
- mir::MirSpan::PatId(pat) => {
+ ExprOrPatId::PatId(pat) => {
if let Ok(pat) = source_map.pat_syntax(pat) {
result.push(CaptureUsageSource { is_ref, source: pat });
}
}
- mir::MirSpan::BindingId(binding) => result.extend(
- source_map
- .patterns_for_binding(binding)
- .iter()
- .filter_map(|&pat| source_map.pat_syntax(pat).ok())
- .map(|pat| CaptureUsageSource { is_ref, source: pat }),
- ),
- mir::MirSpan::SelfParam | mir::MirSpan::Unknown => {
- unreachable!("invalid capture usage span")
- }
}
}
result
@@ -5731,8 +5812,11 @@ impl<'db> Type<'db> {
// FIXME: We don't handle GATs yet.
let projection = Ty::new_alias(
interner,
- AliasTyKind::Projection,
- AliasTy::new_from_args(interner, alias.id.into(), args),
+ AliasTy::new_from_args(
+ interner,
+ AliasTyKind::Projection { def_id: alias.id.into() },
+ args,
+ ),
);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
@@ -5813,6 +5897,18 @@ impl<'db> Type<'db> {
matches!(self.ty.kind(), TyKind::RawPtr(..))
}
+ pub fn is_mutable_raw_ptr(&self) -> bool {
+ // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers).
+ matches!(self.ty.kind(), TyKind::RawPtr(.., hir_ty::next_solver::Mutability::Mut))
+ }
+
+ pub fn as_raw_ptr(&self) -> Option<(Type<'db>, Mutability)> {
+ // Used outside of rust-analyzer (e.g. by `ra_ap_hir` consumers).
+ let TyKind::RawPtr(ty, m) = self.ty.kind() else { return None };
+ let m = Mutability::from_mutable(matches!(m, hir_ty::next_solver::Mutability::Mut));
+ Some((self.derived(ty), m))
+ }
+
pub fn remove_raw_ptr(&self) -> Option<Type<'db>> {
if let TyKind::RawPtr(ty, _) = self.ty.kind() { Some(self.derived(ty)) } else { None }
}
@@ -6336,8 +6432,12 @@ impl<'db> Type<'db> {
}
pub fn as_associated_type_parent_trait(&self, db: &'db dyn HirDatabase) -> Option<Trait> {
- let TyKind::Alias(AliasTyKind::Projection, alias) = self.ty.kind() else { return None };
- match alias.def_id.expect_type_alias().loc(db).container {
+ let TyKind::Alias(AliasTy { kind: AliasTyKind::Projection { def_id }, .. }) =
+ self.ty.kind()
+ else {
+ return None;
+ };
+ match def_id.expect_type_alias().loc(db).container {
ItemContainerId::TraitId(id) => Some(Trait { id }),
_ => None,
}
@@ -6515,7 +6615,7 @@ pub struct Callable<'db> {
enum Callee<'db> {
Def(CallableDefId),
Closure(InternedClosureId, GenericArgs<'db>),
- CoroutineClosure(InternedCoroutineId, GenericArgs<'db>),
+ CoroutineClosure(InternedCoroutineClosureId, GenericArgs<'db>),
FnPtr,
FnImpl(traits::FnTrait),
BuiltinDeriveImplMethod { method: BuiltinDeriveImplMethod, impl_: BuiltinDeriveImplId },
@@ -6653,8 +6753,8 @@ impl Layout {
let offset = stride.bytes() * tail;
self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
}),
- layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
- let tail = memory_index.last_index()?;
+ layout::FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => {
+ let tail = in_memory_order[in_memory_order.len().checked_sub(1)? as u32];
let tail_field_size = field_size(tail.0.into_raw().into_u32() as usize)?;
let offset = offsets.get(tail)?.bytes();
self.0.size.bytes().checked_sub(offset)?.checked_sub(tail_field_size)
@@ -6674,10 +6774,11 @@ impl Layout {
let size = field_size(0)?;
stride.bytes().checked_sub(size)
}
- layout::FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
- let mut reverse_index = vec![None; memory_index.len()];
- for (src, (mem, offset)) in memory_index.iter().zip(offsets.iter()).enumerate() {
- reverse_index[*mem as usize] = Some((src, offset.bytes()));
+ layout::FieldsShape::Arbitrary { ref offsets, ref in_memory_order } => {
+ let mut reverse_index = vec![None; in_memory_order.len()];
+ for (mem, src) in in_memory_order.iter().enumerate() {
+ reverse_index[mem] =
+ Some((src.0.into_raw().into_u32() as usize, offsets[*src].bytes()));
}
if reverse_index.iter().any(|it| it.is_none()) {
stdx::never!();
@@ -7175,7 +7276,7 @@ pub fn resolve_absolute_path<'a, I: Iterator<Item = Symbol> + Clone + 'a>(
.next()
.into_iter()
.flat_map(move |crate_name| {
- db.all_crates()
+ all_crates(db)
.iter()
.filter(|&krate| {
krate
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 4e9e3c44be..b7cc780ae4 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -10,7 +10,7 @@ use std::{
ops::{self, ControlFlow, Not},
};
-use base_db::FxIndexSet;
+use base_db::{FxIndexSet, all_crates, toolchain_channel};
use either::Either;
use hir_def::{
BuiltinDeriveImplId, DefWithBodyId, ExpressionStoreOwnerId, HasModule, MacroId, StructId,
@@ -24,6 +24,7 @@ use hir_def::{
};
use hir_expand::{
EditionedFileId, ExpandResult, FileRange, HirFileId, InMacroFile, MacroCallId,
+ attrs::AstPathExt,
builtin::{BuiltinFnLikeExpander, EagerExpander},
db::ExpandDatabase,
files::{FileRangeWrapper, HirFileRange, InRealFile},
@@ -298,14 +299,15 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
hir_expand::attrs::expand_cfg_attr::<Infallible>(
extra_crate_attrs.chain(ast::attrs_including_inner(&item)),
cfg_options,
- |attr, _, _, _| {
- let hir_expand::attrs::Meta::TokenTree { path, tt } = attr else {
+ |attr, _| {
+ let ast::Meta::TokenTreeMeta(attr) = attr else {
return ControlFlow::Continue(());
};
- if path.segments.len() != 1 {
+ let (Some(segment), Some(tt)) = (attr.path().as_one_segment(), attr.token_tree())
+ else {
return ControlFlow::Continue(());
- }
- let lint_attr = match path.segments[0].text() {
+ };
+ let lint_attr = match &*segment {
"allow" => LintAttr::Allow,
"expect" => LintAttr::Expect,
"warn" => LintAttr::Warn,
@@ -392,7 +394,7 @@ impl<DB: HirDatabase + ?Sized> Semantics<'_, DB> {
}
pub fn is_nightly(&self, krate: Crate) -> bool {
- let toolchain = self.db.toolchain_channel(krate.into());
+ let toolchain = toolchain_channel(self.db.as_dyn_database(), krate.into());
// `toolchain == None` means we're in some detached files. Since we have no information on
// the toolchain being used, let's just allow unstable items to be listed.
matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None)
@@ -458,7 +460,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn parse(&self, file_id: EditionedFileId) -> ast::SourceFile {
let hir_file_id = file_id.into();
- let tree = self.db.parse(file_id).tree();
+ let tree = file_id.parse(self.db).tree();
self.cache(tree.syntax().clone(), hir_file_id);
tree
}
@@ -467,7 +469,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn first_crate(&self, file: FileId) -> Option<Crate> {
match self.file_to_module_defs(file).next() {
Some(module) => Some(module.krate(self.db)),
- None => self.db.all_crates().last().copied().map(Into::into),
+ None => all_crates(self.db).last().copied().map(Into::into),
}
}
@@ -484,7 +486,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn parse_guess_edition(&self, file_id: FileId) -> ast::SourceFile {
let file_id = self.attach_first_edition(file_id);
- let tree = self.db.parse(file_id).tree();
+ let tree = file_id.parse(self.db).tree();
self.cache(tree.syntax().clone(), file_id.into());
tree
}
@@ -554,17 +556,6 @@ impl<'db> SemanticsImpl<'db> {
Some(InFile::new(file_id.into(), node))
}
- pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option<bool> {
- let file_id = self.find_file(attr.syntax()).file_id;
- let krate = match file_id {
- HirFileId::FileId(file_id) => {
- self.file_to_module_defs(file_id.file_id(self.db)).next()?.krate(self.db).id
- }
- HirFileId::MacroFile(macro_file) => self.db.lookup_intern_macro_call(macro_file).krate,
- };
- hir_expand::check_cfg_attr_value(self.db, attr, krate)
- }
-
/// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy
/// expansions.
pub fn expand_allowed_builtins(
@@ -608,8 +599,8 @@ impl<'db> SemanticsImpl<'db> {
Some(self.expand(macro_call_id).map(|it| InFile::new(macro_call_id.into(), it)))
}
- pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
- let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Meta) -> Option<SyntaxNode> {
+ let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?;
let src = self.wrap_node_infile(attr.clone());
let call_id = self.with_ctx(|ctx| {
ctx.attr_to_derive_macro_call(src.with_value(&adt), src).map(|(_, it, _)| it)
@@ -617,7 +608,7 @@ impl<'db> SemanticsImpl<'db> {
Some(self.parse_or_expand(call_id.into()))
}
- pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
+ pub fn resolve_derive_macro(&self, attr: &ast::Meta) -> Option<Vec<Option<Macro>>> {
let calls = self.derive_macro_calls(attr)?;
self.with_ctx(|ctx| {
Some(
@@ -644,7 +635,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn expand_derive_macro(
&self,
- attr: &ast::Attr,
+ attr: &ast::Meta,
) -> Option<Vec<Option<ExpandResult<SyntaxNode>>>> {
let res: Vec<_> = self
.derive_macro_calls(attr)?
@@ -662,9 +653,9 @@ impl<'db> SemanticsImpl<'db> {
fn derive_macro_calls(
&self,
- attr: &ast::Attr,
+ attr: &ast::Meta,
) -> Option<Vec<Option<Either<MacroCallId, BuiltinDeriveImplId>>>> {
- let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ let adt = attr.parent_attr()?.syntax().parent().and_then(ast::Adt::cast)?;
let file_id = self.find_file(adt.syntax()).file_id;
let adt = InFile::new(file_id, &adt);
let src = InFile::new(file_id, attr.clone());
@@ -773,7 +764,11 @@ impl<'db> SemanticsImpl<'db> {
let attr = self.wrap_node_infile(actual_macro_call.clone());
let adt = actual_macro_call.syntax().parent().and_then(ast::Adt::cast)?;
let macro_call_id = self.with_ctx(|ctx| {
- ctx.attr_to_derive_macro_call(attr.with_value(&adt), attr).map(|(_, it, _)| it)
+ ctx.attr_to_derive_macro_call(
+ attr.with_value(&adt),
+ attr.with_value(attr.value.meta()?),
+ )
+ .map(|(_, it, _)| it)
})?;
hir_expand::db::expand_speculative(
self.db,
@@ -1328,7 +1323,7 @@ impl<'db> SemanticsImpl<'db> {
// text ranges of the outer ones, and then all of the inner ones up
// to the invoking attribute so that the inbetween is ignored.
// FIXME: Should cfg_attr be handled differently?
- let (attr, _, _, _) = attr_ids
+ let (attr, _) = attr_ids
.invoc_attr()
.find_attr_range_with_source(db, loc.krate, &item);
let start = attr.syntax().text_range().start();
@@ -1435,7 +1430,7 @@ impl<'db> SemanticsImpl<'db> {
let derive_call = ctx
.attr_to_derive_macro_call(
InFile::new(expansion, &adt),
- InFile::new(expansion, attr.clone()),
+ InFile::new(expansion, meta.clone()),
)?
.1;
@@ -1698,9 +1693,7 @@ impl<'db> SemanticsImpl<'db> {
hir_ty::Adjust::NeverToAny => Adjust::NeverToAny,
hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => {
// FIXME: Should we handle unknown mutability better?
- Adjust::Deref(Some(OverloadedDeref(
- m.map(mutability).unwrap_or(Mutability::Shared),
- )))
+ Adjust::Deref(Some(OverloadedDeref(mutability(m))))
}
hir_ty::Adjust::Deref(None) => Adjust::Deref(None),
hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => {
@@ -2461,7 +2454,7 @@ fn macro_call_to_macro_id(
Either::Left(it) => {
let node = match it.file_id {
HirFileId::FileId(file_id) => {
- it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
+ it.to_ptr(db).to_node(&file_id.parse(db).syntax_node())
}
HirFileId::MacroFile(macro_file) => {
let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
@@ -2473,7 +2466,7 @@ fn macro_call_to_macro_id(
Either::Right(it) => {
let node = match it.file_id {
HirFileId::FileId(file_id) => {
- it.to_ptr(db).to_node(&db.parse(file_id).syntax_node())
+ it.to_ptr(db).to_node(&file_id.parse(db).syntax_node())
}
HirFileId::MacroFile(macro_file) => {
let expansion_info = ctx.cache.get_or_insert_expansion(ctx.db, macro_file);
diff --git a/crates/hir/src/semantics/child_by_source.rs b/crates/hir/src/semantics/child_by_source.rs
index f6d1bec575..babeb35913 100644
--- a/crates/hir/src/semantics/child_by_source.rs
+++ b/crates/hir/src/semantics/child_by_source.rs
@@ -126,8 +126,7 @@ impl ChildBySource for ItemScope {
calls.for_each(|(attr_id, call_id, calls)| {
// FIXME: Is this the right crate?
let krate = call_id.lookup(db).krate;
- // FIXME: Fix cfg_attr handling.
- let (attr, _, _, _) = attr_id.find_attr_range_with_source(db, krate, &adt);
+ let (_, attr) = attr_id.find_attr_range_with_source(db, krate, &adt);
res[keys::DERIVE_MACRO_CALL]
.insert(AstPtr::new(&attr), (attr_id, call_id, calls.into()));
});
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index a9a779a287..d932198b43 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -85,6 +85,7 @@
//! active crate for a given position, and then provide an API to resolve all
//! syntax nodes against this specific crate.
+use base_db::relevant_crates;
use either::Either;
use hir_def::{
AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId,
@@ -145,7 +146,7 @@ impl SourceToDefCache {
return m;
}
self.included_file_cache.insert(file, None);
- for &crate_id in db.relevant_crates(file.file_id(db)).iter() {
+ for &crate_id in relevant_crates(db, file.file_id(db)).iter() {
db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| {
self.included_file_cache.insert(file_id, Some(macro_call_id));
});
@@ -180,7 +181,7 @@ impl SourceToDefCtx<'_, '_> {
self.cache.file_to_def_cache.entry(file).or_insert_with(|| {
let mut mods = SmallVec::new();
- for &crate_id in self.db.relevant_crates(file).iter() {
+ for &crate_id in relevant_crates(self.db, file).iter() {
// Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = crate_def_map(self.db, crate_id);
let n_mods = mods.len();
@@ -397,7 +398,7 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn attr_to_derive_macro_call(
&mut self,
item: InFile<&ast::Adt>,
- src: InFile<ast::Attr>,
+ src: InFile<ast::Meta>,
) -> Option<(AttrId, MacroCallId, &[Option<Either<MacroCallId, BuiltinDeriveImplId>>])> {
let map = self.dyn_map(item)?;
map[keys::DERIVE_MACRO_CALL]
@@ -422,6 +423,7 @@ impl SourceToDefCtx<'_, '_> {
let dyn_map = &map[keys::DERIVE_MACRO_CALL];
adt.value
.attrs()
+ .flat_map(|attr| attr.skip_cfg_attrs())
.filter_map(move |attr| dyn_map.get(&AstPtr::new(&attr)))
.map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids))
})
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 1a34fa9134..6c43f80ce8 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -37,7 +37,7 @@ use hir_ty::{
lang_items::lang_items_for_bin_op,
method_resolution::{self, CandidateId},
next_solver::{
- DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,
+ AliasTy, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,
infer::DbInternerInferExt,
},
traits::structurally_normalize_ty,
@@ -1293,10 +1293,14 @@ impl<'db> SourceAnalyzer<'db> {
PathResolution::Def(ModuleDef::Adt(adt_id.into())),
)
}
- TyKind::Alias(AliasTyKind::Projection, alias) => {
- let assoc_id = alias.def_id.expect_type_alias();
+ TyKind::Alias(AliasTy {
+ kind: AliasTyKind::Projection { def_id },
+ args,
+ ..
+ }) => {
+ let assoc_id = def_id.expect_type_alias();
(
- GenericSubstitution::new(assoc_id.into(), alias.args, env),
+ GenericSubstitution::new(assoc_id.into(), args, env),
PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())),
)
}
diff --git a/crates/ide-assists/src/handlers/add_braces.rs b/crates/ide-assists/src/handlers/add_braces.rs
index da1322de4b..c5ec88ffb8 100644
--- a/crates/ide-assists/src/handlers/add_braces.rs
+++ b/crates/ide-assists/src/handlers/add_braces.rs
@@ -1,7 +1,7 @@
use either::Either;
use syntax::{
AstNode, T,
- ast::{self, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+ ast::{self, edit::AstNodeEdit},
match_ast,
};
@@ -56,15 +56,13 @@ pub(crate) fn add_braces(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
},
expr.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(expr.syntax());
+ let editor = builder.make_editor(expr.syntax());
+ let make = editor.make();
let new_expr = expr.reset_indent().indent(1.into());
let block_expr = make.block_expr(None, Some(new_expr));
editor.replace(expr.syntax(), block_expr.indent(expr.indent_level()).syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
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 d27a6b4ce7..1809b8f305 100644
--- a/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
+++ b/crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
@@ -53,18 +53,18 @@ pub(crate) fn add_explicit_method_call_deref(
"Insert explicit method call derefs",
dot_token.text_range(),
|builder| {
- let mut edit = builder.make_editor(method_call_expr.syntax());
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(method_call_expr.syntax());
+ let make = editor.make();
let mut expr = receiver.clone();
for adjust_kind in adjustments {
- expr = adjust_kind.wrap_expr(expr, &make);
+ expr = adjust_kind.wrap_expr(expr, make);
}
expr = make.expr_paren(expr).into();
- edit.replace(receiver.syntax(), expr.syntax());
+ editor.replace(receiver.syntax(), expr.syntax());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
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 6a408e5254..41e9b6cc84 100644
--- a/crates/ide-assists/src/handlers/add_label_to_loop.rs
+++ b/crates/ide-assists/src/handlers/add_label_to_loop.rs
@@ -3,7 +3,7 @@ use ide_db::{
};
use syntax::{
SyntaxToken, T,
- ast::{self, AstNode, HasLoopBody, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, HasLoopBody},
syntax_editor::{Position, SyntaxEditor},
};
@@ -42,8 +42,8 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
"Add Label",
loop_expr.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(loop_expr.syntax());
+ let editor = builder.make_editor(loop_expr.syntax());
+ let make = editor.make();
let label = make.lifetime("'l");
let elements = vec![
@@ -65,11 +65,10 @@ pub(crate) fn add_label_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
_ => return,
};
if let Some(token) = token {
- insert_label_after_token(&mut editor, &make, &token, ctx, builder);
+ insert_label_after_token(&editor, &token, ctx, builder);
}
});
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
builder.rename();
},
@@ -85,12 +84,12 @@ fn loop_token(loop_expr: &ast::AnyHasLoopBody) -> Option<syntax::SyntaxToken> {
}
fn insert_label_after_token(
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
token: &SyntaxToken,
ctx: &AssistContext<'_>,
builder: &mut SourceChangeBuilder,
) {
+ let make = editor.make();
let label = make.lifetime("'l");
let elements = vec![make.whitespace(" ").into(), label.syntax().clone().into()];
editor.insert_all(Position::after(token), elements);
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 afdced4215..d1f1f9f123 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -1,7 +1,7 @@
use hir::HasSource;
use syntax::{
Edition,
- ast::{self, AstNode, make},
+ ast::{self, AstNode, syntax_factory::SyntaxFactory},
syntax_editor::{Position, SyntaxEditor},
};
@@ -148,7 +148,10 @@ fn add_missing_impl_members_inner(
let target = impl_def.syntax().text_range();
acc.add(AssistId::quick_fix(assist_id), label, target, |edit| {
+ let editor = edit.make_editor(impl_def.syntax());
+ let make = editor.make();
let new_item = add_trait_assoc_items_to_impl(
+ make,
&ctx.sema,
ctx.config,
&missing_items,
@@ -164,6 +167,7 @@ fn add_missing_impl_members_inner(
let mut first_new_item = if let DefaultMethods::No = mode
&& let ast::AssocItem::Fn(func) = &first_new_item
&& let Some(body) = try_gen_trait_body(
+ make,
ctx,
func,
trait_ref,
@@ -172,7 +176,7 @@ fn add_missing_impl_members_inner(
)
&& let Some(func_body) = func.body()
{
- let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree());
+ let (func_editor, _) = SyntaxEditor::new(first_new_item.syntax().clone());
func_editor.replace(func_body.syntax(), body.syntax());
ast::AssocItem::cast(func_editor.finish().new_root().clone())
} else {
@@ -185,14 +189,13 @@ fn add_missing_impl_members_inner(
.chain(other_items.iter().cloned())
.collect::<Vec<_>>();
- let mut editor = edit.make_editor(impl_def.syntax());
if let Some(assoc_item_list) = impl_def.assoc_item_list() {
- assoc_item_list.add_items(&mut editor, new_assoc_items);
+ assoc_item_list.add_items(&editor, new_assoc_items);
} else {
- let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update();
+ let assoc_item_list = make.assoc_item_list(new_assoc_items);
editor.insert_all(
Position::after(impl_def.syntax()),
- vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()],
+ vec![make.whitespace(" ").into(), assoc_item_list.syntax().clone().into()],
);
first_new_item = assoc_item_list.assoc_items().next();
}
@@ -220,18 +223,18 @@ fn add_missing_impl_members_inner(
}
fn try_gen_trait_body(
+ make: &SyntaxFactory,
ctx: &AssistContext<'_>,
func: &ast::Fn,
trait_ref: hir::TraitRef<'_>,
impl_def: &ast::Impl,
edition: Edition,
) -> Option<ast::BlockExpr> {
- let trait_path = make::ext::ident_path(
- &trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(),
- );
+ let trait_path =
+ make.ident_path(&trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string());
let hir_ty = ctx.sema.resolve_type(&impl_def.self_ty()?)?;
let adt = hir_ty.as_adt()?.source(ctx.db())?;
- gen_trait_fn_body(func, &trait_path, &adt.value, Some(trait_ref))
+ gen_trait_fn_body(make, func, &trait_path, &adt.value, Some(trait_ref))
}
#[cfg(test)]
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 b063e5ffce..3c33ddec31 100644
--- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -1,4 +1,4 @@
-use std::iter::{self, Peekable};
+use std::iter;
use either::Either;
use hir::{Adt, AsAssocItem, Crate, FindPathConfig, HasAttrs, ModuleDef, Semantics};
@@ -8,7 +8,7 @@ use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast};
use itertools::Itertools;
use syntax::ast::edit::IndentLevel;
use syntax::ast::syntax_factory::SyntaxFactory;
-use syntax::ast::{self, AstNode, MatchArmList, MatchExpr, Pat, make};
+use syntax::ast::{self, AstNode, MatchArmList, MatchExpr, Pat};
use syntax::syntax_editor::{Position, SyntaxEditor};
use syntax::{SyntaxKind, SyntaxNode, ToSmolStr};
@@ -74,7 +74,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
.filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
.collect();
- let make = SyntaxFactory::with_mappings();
+ let make = SyntaxFactory::without_mappings();
let scope = ctx.sema.scope(expr.syntax())?;
let module = scope.module();
@@ -93,8 +93,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
} else {
None
};
- let (mut missing_pats, is_non_exhaustive, has_hidden_variants): (
- Peekable<Box<dyn Iterator<Item = (ast::Pat, bool)>>>,
+ let (missing_pats, is_non_exhaustive, has_hidden_variants): (
+ Vec<(ast::Pat, bool)>,
bool,
bool,
) = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
@@ -117,15 +117,15 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
.filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
let option_enum = FamousDefs(&ctx.sema, module.krate(ctx.db())).core_option_Option();
- let missing_pats: Box<dyn Iterator<Item = _>> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum)
+ let missing_pats: Vec<_> = if matches!(enum_def, ExtendedEnum::Enum { enum_: e, .. } if Some(e) == option_enum)
{
// Match `Some` variant first.
cov_mark::hit!(option_order);
- Box::new(missing_pats.rev())
+ missing_pats.rev().collect()
} else {
- Box::new(missing_pats)
+ missing_pats.collect()
};
- (missing_pats.peekable(), is_non_exhaustive, has_hidden_variants)
+ (missing_pats, is_non_exhaustive, has_hidden_variants)
} else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr, self_ty.as_ref()) {
let is_non_exhaustive = enum_defs
.iter()
@@ -169,12 +169,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
(ast::Pat::from(make.tuple_pat(patterns)), is_hidden)
})
- .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
- (
- (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(),
- is_non_exhaustive,
- has_hidden_variants,
- )
+ .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat))
+ .collect();
+ (missing_pats, is_non_exhaustive, has_hidden_variants)
} else if let Some((enum_def, len)) =
resolve_array_of_enum_def(&ctx.sema, &expr, self_ty.as_ref())
{
@@ -205,12 +202,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
(ast::Pat::from(make.slice_pat(patterns)), is_hidden)
})
- .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat));
- (
- (Box::new(missing_pats) as Box<dyn Iterator<Item = _>>).peekable(),
- is_non_exhaustive,
- has_hidden_variants,
- )
+ .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat))
+ .collect();
+ (missing_pats, is_non_exhaustive, has_hidden_variants)
} else {
return None;
};
@@ -218,20 +212,31 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
let mut needs_catch_all_arm = is_non_exhaustive && !has_catch_all_arm;
if !needs_catch_all_arm
- && ((has_hidden_variants && has_catch_all_arm) || missing_pats.peek().is_none())
+ && ((has_hidden_variants && has_catch_all_arm) || missing_pats.is_empty())
{
return None;
}
+ let visible_count = missing_pats.iter().filter(|(_, hidden)| !hidden).count();
+ let label = if visible_count == 0 {
+ "Add missing catch-all match arm `_`".to_owned()
+ } else if visible_count == 1 {
+ let pat = &missing_pats.iter().find(|(_, hidden)| !hidden).unwrap().0;
+ format!("Add missing match arm `{pat}`")
+ } else {
+ format!("Add {visible_count} missing match arms")
+ };
+
acc.add(
AssistId::quick_fix("add_missing_match_arms"),
- "Fill match arms",
+ label,
ctx.sema.original_range(match_expr.syntax()).range,
|builder| {
// having any hidden variants means that we need a catch-all arm
needs_catch_all_arm |= has_hidden_variants;
let mut missing_arms = missing_pats
+ .into_iter()
.filter(|(_, hidden)| {
// filter out hidden patterns because they're handled by the catch-all arm
!hidden
@@ -266,12 +271,12 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
}
};
- let mut editor = builder.make_editor(&old_place);
+ let editor = builder.make_editor(&old_place);
let mut arms_edit = ArmsEdit { match_arm_list, place: old_place, last_arm: None };
- arms_edit.remove_wildcard_arms(ctx, &mut editor);
- arms_edit.add_comma_after_last_arm(ctx, &make, &mut editor);
- arms_edit.append_arms(&missing_arms, &make, &mut editor);
+ arms_edit.remove_wildcard_arms(ctx, &editor);
+ arms_edit.add_comma_after_last_arm(ctx, &make, &editor);
+ arms_edit.append_arms(&missing_arms, &make, &editor);
if let Some(cap) = ctx.config.snippet_cap {
if let Some(it) = missing_arms
@@ -292,7 +297,6 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
}
}
- editor.add_mappings(make.take());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -353,7 +357,7 @@ struct ArmsEdit {
}
impl ArmsEdit {
- fn remove_wildcard_arms(&mut self, ctx: &AssistContext<'_>, editor: &mut 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);
@@ -382,7 +386,7 @@ impl ArmsEdit {
}
}
- fn append_arms(&self, arms: &[ast::MatchArm], make: &SyntaxFactory, editor: &mut SyntaxEditor) {
+ fn append_arms(&self, arms: &[ast::MatchArm], make: &SyntaxFactory, editor: &SyntaxEditor) {
let Some(mut before) = self.place.last_token() else {
stdx::never!("match arm list not contain any token");
return;
@@ -415,7 +419,7 @@ impl ArmsEdit {
&self,
ctx: &AssistContext<'_>,
make: &SyntaxFactory,
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
) {
if let Some(last_arm) = &self.last_arm
&& last_arm.comma_token().is_none()
@@ -588,12 +592,12 @@ fn build_pat(
ExtendedVariant::Variant { variant: var, use_self } => {
let edition = module.krate(db).edition(db);
let path = if use_self {
- make::path_from_segments(
+ make.path_from_segments(
[
- make::path_segment(make::name_ref_self_ty()),
- make::path_segment(make::name_ref(
- &var.name(db).display(db, edition).to_smolstr(),
- )),
+ make.path_segment(make.name_ref_self_ty()),
+ make.path_segment(
+ make.name_ref(&var.name(db).display(db, edition).to_smolstr()),
+ ),
],
false,
)
@@ -607,7 +611,7 @@ fn build_pat(
let pats = fields.into_iter().map(|f| {
let name = name_generator.for_type(&f.ty(db).to_type(db), db, edition);
match name {
- Some(name) => make::ext::simple_ident_pat(make.name(&name)).into(),
+ Some(name) => make.ident_pat(false, false, make.name(&name)).into(),
None => make.wildcard_pat().into(),
}
});
@@ -635,7 +639,7 @@ mod tests {
use crate::AssistConfig;
use crate::tests::{
TEST_CONFIG, check_assist, check_assist_not_applicable, check_assist_target,
- check_assist_unresolved, check_assist_with_config,
+ check_assist_unresolved, check_assist_with_config, check_assist_with_label,
};
use super::add_missing_match_arms;
@@ -1828,8 +1832,10 @@ fn foo(t: Test) {
#[test]
fn lazy_computation() {
- // Computing a single missing arm is enough to determine applicability of the assist.
- cov_mark::check_count!(add_missing_match_arms_lazy_computation, 1);
+ // We now collect all missing arms eagerly, so we can show the count
+ // of missing arms.
+ cov_mark::check_count!(add_missing_match_arms_lazy_computation, 4);
+
check_assist_unresolved(
add_missing_match_arms,
r#"
@@ -1842,6 +1848,54 @@ fn foo(tuple: (A, A)) {
}
#[test]
+ fn label_single_missing_arm() {
+ check_assist_with_label(
+ add_missing_match_arms,
+ r#"
+enum A { One, Two }
+fn foo(a: A) {
+ match $0a {
+ A::One => {}
+ }
+}
+"#,
+ "Add missing match arm `A::Two`",
+ );
+ }
+
+ #[test]
+ fn label_multiple_missing_arms() {
+ check_assist_with_label(
+ add_missing_match_arms,
+ r#"
+enum A { One, Two, Three }
+fn foo(a: A) {
+ match $0a {}
+}
+"#,
+ "Add 3 missing match arms",
+ );
+ }
+
+ #[test]
+ fn label_catch_all_only() {
+ check_assist_with_label(
+ add_missing_match_arms,
+ r#"
+//- /main.rs crate:main deps:e
+fn foo(t: ::e::E) {
+ match $0t {
+ e::E::A => {}
+ }
+}
+//- /e.rs crate:e
+pub enum E { A, #[doc(hidden)] B, }
+"#,
+ "Add missing catch-all match arm `_`",
+ );
+ }
+
+ #[test]
fn adds_comma_before_new_arms() {
check_assist(
add_missing_match_arms,
diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs
index c5e722d87e..dcd2124f7b 100644
--- a/crates/ide-assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs
@@ -93,8 +93,8 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
"Add `: _` before assignment operator",
ident.text_range(),
|builder| {
- let mut editor = builder.make_editor(let_stmt.syntax());
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(let_stmt.syntax());
+ let make = editor.make();
if let_stmt.semicolon_token().is_none() {
editor.insert(
@@ -141,14 +141,12 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
ident.text_range(),
|builder| {
builder.trigger_parameter_hints();
-
- let make = SyntaxFactory::with_mappings();
- let mut editor = match &turbofish_target {
+ let editor = match &turbofish_target {
Either::Left(it) => builder.make_editor(it.syntax()),
Either::Right(it) => builder.make_editor(it.syntax()),
};
- let fish_head = get_fish_head(&make, number_of_arguments);
+ let fish_head = get_fish_head(editor.make(), number_of_arguments);
match turbofish_target {
Either::Left(path_segment) => {
@@ -180,8 +178,6 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
editor.add_annotation(arg.syntax(), builder.make_placeholder_snippet(cap));
}
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 4ee4970248..b87a757047 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -80,10 +80,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
_ => return None,
};
- let make = SyntaxFactory::with_mappings();
-
- let demorganed = bin_expr.clone_subtree();
- let mut editor = SyntaxEditor::new(demorganed.syntax().clone());
+ let (editor, demorganed) = SyntaxEditor::with_ast_node(&bin_expr);
+ let make = editor.make();
editor.replace(demorganed.op_token()?, make.token(inv_token));
let mut exprs = VecDeque::from([
@@ -99,7 +97,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec));
exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec));
} else {
- let mut inv = invert_boolean_expression(&make, expr);
+ let mut inv = invert_boolean_expression(make, expr);
if precedence(&inv).needs_parentheses_in(prec) {
inv = make.expr_paren(inv).into();
}
@@ -109,7 +107,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
return None;
}
} else {
- let mut inv = invert_boolean_expression(&make, demorganed.clone());
+ let mut inv = invert_boolean_expression(make, demorganed.clone());
if precedence(&inv).needs_parentheses_in(prec) {
inv = make.expr_paren(inv).into();
}
@@ -117,7 +115,6 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
}
- editor.add_mappings(make.finish_with_mappings());
let edit = editor.finish();
let demorganed = ast::Expr::cast(edit.new_root().clone())?;
@@ -127,7 +124,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
"Apply De Morgan's law",
op_range,
|builder| {
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(bin_expr.syntax());
+ let make = editor.make();
+
let (target_node, result_expr) = if let Some(neg_expr) = bin_expr
.syntax()
.parent()
@@ -142,9 +141,9 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
bin_expr.syntax().parent().and_then(ast::ParenExpr::cast)
{
cov_mark::hit!(demorgan_double_parens);
- (paren_expr.syntax().clone(), add_bang_paren(&make, demorganed))
+ (paren_expr.syntax().clone(), add_bang_paren(make, demorganed))
} else {
- (bin_expr.syntax().clone(), add_bang_paren(&make, demorganed))
+ (bin_expr.syntax().clone(), add_bang_paren(make, demorganed))
};
let final_expr = if target_node
@@ -157,9 +156,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
result_expr
};
- let mut editor = builder.make_editor(&target_node);
editor.replace(&target_node, final_expr.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -207,8 +204,8 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
label,
op_range,
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(method_call.syntax());
+ let editor = builder.make_editor(method_call.syntax());
+ let make = editor.make();
// replace the method name
let new_name = match name.text().as_str() {
"all" => make.name_ref("any"),
@@ -218,7 +215,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
editor.replace(name.syntax(), new_name.syntax());
// negate all tail expressions in the closure body
- let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e);
+ let tail_cb = &mut |e: &_| tail_cb_impl(&editor, e);
walk_expr(&closure_body, &mut |expr| {
if let ast::Expr::ReturnExpr(ret_expr) = expr
&& let Some(ret_expr_arg) = &ret_expr.expr()
@@ -241,8 +238,6 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
} else {
editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG));
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -270,18 +265,18 @@ fn validate_method_call_expr(
it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr))
}
-fn tail_cb_impl(editor: &mut SyntaxEditor, make: &SyntaxFactory, e: &ast::Expr) {
+fn tail_cb_impl(editor: &SyntaxEditor, e: &ast::Expr) {
match e {
ast::Expr::BreakExpr(break_expr) => {
if let Some(break_expr_arg) = break_expr.expr() {
- for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, make, e))
+ for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, e))
}
}
ast::Expr::ReturnExpr(_) => {
// all return expressions have already been handled by the walk loop
}
e => {
- let inverted_body = invert_boolean_expression(make, e.clone());
+ let inverted_body = invert_boolean_expression(editor.make(), e.clone());
editor.replace(e.syntax(), inverted_body.syntax());
}
}
diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs
index de5dfdf4d9..f9d618790c 100644
--- a/crates/ide-assists/src/handlers/auto_import.rs
+++ b/crates/ide-assists/src/handlers/auto_import.rs
@@ -6,7 +6,7 @@ use ide_db::{
active_parameter::ActiveParameter,
helpers::mod_path_to_ast,
imports::{
- import_assets::{ImportAssets, ImportCandidate, LocatedImport},
+ import_assets::{ImportAssets, ImportCandidate, LocatedImport, TraitImportCandidate},
insert_use::{ImportScope, insert_use, insert_use_as_alias},
},
};
@@ -123,44 +123,48 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
let (assist_id, import_name) =
(AssistId::quick_fix("auto_import"), import_path.display(ctx.db(), edition));
- acc.add_group(
- &group_label,
- assist_id,
- format!("Import `{import_name}`"),
- range,
- |builder| {
+ 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);
- },
- );
-
- match import_assets.import_candidate() {
- ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) => {
- let is_method =
- matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_));
- let type_ = if is_method { "method" } else { "item" };
- let group_label = GroupLabel(format!(
- "Import a trait for {} {} by alias",
- type_,
- name.assoc_item_name.text()
- ));
- acc.add_group(
- &group_label,
- assist_id,
- format!("Import `{import_name} as _`"),
- range,
- |builder| {
- let scope = builder.make_import_scope_mut(scope.clone());
- insert_use_as_alias(
- &scope,
- mod_path_to_ast(&import_path, edition),
- &ctx.config.insert_use,
- edition,
- );
- },
+ })
+ };
+ let add_underscore_import = |acc: &mut Assists, name: &TraitImportCandidate<'_>, label| {
+ let is_method =
+ matches!(import_assets.import_candidate(), ImportCandidate::TraitMethod(_));
+ let type_ = if is_method { "method" } else { "item" };
+ let group_label = GroupLabel(format!(
+ "Import a trait for {} {} by alias",
+ type_,
+ 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(
+ &scope,
+ mod_path_to_ast(&import_path, edition),
+ &ctx.config.insert_use,
+ edition,
);
- }
- _ => {}
+ });
+ };
+
+ if let ImportCandidate::TraitAssocItem(name) | ImportCandidate::TraitMethod(name) =
+ import_assets.import_candidate()
+ {
+ if let hir::ItemInNs::Types(hir::ModuleDef::Trait(trait_to_import)) =
+ import.item_to_import
+ && trait_to_import.prefer_underscore_import(ctx.db())
+ {
+ // Flip the order of the suggestions and show a preference for `as _` in the name.
+ add_underscore_import(acc, name, format!("Import `{import_name}`"));
+ add_normal_import(acc, format!("Import `{import_name}` without `as _`"));
+ } else {
+ add_normal_import(acc, format!("Import `{import_name}`"));
+ add_underscore_import(acc, name, format!("Import `{import_name} as _`"));
+ }
+ } else {
+ add_normal_import(acc, format!("Import `{import_name}`"));
}
}
Some(())
@@ -1928,4 +1932,101 @@ fn f() {
"#;
check_auto_import_order(before, &["Import `foo::wanted`", "Import `quux::wanted`"]);
}
+
+ #[test]
+ fn consider_definition_kind() {
+ check_assist(
+ auto_import,
+ r#"
+//- /eyre.rs crate:eyre
+#[macro_export]
+macro_rules! eyre {
+ () => {};
+}
+
+//- /color-eyre.rs crate:color-eyre deps:eyre
+pub use eyre;
+
+//- /main.rs crate:main deps:color-eyre
+fn main() {
+ ey$0re!();
+}
+ "#,
+ r#"
+use color_eyre::eyre::eyre;
+
+fn main() {
+ eyre!();
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn prefer_underscore_import() {
+ check_assist_by_label(
+ auto_import,
+ r#"
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.b$0ar();
+}
+ "#,
+ r#"
+use foo::Ext as _;
+
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.bar();
+}
+ "#,
+ "Import `foo::Ext`",
+ );
+ check_assist_by_label(
+ auto_import,
+ r#"
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.b$0ar();
+}
+ "#,
+ r#"
+use foo::Ext;
+
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.bar();
+}
+ "#,
+ "Import `foo::Ext` without `as _`",
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/bind_unused_param.rs b/crates/ide-assists/src/handlers/bind_unused_param.rs
index 0e85a77822..50e4a367e9 100644
--- a/crates/ide-assists/src/handlers/bind_unused_param.rs
+++ b/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -1,5 +1,5 @@
use crate::assist_context::{AssistContext, Assists};
-use ide_db::{LineIndexDatabase, assists::AssistId, defs::Definition};
+use ide_db::{assists::AssistId, defs::Definition, line_index};
use syntax::{
AstNode,
ast::{self, HasName, edit::AstNodeEdit},
@@ -43,7 +43,7 @@ pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
format!("Bind as `let _ = {name};`"),
param.syntax().text_range(),
|builder| {
- let line_index = ctx.db().line_index(ctx.vfs_file_id());
+ let line_index = line_index(ctx.db(), ctx.vfs_file_id());
let indent = func.indent_level();
let text_indent = indent + 1;
diff --git a/crates/ide-assists/src/handlers/convert_bool_then.rs b/crates/ide-assists/src/handlers/convert_bool_then.rs
index b3bfe5b8c4..a2a71bcba6 100644
--- a/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -77,8 +77,7 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
"Convert `if` expression to `bool::then` call",
target,
|builder| {
- let closure_body = closure_body.clone_subtree();
- let mut editor = SyntaxEditor::new(closure_body.syntax().clone());
+ let (editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body);
// Rewrite all `Some(e)` in tail position to `e`
for_each_tail_expr(&closure_body, &mut |e| {
let e = match e {
@@ -96,13 +95,13 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
let edit = editor.finish();
let closure_body = ast::Expr::cast(edit.new_root().clone()).unwrap();
- let mut editor = builder.make_editor(expr.syntax());
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(expr.syntax());
+ let make = editor.make();
let closure_body = match closure_body {
ast::Expr::BlockExpr(block) => unwrap_trivial_block(block),
e => e,
};
- let cond = if invert_cond { invert_boolean_expression(&make, cond) } else { cond };
+ let cond = if invert_cond { invert_boolean_expression(make, cond) } else { cond };
let parenthesize = matches!(
cond,
@@ -129,8 +128,6 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
let arg_list = make.arg_list(Some(make.expr_closure(None, closure_body).into()));
let mcall = make.expr_method_call(cond, make.name_ref("then"), arg_list);
editor.replace(expr.syntax(), mcall.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -188,8 +185,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
e => mapless_make.block_expr(None, Some(e)),
};
- let closure_body = closure_body.clone_subtree();
- let mut editor = SyntaxEditor::new(closure_body.syntax().clone());
+ let (editor, closure_body) = SyntaxEditor::with_ast_node(&closure_body);
// Wrap all tails in `Some(...)`
let none_path = mapless_make.expr_path(mapless_make.ident_path("None"));
let some_path = mapless_make.expr_path(mapless_make.ident_path("Some"));
@@ -212,8 +208,8 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
let edit = editor.finish();
let closure_body = ast::BlockExpr::cast(edit.new_root().clone()).unwrap();
- let mut editor = builder.make_editor(mcall.syntax());
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(mcall.syntax());
+ let make = editor.make();
let cond = match &receiver {
ast::Expr::ParenExpr(expr) => expr.expr().unwrap_or(receiver),
@@ -227,8 +223,6 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
)
.indent(mcall.indent_level());
editor.replace(mcall.syntax().clone(), if_expr.syntax().clone());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
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 9f9ced98d7..acade43397 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -24,7 +24,7 @@ use crate::assist_context::{AssistContext, Assists};
// This converts a closure to a freestanding function, changing all captures to parameters.
//
// ```
-// # //- minicore: copy
+// # //- minicore: copy, fn
// # struct String;
// # impl String {
// # fn new() -> Self {}
@@ -90,6 +90,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
}
})
.collect::<Option<Vec<_>>>()?;
+ let capture_params_start = params.len();
let mut body = closure.body()?.clone_for_update();
let mut is_gen = false;
@@ -152,7 +153,8 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
.map(|(_, _, it)| it.clone())
.unwrap_or_else(|| make::name("fun_name"));
let captures = closure_ty.captured_items(ctx.db());
- let capture_tys = closure_ty.capture_types(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());
@@ -163,22 +165,28 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
for (capture, capture_ty) in std::iter::zip(&captures, &capture_tys) {
// FIXME: Allow configuring the replacement of `self`.
- let capture_name =
- if capture.local().is_self(ctx.db()) && !capture.has_field_projections() {
- make::name("this")
- } else {
- make::name(&capture.place_to_name(ctx.db()))
- };
+ let is_self = capture.local().is_self(ctx.db()) && !capture.has_field_projections();
+ let capture_name = if is_self {
+ make::name("this")
+ } else {
+ make::name(&capture.place_to_name(ctx.db(), ctx.edition()))
+ };
closure_mentioned_generic_params.extend(capture_ty.generic_params(ctx.db()));
let capture_ty = capture_ty
.display_source_code(ctx.db(), module.into(), true)
.unwrap_or_else(|_| "_".to_owned());
- params.push(make::param(
+ let param = make::param(
ast::Pat::IdentPat(make::ident_pat(false, false, capture_name.clone_subtree())),
make::ty(&capture_ty),
- ));
+ );
+ if is_self {
+ // Always put `this` first.
+ params.insert(capture_params_start, param);
+ } else {
+ params.push(param);
+ }
for capture_usage in capture.usages().sources(ctx.db()) {
if capture_usage.file_id() != ctx.file_id() {
@@ -188,24 +196,32 @@ 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 expr = match capture_usage_source {
+ let mut expr = match capture_usage_source {
Either::Left(expr) => expr,
Either::Right(pat) => {
let Some(expr) = expr_of_pat(pat) else { continue };
expr
}
};
+ if !capture_usage.is_ref() {
+ expr = peel_ref(expr);
+ }
let replacement = wrap_capture_in_deref_if_needed(
&expr,
&capture_name,
capture.kind(),
- capture_usage.is_ref(),
+ matches!(expr, ast::Expr::RefExpr(_)) || capture_usage.is_ref(),
)
.clone_for_update();
capture_usages_replacement_map.push((expr, replacement));
}
- captures_as_args.push(capture_as_arg(ctx, capture));
+ let capture_as_arg = capture_as_arg(ctx, capture);
+ if is_self {
+ captures_as_args.insert(0, capture_as_arg);
+ } else {
+ captures_as_args.push(capture_as_arg);
+ }
}
let (closure_type_params, closure_where_clause) =
@@ -463,24 +479,29 @@ fn compute_closure_type_params(
(Some(make::generic_param_list(include_params)), where_clause)
}
+fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
+ loop {
+ if ast::ParenExpr::can_cast(expr.syntax().kind()) {
+ let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) else { break };
+ expr = parent;
+ } else {
+ break;
+ }
+ }
+ expr
+}
+
+fn peel_ref(mut expr: ast::Expr) -> ast::Expr {
+ expr = peel_parens(expr);
+ expr.syntax().parent().and_then(ast::RefExpr::cast).map(Into::into).unwrap_or(expr)
+}
+
fn wrap_capture_in_deref_if_needed(
expr: &ast::Expr,
capture_name: &ast::Name,
capture_kind: CaptureKind,
is_ref: bool,
) -> ast::Expr {
- fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
- loop {
- if ast::ParenExpr::can_cast(expr.syntax().kind()) {
- let Some(parent) = expr.syntax().parent().and_then(ast::Expr::cast) else { break };
- expr = parent;
- } else {
- break;
- }
- }
- expr
- }
-
let capture_name = make::expr_path(make::path_from_text(&capture_name.text()));
if capture_kind == CaptureKind::Move || is_ref {
return capture_name;
@@ -507,8 +528,11 @@ fn wrap_capture_in_deref_if_needed(
}
fn capture_as_arg(ctx: &AssistContext<'_>, capture: &ClosureCapture<'_>) -> ast::Expr {
- let place = parse_expr_from_str(&capture.display_place_source_code(ctx.db()), ctx.edition())
- .expect("`display_place_source_code()` produced an invalid expr");
+ let place = parse_expr_from_str(
+ &capture.display_place_source_code(ctx.db(), ctx.edition()),
+ ctx.edition(),
+ )
+ .expect("`display_place_source_code()` produced an invalid expr");
let needs_mut = match capture.kind() {
CaptureKind::SharedRef => false,
CaptureKind::MutableRef | CaptureKind::UniqueSharedRef => true,
@@ -688,7 +712,7 @@ mod tests {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore:copy
+//- minicore: copy, fn
fn main() {
let s = &mut true;
let closure = |$0| { *s = false; };
@@ -710,7 +734,7 @@ fn main() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore:copy
+//- minicore: copy, fn
struct A { a: i32, b: bool }
fn main() {
let mut a = A { a: 123, b: false };
@@ -740,8 +764,8 @@ fn main() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore:copy
-struct A { b: &'static B, c: i32 }
+//- minicore: copy, fn
+struct A { b: &'static mut B, c: i32 }
struct B(bool, i32);
struct C;
impl C {
@@ -756,7 +780,7 @@ impl C {
}
"#,
r#"
-struct A { b: &'static B, c: i32 }
+struct A { b: &'static mut B, c: i32 }
struct B(bool, i32);
struct C;
impl C {
@@ -778,7 +802,7 @@ impl C {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore:copy
+//- minicore: copy, fn
struct A { b: &'static B, c: i32 }
struct B(bool, i32);
impl A {
@@ -795,10 +819,10 @@ struct A { b: &'static B, c: i32 }
struct B(bool, i32);
impl A {
fn foo(&self) {
- fn closure(self_b_1: &i32) {
- let b = *self_b_1;
+ fn closure(self_b: &B) {
+ let b = self_b.1;
}
- closure(&self.b.1);
+ closure(self.b);
}
}
"#,
@@ -810,7 +834,7 @@ impl A {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy, future
+//- minicore: copy, future, async_fn
fn foo(&self) {
let closure = async |$0| 1;
closure();
@@ -832,7 +856,7 @@ fn foo(&self) {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy, future
+//- minicore: copy, future, fn
fn foo() {
let closure = |$0| async { 1 };
closure();
@@ -878,7 +902,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let closure = |$0| {};
closure();
@@ -898,7 +922,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let a = 1;
let closure = |$0| a;
@@ -918,7 +942,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let closure = |$0| 'label: {};
closure();
@@ -936,7 +960,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let closure = |$0| {
const { () }
@@ -956,7 +980,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let closure = |$0| unsafe { };
closure();
@@ -974,7 +998,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
{
let closure = |$0| match () {
@@ -1049,7 +1073,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
struct A { b: B }
struct B(bool, i32);
fn foo() {
@@ -1066,7 +1090,7 @@ struct B(bool, i32);
fn foo() {
let mut a = A { b: B(true, 0) };
fn closure(a_b_1: &mut i32) {
- let A { b: B(_, ref mut c) } = a_b_1;
+ let A { b: B(_, ref mut c) } = *a_b_1;
}
closure(&mut a.b.1);
}
@@ -1079,7 +1103,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let (mut a, b) = (0.1, "abc");
let closure = |$0p1: i32, p2: &mut bool| {
@@ -1107,7 +1131,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let (mut a, b) = (0.1, "abc");
let closure = |$0p1: i32, p2| {
@@ -1145,7 +1169,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, fn
fn foo() {
let (mut a, b) = (0.1, "abc");
let closure = |$0p1: i32, p2| {
@@ -1183,7 +1207,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore: copy
+//- minicore: copy, from
struct Foo<A, B, const C: usize>(A, B);
impl<A, B: From<A>, const C: usize> Foo<A, B, C> {
fn foo<D, E, F, G>(a: A, b: D)
@@ -1244,7 +1268,7 @@ fn foo() {
check_assist(
convert_closure_to_fn,
r#"
-//- minicore:copy
+//- minicore: copy, fn
fn main() {
let a = &mut true;
let closure = |$0| {
diff --git a/crates/ide-assists/src/handlers/convert_comment_block.rs b/crates/ide-assists/src/handlers/convert_comment_block.rs
index 0d36a5ddb3..f242fe8314 100644
--- a/crates/ide-assists/src/handlers/convert_comment_block.rs
+++ b/crates/ide-assists/src/handlers/convert_comment_block.rs
@@ -382,6 +382,21 @@ fn main() {
}
#[test]
+ fn empty_block_to_line() {
+ check_assist(
+ convert_comment_block,
+ r#"
+/**/$0
+fn main() {}
+"#,
+ r#"
+
+fn main() {}
+"#,
+ );
+ }
+
+ #[test]
fn end_of_line_block_to_line() {
check_assist_not_applicable(
convert_comment_block,
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 15f324eff3..9eb4c0584b 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
@@ -2,7 +2,7 @@ use hir::{Name, sym};
use ide_db::{famous_defs::FamousDefs, syntax_helpers::suggest_name};
use syntax::{
AstNode,
- ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel, syntax_factory::SyntaxFactory},
+ ast::{self, HasAttrs, HasLoopBody, edit::IndentLevel},
syntax_editor::Position,
};
@@ -48,8 +48,8 @@ pub(crate) fn convert_for_loop_to_while_let(
"Replace this for loop with `while let`",
for_loop.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(for_loop.syntax());
+ let editor = builder.make_editor(for_loop.syntax());
+ let make = editor.make();
let (iterable, method) = if impls_core_iter(&ctx.sema, &iterable) {
(iterable, None)
@@ -81,16 +81,11 @@ pub(crate) fn convert_for_loop_to_while_let(
let indent = IndentLevel::from_node(for_loop.syntax());
if let Some(label) = for_loop.label() {
- let label = label.syntax().clone_for_update();
+ let label = label.syntax();
editor.insert(Position::before(for_loop.syntax()), make.whitespace(" "));
editor.insert(Position::before(for_loop.syntax()), label);
}
- crate::utils::insert_attributes(
- for_loop.syntax(),
- &mut editor,
- for_loop.attrs().map(|it| it.clone_for_update()),
- &make,
- );
+ crate::utils::insert_attributes(for_loop.syntax(), &editor, for_loop.attrs());
editor.insert(
Position::before(for_loop.syntax()),
@@ -110,7 +105,6 @@ pub(crate) fn convert_for_loop_to_while_let(
editor.replace(for_loop.syntax(), while_loop.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
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 66ccd2a4e4..18f3ced414 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -74,8 +74,8 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
"Convert From to TryFrom",
impl_.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(impl_.syntax());
+ let editor = builder.make_editor(impl_.syntax());
+ let make = editor.make();
editor.replace(trait_ty.syntax(), make.ty(&format!("TryFrom<{from_type}>")).syntax());
editor.replace(
@@ -83,11 +83,11 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
make.ty("Result<Self, Self::Error>").syntax(),
);
editor.replace(from_fn_name.syntax(), make.name("try_from").syntax());
- editor.replace(tail_expr.syntax(), wrap_ok(&make, tail_expr.clone()).syntax());
+ editor.replace(tail_expr.syntax(), wrap_ok(make, tail_expr.clone()).syntax());
for r in return_exprs {
let t = r.expr().unwrap_or_else(|| make.expr_unit());
- editor.replace(t.syntax(), wrap_ok(&make, t.clone()).syntax());
+ editor.replace(t.syntax(), wrap_ok(make, t.clone()).syntax());
}
let error_type_alias =
@@ -111,7 +111,6 @@ pub(crate) fn convert_from_to_tryfrom(acc: &mut Assists, ctx: &AssistContext<'_>
make.whitespace("\n").syntax_element(),
],
);
- editor.add_mappings(make.finish_with_mappings());
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 63b1a0193b..cc5cc490f1 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
@@ -1,12 +1,11 @@
use hir::{Name, sym};
use ide_db::famous_defs::FamousDefs;
-use stdx::format_to;
use syntax::{
AstNode,
- ast::{self, HasArgList, HasLoopBody, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+ ast::{self, HasArgList, HasLoopBody, edit::AstNodeEdit},
};
-use crate::{AssistContext, AssistId, Assists};
+use crate::{AssistContext, AssistId, Assists, utils::wrap_paren};
// Assist: convert_iter_for_each_to_for
//
@@ -57,7 +56,9 @@ pub(crate) fn convert_iter_for_each_to_for(
"Replace this `Iterator::for_each` with a for loop",
range,
|builder| {
- let make = SyntaxFactory::with_mappings();
+ let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax);
+ let editor = builder.make_editor(target_node);
+ let make = editor.make();
let indent =
stmt.as_ref().map_or_else(|| method.indent_level(), ast::ExprStmt::indent_level);
@@ -68,9 +69,6 @@ pub(crate) fn convert_iter_for_each_to_for(
.indent(indent);
let expr_for_loop = make.expr_for_loop(param, receiver, block);
-
- let target_node = stmt.as_ref().map_or(method.syntax(), AstNode::syntax);
- let mut editor = builder.make_editor(target_node);
editor.replace(target_node, expr_for_loop.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
@@ -116,32 +114,40 @@ pub(crate) fn convert_for_loop_with_for_each(
"Replace this for loop with `Iterator::for_each`",
for_loop.syntax().text_range(),
|builder| {
- let mut buf = String::new();
+ let editor = builder.make_editor(for_loop.syntax());
+ let make = editor.make();
+
+ let mut receiver = iterable.clone();
- if let Some((expr_behind_ref, method, krate)) =
+ let iter_method = if let Some((expr_behind_ref, method, krate)) =
is_ref_and_impls_iter_method(&ctx.sema, &iterable)
{
+ receiver = expr_behind_ref;
// We have either "for x in &col" and col implements a method called iter
// or "for x in &mut col" and col implements a method called iter_mut
- format_to!(
- buf,
- "{expr_behind_ref}.{}()",
- method.display(ctx.db(), krate.edition(ctx.db()))
- );
- } else if let ast::Expr::RangeExpr(..) = iterable {
- // range expressions need to be parenthesized for the syntax to be correct
- format_to!(buf, "({iterable})");
- } else if impls_core_iter(&ctx.sema, &iterable) {
- format_to!(buf, "{iterable}");
- } else if let ast::Expr::RefExpr(_) = iterable {
- format_to!(buf, "({iterable}).into_iter()");
+ method.display(ctx.db(), krate.edition(ctx.db())).to_string()
} else {
- format_to!(buf, "{iterable}.into_iter()");
+ "into_iter".to_owned()
+ };
+
+ receiver = wrap_paren(receiver, make, ast::prec::ExprPrecedence::Postfix);
+
+ if !impls_core_iter(&ctx.sema, &iterable) {
+ receiver = make
+ .expr_method_call(receiver, make.name_ref(&iter_method), make.arg_list([]))
+ .into();
}
- format_to!(buf, ".for_each(|{pat}| {body});");
+ let loop_arg = make.expr_closure([make.untyped_param(pat)], body.into());
+ let for_each = make.expr_method_call(
+ receiver,
+ make.name_ref("for_each"),
+ make.arg_list([loop_arg.into()]),
+ );
+ let for_each = make.expr_stmt(for_each.into());
- builder.replace(for_loop.syntax().text_range(), buf)
+ editor.replace(for_loop.syntax(), for_each.syntax());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
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 d2336a4a5d..1ae12390ee 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
@@ -1,8 +1,8 @@
use syntax::T;
use syntax::ast::RangeItem;
use syntax::ast::edit::AstNodeEdit;
-use syntax::ast::syntax_factory::SyntaxFactory;
use syntax::ast::{self, AstNode, HasName, LetStmt, Pat};
+use syntax::syntax_editor::SyntaxEditor;
use crate::{AssistContext, AssistId, Assists};
@@ -25,6 +25,7 @@ use crate::{AssistContext, AssistId, Assists};
// }
// ```
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
.find_token_syntax_at_offset(T![else])
@@ -32,10 +33,12 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
.or_else(|| ctx.find_token_syntax_at_offset(T![let])?.parent())?;
let let_stmt = LetStmt::cast(let_stmt)?;
let else_block = let_stmt.let_else()?.block_expr()?;
- let else_expr = if else_block.statements().next().is_none() {
- else_block.tail_expr()?
+ let else_expr = if else_block.statements().next().is_none()
+ && let Some(tail_expr) = else_block.tail_expr()
+ {
+ tail_expr.reset_indent()
} else {
- else_block.into()
+ else_block.reset_indent().into()
};
let init = let_stmt.initializer()?;
// Ignore let stmt with type annotation
@@ -43,10 +46,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
return None;
}
let pat = let_stmt.pat()?;
-
- let make = SyntaxFactory::with_mappings();
let mut idents = Vec::default();
- let pat_without_mut = remove_mut_and_collect_idents(&make, &pat, &mut idents)?;
+ let pat_without_mut = remove_mut_and_collect_idents(&editor, &pat, &mut idents)?;
let bindings = idents
.into_iter()
.filter_map(|ref pat| {
@@ -68,8 +69,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
},
let_stmt.syntax().text_range(),
|builder| {
- let mut editor = builder.make_editor(let_stmt.syntax());
-
+ let make = editor.make();
let binding_paths = bindings
.iter()
.map(|(name, _)| make.expr_path(make.ident_path(&name.to_string())))
@@ -91,8 +91,8 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
},
);
let else_arm = make.match_arm(make.wildcard_pat().into(), None, else_expr);
- let match_ = make.expr_match(init, make.match_arm_list([binding_arm, else_arm]));
- let match_ = match_.reset_indent();
+ let arms = [binding_arm, else_arm].map(|arm| arm.indent(1.into()));
+ let match_ = make.expr_match(init, make.match_arm_list(arms));
let match_ = match_.indent(let_stmt.indent_level());
if bindings.is_empty() {
@@ -113,18 +113,17 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<'
);
editor.replace(let_stmt.syntax(), new_let_stmt.syntax());
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
fn remove_mut_and_collect_idents(
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
pat: &ast::Pat,
acc: &mut Vec<ast::IdentPat>,
) -> Option<ast::Pat> {
+ let make = editor.make();
Some(match pat {
ast::Pat::IdentPat(p) => {
acc.push(p.clone());
@@ -133,90 +132,92 @@ fn remove_mut_and_collect_idents(
p.ref_token().is_some() && p.mut_token().is_some(),
p.name()?,
);
- if let Some(inner) = p.pat() {
- non_mut_pat.set_pat(remove_mut_and_collect_idents(make, &inner, acc));
- }
+ let non_mut_pat = if let Some(inner) = p.pat() {
+ non_mut_pat.set_pat(remove_mut_and_collect_idents(editor, &inner, acc), editor)
+ } else {
+ non_mut_pat
+ };
non_mut_pat.into()
}
ast::Pat::BoxPat(p) => {
- make.box_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into()
+ let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
+ make.box_pat(pat).into()
+ }
+ ast::Pat::OrPat(p) => {
+ let pats = p
+ .pats()
+ .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc))
+ .collect::<Option<Vec<_>>>()?;
+ make.or_pat(pats, p.leading_pipe().is_some()).into()
}
- ast::Pat::OrPat(p) => make
- .or_pat(
- p.pats()
- .map(|pat| remove_mut_and_collect_idents(make, &pat, acc))
- .collect::<Option<Vec<_>>>()?,
- p.leading_pipe().is_some(),
- )
- .into(),
ast::Pat::ParenPat(p) => {
- make.paren_pat(remove_mut_and_collect_idents(make, &p.pat()?, acc)?).into()
+ let pat = remove_mut_and_collect_idents(editor, &p.pat()?, acc)?;
+ make.paren_pat(pat).into()
}
- ast::Pat::RangePat(p) => make
- .range_pat(
- if let Some(start) = p.start() {
- Some(remove_mut_and_collect_idents(make, &start, acc)?)
- } else {
- None
- },
- if let Some(end) = p.end() {
- Some(remove_mut_and_collect_idents(make, &end, acc)?)
- } else {
- None
- },
- )
- .into(),
- ast::Pat::RecordPat(p) => make
- .record_pat_with_fields(
+ ast::Pat::RangePat(p) => {
+ let start = if let Some(start) = p.start() {
+ Some(remove_mut_and_collect_idents(editor, &start, acc)?)
+ } else {
+ None
+ };
+ let end = if let Some(end) = p.end() {
+ Some(remove_mut_and_collect_idents(editor, &end, acc)?)
+ } else {
+ None
+ };
+ make.range_pat(start, end).into()
+ }
+ ast::Pat::RecordPat(p) => {
+ let fields = p
+ .record_pat_field_list()?
+ .fields()
+ .map(|field| {
+ remove_mut_and_collect_idents(editor, &field.pat()?, acc).map(|pat| {
+ if let Some(name_ref) = field.name_ref() {
+ make.record_pat_field(name_ref, pat)
+ } else {
+ make.record_pat_field_shorthand(pat)
+ }
+ })
+ })
+ .collect::<Option<Vec<_>>>()?;
+ make.record_pat_with_fields(
p.path()?,
- make.record_pat_field_list(
- p.record_pat_field_list()?
- .fields()
- .map(|field| {
- remove_mut_and_collect_idents(make, &field.pat()?, acc).map(|pat| {
- if let Some(name_ref) = field.name_ref() {
- make.record_pat_field(name_ref, pat)
- } else {
- make.record_pat_field_shorthand(pat)
- }
- })
- })
- .collect::<Option<Vec<_>>>()?,
- p.record_pat_field_list()?.rest_pat(),
- ),
+ make.record_pat_field_list(fields, p.record_pat_field_list()?.rest_pat()),
)
- .into(),
+ .into()
+ }
ast::Pat::RefPat(p) => {
let inner = p.pat()?;
if let ast::Pat::IdentPat(ident) = inner {
acc.push(ident);
- p.clone_for_update().into()
+ p.clone().into()
} else {
- make.ref_pat(remove_mut_and_collect_idents(make, &inner, acc)?).into()
+ let pat = remove_mut_and_collect_idents(editor, &inner, acc)?;
+ make.ref_pat(pat).into()
}
}
- ast::Pat::SlicePat(p) => make
- .slice_pat(
- p.pats()
- .map(|pat| remove_mut_and_collect_idents(make, &pat, acc))
- .collect::<Option<Vec<_>>>()?,
- )
- .into(),
- ast::Pat::TuplePat(p) => make
- .tuple_pat(
- p.fields()
- .map(|field| remove_mut_and_collect_idents(make, &field, acc))
- .collect::<Option<Vec<_>>>()?,
- )
- .into(),
- ast::Pat::TupleStructPat(p) => make
- .tuple_struct_pat(
- p.path()?,
- p.fields()
- .map(|field| remove_mut_and_collect_idents(make, &field, acc))
- .collect::<Option<Vec<_>>>()?,
- )
- .into(),
+ ast::Pat::SlicePat(p) => {
+ let pats = p
+ .pats()
+ .map(|pat| remove_mut_and_collect_idents(editor, &pat, acc))
+ .collect::<Option<Vec<_>>>()?;
+ make.slice_pat(pats).into()
+ }
+ ast::Pat::TuplePat(p) => {
+ let pats = p
+ .fields()
+ .map(|field| remove_mut_and_collect_idents(editor, &field, acc))
+ .collect::<Option<Vec<_>>>()?;
+ make.tuple_pat(pats).into()
+ }
+ ast::Pat::TupleStructPat(p) => {
+ let fields = p
+ .fields()
+ .map(|field| remove_mut_and_collect_idents(editor, &field, acc))
+ .collect::<Option<Vec<_>>>()?;
+ make.tuple_struct_pat(p.path()?, fields).into()
+ }
ast::Pat::RestPat(_)
| ast::Pat::LiteralPat(_)
| ast::Pat::PathPat(_)
@@ -299,6 +300,81 @@ fn main() {
}
#[test]
+ fn convert_let_else_to_match_with_empty_else_block() {
+ check_assist(
+ convert_let_else_to_match,
+ r"
+fn main() {
+ let Ok(x) = f() else$0 {};
+}",
+ r"
+fn main() {
+ let x = match f() {
+ Ok(x) => x,
+ _ => {}
+ };
+}",
+ );
+ }
+
+ #[test]
+ fn convert_let_else_to_match_with_some_indent() {
+ check_assist(
+ convert_let_else_to_match,
+ r#"
+mod indent {
+ fn main() {
+ let Ok(x) = f() else$0 {
+ log();
+ unreachable!(
+ "..."
+ );
+ };
+ }
+}"#,
+ r#"
+mod indent {
+ fn main() {
+ let x = match f() {
+ Ok(x) => x,
+ _ => {
+ log();
+ unreachable!(
+ "..."
+ );
+ }
+ };
+ }
+}"#,
+ );
+
+ check_assist(
+ convert_let_else_to_match,
+ r#"
+mod indent {
+ fn main() {
+ let Ok(x) = f() else$0 {
+ unreachable!(
+ "..."
+ )
+ };
+ }
+}"#,
+ r#"
+mod indent {
+ fn main() {
+ let x = match f() {
+ Ok(x) => x,
+ _ => unreachable!(
+ "..."
+ ),
+ };
+ }
+}"#,
+ );
+ }
+
+ #[test]
fn convert_let_else_to_match_const_ref() {
check_assist(
convert_let_else_to_match,
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 1a6d176c90..bc49acc1ef 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
@@ -1,7 +1,7 @@
use ide_db::defs::{Definition, NameRefClass};
use syntax::{
AstNode, SyntaxNode,
- ast::{self, HasName, Name, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+ ast::{self, HasName, Name, edit::AstNodeEdit},
syntax_editor::SyntaxEditor,
};
@@ -121,9 +121,8 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
// Rename `extracted` with `binding` in `pat`.
fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
- let syntax = pat.syntax().clone_subtree();
- let mut editor = SyntaxEditor::new(syntax.clone());
- let make = SyntaxFactory::with_mappings();
+ let (editor, syntax) = SyntaxEditor::new(pat.syntax().clone());
+ let make = editor.make();
let extracted = extracted
.iter()
.map(|e| e.syntax().text_range() - pat.syntax().text_range().start())
@@ -138,18 +137,14 @@ fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> Syn
if let Some(name_ref) = record_pat_field.field_name() {
editor.replace(
record_pat_field.syntax(),
- make.record_pat_field(
- make.name_ref(&name_ref.text()),
- binding.clone_for_update(),
- )
- .syntax(),
+ make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone())
+ .syntax(),
);
}
} else {
- editor.replace(extracted_syntax, binding.syntax().clone_for_update());
+ editor.replace(extracted_syntax, binding.syntax());
}
}
- editor.add_mappings(make.finish_with_mappings());
let new_node = editor.finish().new_root().clone();
if let Some(pat) = ast::Pat::cast(new_node.clone()) {
pat.dedent(1.into()).syntax().clone()
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 aaf727058c..5b691dba5e 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
@@ -3,9 +3,7 @@ use ide_db::{defs::Definition, search::FileReference};
use syntax::{
NodeOrToken, SyntaxKind, SyntaxNode, T,
algo::next_non_trivia_token,
- ast::{
- self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory,
- },
+ ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
match_ast,
syntax_editor::{Element, Position, SyntaxEditor},
};
@@ -101,27 +99,26 @@ fn edit_struct_def(
) {
// Note that we don't need to consider macro files in this function because this is
// currently not triggered for struct definitions inside macro calls.
+ let editor = builder.make_editor(strukt.syntax());
+ let make = editor.make();
+
let tuple_fields = record_fields.fields().filter_map(|f| {
- let field = ast::make::tuple_field(f.visibility(), f.ty()?);
- let mut editor = SyntaxEditor::new(field.syntax().clone());
- editor.insert_all(
+ let (field_editor, field) =
+ SyntaxEditor::with_ast_node(&make.tuple_field(f.visibility(), f.ty()?));
+ field_editor.insert_all(
Position::first_child_of(field.syntax()),
- f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
+ f.attrs().map(|attr| attr.syntax().clone().into()).collect(),
);
- let field_syntax = editor.finish().new_root().clone();
- let field = ast::TupleField::cast(field_syntax)?;
- Some(field)
+ let field_syntax = field_editor.finish().new_root().clone();
+ ast::TupleField::cast(field_syntax)
});
- let make = SyntaxFactory::without_mappings();
- let mut edit = builder.make_editor(strukt.syntax());
-
let tuple_fields = make.tuple_field_list(tuple_fields);
let mut elements = vec![tuple_fields.syntax().clone().into()];
if let Either::Left(strukt) = strukt {
if let Some(w) = strukt.where_clause() {
- edit.delete(w.syntax());
+ editor.delete(w.syntax());
elements.extend([
make.whitespace("\n").into(),
@@ -136,23 +133,23 @@ fn edit_struct_def(
.and_then(|tok| tok.next_token())
.filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
{
- edit.delete(tok);
+ editor.delete(tok);
}
} else {
elements.push(make.token(T![;]).into());
}
}
- edit.replace_with_many(record_fields.syntax(), elements);
+ editor.replace_with_many(record_fields.syntax(), elements);
if let Some(tok) = record_fields
.l_curly_token()
.and_then(|tok| tok.prev_token())
.filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
{
- edit.delete(tok)
+ editor.delete(tok)
}
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
}
fn edit_struct_references(
@@ -168,18 +165,18 @@ fn edit_struct_references(
for (file_id, refs) in usages {
let source = ctx.sema.parse(file_id);
- let mut edit = builder.make_editor(source.syntax());
+ let editor = builder.make_editor(source.syntax());
for r in refs {
- process_struct_name_reference(ctx, r, &mut edit, &source);
+ process_struct_name_reference(ctx, r, &editor, &source);
}
- builder.add_file_edits(file_id.file_id(ctx.db()), edit);
+ builder.add_file_edits(file_id.file_id(ctx.db()), editor);
}
}
fn process_struct_name_reference(
ctx: &AssistContext<'_>,
r: FileReference,
- edit: &mut SyntaxEditor,
+ edit: &SyntaxEditor,
source: &ast::SourceFile,
) -> Option<()> {
// First check if it's the last semgnet of a path that directly belongs to a record
@@ -232,7 +229,7 @@ fn process_struct_name_reference(
fn record_to_tuple_struct_like<T, I>(
ctx: &AssistContext<'_>,
source: &ast::SourceFile,
- edit: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
field_list: T,
fields: impl FnOnce(&T) -> I,
) -> Option<()>
@@ -240,7 +237,7 @@ where
T: AstNode,
I: IntoIterator<Item = ast::NameRef>,
{
- let make = SyntaxFactory::without_mappings();
+ let make = editor.make();
let orig = ctx.sema.original_range_opt(field_list.syntax())?;
let list_range = cover_edit_range(source.syntax(), orig.range);
@@ -254,13 +251,13 @@ where
};
if l_curly.kind() == T!['{'] {
- delete_whitespace(edit, l_curly.prev_token());
- delete_whitespace(edit, l_curly.next_token());
- edit.replace(l_curly, make.token(T!['(']));
+ delete_whitespace(editor, l_curly.prev_token());
+ delete_whitespace(editor, l_curly.next_token());
+ editor.replace(l_curly, make.token(T!['(']));
}
if r_curly.kind() == T!['}'] {
- delete_whitespace(edit, r_curly.prev_token());
- edit.replace(r_curly, make.token(T![')']));
+ delete_whitespace(editor, r_curly.prev_token());
+ editor.replace(r_curly, make.token(T![')']));
}
for name_ref in fields(&field_list) {
@@ -270,14 +267,14 @@ where
if let Some(colon) = next_non_trivia_token(name_range.end().clone())
&& colon.kind() == T![:]
{
- edit.delete(&colon);
- edit.delete_all(name_range);
+ editor.delete(&colon);
+ editor.delete_all(name_range);
if let Some(next) = next_non_trivia_token(colon.clone())
&& next.kind() != T!['}']
{
// Avoid overlapping delete whitespace on `{ field: }`
- delete_whitespace(edit, colon.next_token());
+ delete_whitespace(editor, colon.next_token());
}
}
}
@@ -289,7 +286,6 @@ fn edit_field_references(
builder: &mut SourceChangeBuilder,
fields: impl Iterator<Item = ast::RecordField>,
) {
- let make = SyntaxFactory::without_mappings();
for (index, field) in fields.enumerate() {
let field = match ctx.sema.to_def(&field) {
Some(it) => it,
@@ -299,13 +295,14 @@ fn edit_field_references(
let usages = def.usages(&ctx.sema).all();
for (file_id, refs) in usages {
let source = ctx.sema.parse(file_id);
- let mut edit = builder.make_editor(source.syntax());
+ let editor = builder.make_editor(source.syntax());
+ let make = editor.make();
for r in refs {
if let Some(name_ref) = r.name.as_name_ref() {
// Only edit the field reference if it's part of a `.field` access
if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
- edit.replace_all(
+ editor.replace_all(
cover_edit_range(source.syntax(), r.range),
vec![make.name_ref(&index.to_string()).syntax().clone().into()],
);
@@ -313,12 +310,12 @@ fn edit_field_references(
}
}
- builder.add_file_edits(file_id.file_id(ctx.db()), edit);
+ builder.add_file_edits(file_id.file_id(ctx.db()), editor);
}
}
}
-fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option<impl Element>) {
+fn delete_whitespace(edit: &SyntaxEditor, whitespace: Option<impl Element>) {
let Some(whitespace) = whitespace else { return };
let NodeOrToken::Token(token) = whitespace.syntax_element() else { return };
@@ -328,8 +325,7 @@ fn delete_whitespace(edit: &mut SyntaxEditor, whitespace: Option<impl Element>)
}
fn remove_trailing_comma(w: ast::WhereClause) -> SyntaxNode {
- let w = w.syntax().clone_subtree();
- let mut editor = SyntaxEditor::new(w.clone());
+ let (editor, w) = SyntaxEditor::new(w.syntax().clone());
if let Some(last) = w.last_child_or_token()
&& last.kind() == T![,]
{
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 2e649f14be..c83f8b0765 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
@@ -6,7 +6,7 @@ use syntax::{
T,
algo::previous_non_trivia_token,
ast::{
- self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make,
+ self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit,
syntax_factory::SyntaxFactory,
},
syntax_editor::{Element, Position, SyntaxEditor},
@@ -36,11 +36,13 @@ use crate::assist_context::{AssistContext, Assists};
// }
// ```
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])?;
let for_ = ast::ForExpr::cast(for_kw.parent()?)?;
let ast::Pat::IdentPat(pat) = for_.pat()? else { return None };
let iterable = for_.iterable()?;
- let (start, end, step, inclusive) = extract_range(&iterable)?;
+ let (start, end, step, inclusive) = extract_range(&iterable, make)?;
let name = pat.name()?;
let body = for_.loop_body()?.stmt_list()?;
let label = for_.label();
@@ -55,13 +57,11 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
description,
for_.syntax().text_range(),
|builder| {
- let mut edit = builder.make_editor(for_.syntax());
- let make = SyntaxFactory::with_mappings();
-
+ let make = editor.make();
let indent = for_.indent_level();
let pat = make.ident_pat(pat.ref_token().is_some(), true, name.clone());
let let_stmt = make.let_stmt(pat.into(), None, Some(start));
- edit.insert_all(
+ editor.insert_all(
Position::before(for_.syntax()),
vec![
let_stmt.syntax().syntax_element(),
@@ -86,39 +86,36 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
elements.push(make.token(T![loop]).syntax_element());
}
- edit.replace_all(
+ editor.replace_all(
for_kw.syntax_element()..=iterable.syntax().syntax_element(),
elements,
);
let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) };
- process_loop_body(
- body,
- label,
- &mut edit,
- vec![
- make.whitespace(&format!("\n{}", indent + 1)).syntax_element(),
- make.expr_bin(var_expr, op, step).syntax().syntax_element(),
- make.token(T![;]).syntax_element(),
- ],
- );
-
- edit.add_mappings(make.finish_with_mappings());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ let incrementer = vec![
+ make.whitespace(&format!("\n{}", indent + 1)).syntax_element(),
+ make.expr_bin(var_expr, op, step).syntax().syntax_element(),
+ make.token(T![;]).syntax_element(),
+ ];
+ process_loop_body(body, label, &editor, incrementer);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
-fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option<ast::Expr>, ast::Expr, bool)> {
+fn extract_range(
+ iterable: &ast::Expr,
+ make: &SyntaxFactory,
+) -> Option<(ast::Expr, Option<ast::Expr>, ast::Expr, bool)> {
Some(match iterable {
- ast::Expr::ParenExpr(expr) => extract_range(&expr.expr()?)?,
+ ast::Expr::ParenExpr(expr) => extract_range(&expr.expr()?, make)?,
ast::Expr::RangeExpr(range) => {
let inclusive = range.op_kind()? == ast::RangeOp::Inclusive;
- (range.start()?, range.end(), make::expr_literal("1").into(), inclusive)
+ (range.start()?, range.end(), make.expr_literal("1").into(), inclusive)
}
ast::Expr::MethodCallExpr(call) if call.name_ref()?.text() == "step_by" => {
let [step] = Itertools::collect_array(call.arg_list()?.args())?;
- let (start, end, _, inclusive) = extract_range(&call.receiver()?)?;
+ let (start, end, _, inclusive) = extract_range(&call.receiver()?, make)?;
(start, end, step, inclusive)
}
_ => return None,
@@ -128,12 +125,13 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option<ast::Expr>,
fn process_loop_body(
body: ast::StmtList,
label: Option<ast::Label>,
- edit: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
incrementer: Vec<SyntaxElement>,
) -> Option<()> {
+ let make = editor.make();
let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element();
- let new_body = body.indent(1.into()).clone_subtree();
+ let new_body = body.indent(1.into());
let mut continues = vec![];
collect_continue_to(
&mut continues,
@@ -143,7 +141,7 @@ fn process_loop_body(
);
if continues.is_empty() {
- edit.insert_all(Position::after(last), incrementer);
+ editor.insert_all(Position::after(last), incrementer);
return Some(());
}
@@ -154,23 +152,23 @@ fn process_loop_body(
let first = children.next()?;
let block_content = first.clone()..=children.last().unwrap_or(first);
- let continue_label = make::lifetime("'cont");
- let break_expr = make::expr_break(Some(continue_label.clone()), None).clone_for_update();
- let mut new_edit = SyntaxEditor::new(new_body.syntax().clone());
+ let continue_label = make.lifetime("'cont");
+ let break_expr = make.expr_break(Some(continue_label.clone()), None);
+ let (new_edit, _) = SyntaxEditor::new(new_body.syntax().clone());
for continue_expr in &continues {
new_edit.replace(continue_expr.syntax(), break_expr.syntax());
}
let new_body = new_edit.finish().new_root().clone();
let elements = itertools::chain(
[
- continue_label.syntax().clone_for_update().syntax_element(),
- make::token(T![:]).syntax_element(),
- make::tokens::single_space().syntax_element(),
+ continue_label.syntax().syntax_element(),
+ make.token(T![:]).syntax_element(),
+ make.whitespace(" ").syntax_element(),
new_body.syntax_element(),
],
incrementer,
);
- edit.replace_all(block_content, elements.collect());
+ editor.replace_all(block_content, elements.collect());
Some(())
}
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 db45916792..791a6a26af 100644
--- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -1,17 +1,19 @@
use std::iter::once;
use either::Either;
-use hir::{Semantics, TypeInfo};
+use hir::Semantics;
use ide_db::{RootDatabase, ty_filter::TryEnum};
use syntax::{
AstNode,
- SyntaxKind::{CLOSURE_EXPR, FN, FOR_EXPR, LOOP_EXPR, WHILE_EXPR, WHITESPACE},
+ SyntaxKind::WHITESPACE,
SyntaxNode, T,
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
syntax_factory::SyntaxFactory,
},
+ match_ast,
+ syntax_editor::SyntaxEditor,
};
use crate::{
@@ -71,9 +73,7 @@ fn if_expr_to_guarded_return(
) -> Option<()> {
let make = SyntaxFactory::without_mappings();
let else_block = match if_expr.else_branch() {
- Some(ast::ElseBranch::Block(block_expr)) if is_never_block(&ctx.sema, &block_expr) => {
- Some(block_expr)
- }
+ Some(ast::ElseBranch::Block(block_expr)) => Some(block_expr),
Some(_) => return None,
_ => None,
};
@@ -96,25 +96,20 @@ fn if_expr_to_guarded_return(
let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
- if parent_block.tail_expr() != Some(if_expr.clone().into())
- && !(else_block.is_some() && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind()))
- {
- return None;
- }
-
// check for early return and continue
if is_early_block(&then_block) || is_never_block(&ctx.sema, &then_branch) {
return None;
}
- let parent_container = parent_block.syntax().parent()?;
+ let container = container_of(&parent_block)?;
+ let else_block = ElseBlock::new(&ctx.sema, else_block, &container)?;
- let early_expression = else_block
- .or_else(|| {
- early_expression(parent_container, &ctx.sema, &make)
- .map(ast::make::tail_only_block_expr)
- })?
- .reset_indent();
+ if parent_block.tail_expr() != Some(if_expr.clone().into())
+ && !(else_block.is_never_block
+ && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind()))
+ {
+ return None;
+ }
then_block.syntax().first_child_or_token().map(|t| t.kind() == T!['{'])?;
@@ -135,8 +130,10 @@ fn if_expr_to_guarded_return(
"Convert to guarded return",
target,
|edit| {
- let make = SyntaxFactory::without_mappings();
+ let editor = edit.make_editor(if_expr.syntax());
+ let make = editor.make();
let if_indent_level = IndentLevel::from_node(if_expr.syntax());
+ let early_expression = else_block.make_early_block(&ctx.sema, make);
let replacement = let_chains.into_iter().map(|expr| {
if let ast::Expr::LetExpr(let_expr) = &expr
&& let (Some(pat), Some(expr)) = (let_expr.pat(), let_expr.expr())
@@ -149,8 +146,8 @@ fn if_expr_to_guarded_return(
} else {
// If.
let new_expr = {
- let then_branch = clean_stmt_block(&early_expression, &make);
- let cond = invert_boolean_expression(&make, expr);
+ let then_branch = clean_stmt_block(&early_expression, make);
+ let cond = invert_boolean_expression(make, expr);
make.expr_if(cond, then_branch, None).indent(if_indent_level)
};
new_expr.syntax().clone()
@@ -174,7 +171,6 @@ fn if_expr_to_guarded_return(
.take_while(|i| *i != end_of_then),
)
.collect();
- let mut editor = edit.make_editor(if_expr.syntax());
editor.replace_with_many(if_expr.syntax(), then_statements);
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
@@ -204,68 +200,158 @@ fn let_stmt_to_guarded_return(
let happy_pattern = try_enum.happy_pattern(pat);
let target = let_stmt.syntax().text_range();
- let make = SyntaxFactory::without_mappings();
- let early_expression: ast::Expr = {
- let parent_block =
- let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
- let parent_container = parent_block.syntax().parent()?;
-
- early_expression(parent_container, &ctx.sema, &make)?
- };
+ let parent_block = let_stmt.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;
+ let container = container_of(&parent_block)?;
+ let else_block = ElseBlock::new(&ctx.sema, None, &container)?;
acc.add(
AssistId::refactor_rewrite("convert_to_guarded_return"),
"Convert to guarded return",
target,
|edit| {
+ let editor = edit.make_editor(let_stmt.syntax());
+ let make = editor.make();
let let_indent_level = IndentLevel::from_node(let_stmt.syntax());
- let make = SyntaxFactory::without_mappings();
let replacement = {
let let_else_stmt = make.let_else_stmt(
happy_pattern,
let_stmt.ty(),
expr.reset_indent(),
- ast::make::tail_only_block_expr(early_expression),
+ else_block.make_early_block(&ctx.sema, make),
);
let let_else_stmt = let_else_stmt.indent(let_indent_level);
let_else_stmt.syntax().clone()
};
- let mut editor = edit.make_editor(let_stmt.syntax());
editor.replace(let_stmt.syntax(), replacement);
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
-fn early_expression(
- parent_container: SyntaxNode,
- sema: &Semantics<'_, RootDatabase>,
- make: &SyntaxFactory,
-) -> Option<ast::Expr> {
- let return_none_expr = || {
- let none_expr = make.expr_path(make.ident_path("None"));
- make.expr_return(Some(none_expr))
- };
- if let Some(fn_) = ast::Fn::cast(parent_container.clone())
- && let Some(fn_def) = sema.to_def(&fn_)
- && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &fn_def.ret_type(sema.db))
- {
- return Some(return_none_expr().into());
+fn container_of(block: &ast::BlockExpr) -> Option<SyntaxNode> {
+ if block.label().is_some() {
+ return Some(block.syntax().clone());
}
- if let Some(body) = ast::ClosureExpr::cast(parent_container.clone()).and_then(|it| it.body())
- && let Some(ret_ty) = sema.type_of_expr(&body).map(TypeInfo::original)
- && let Some(TryEnum::Option) = TryEnum::from_ty(sema, &ret_ty)
- {
- return Some(return_none_expr().into());
+ block.syntax().parent()
+}
+
+struct ElseBlock<'db> {
+ exist_else_block: Option<ast::BlockExpr>,
+ is_never_block: bool,
+ kind: EarlyKind<'db>,
+}
+
+impl<'db> ElseBlock<'db> {
+ fn new(
+ sema: &Semantics<'db, RootDatabase>,
+ exist_else_block: Option<ast::BlockExpr>,
+ parent_container: &SyntaxNode,
+ ) -> Option<Self> {
+ let is_never_block = exist_else_block.as_ref().is_some_and(|it| is_never_block(sema, it));
+ let kind = EarlyKind::from_node(parent_container, sema)?;
+
+ Some(Self { exist_else_block, is_never_block, kind })
+ }
+
+ fn make_early_block(
+ self,
+ sema: &Semantics<'_, RootDatabase>,
+ make: &SyntaxFactory,
+ ) -> ast::BlockExpr {
+ let Some(block_expr) = self.exist_else_block 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();
+ }
+
+ let (editor, block_expr) = SyntaxEditor::with_ast_node(&block_expr.reset_indent());
+ let make = editor.make();
+
+ let last_stmt = block_expr.statements().last().map(|it| it.syntax().clone());
+ let tail_expr = block_expr.tail_expr().map(|it| it.syntax().clone());
+ let Some(last_element) = tail_expr.clone().or(last_stmt.clone()) else {
+ return make.tail_only_block_expr(self.kind.make_early_expr(sema, make, None));
+ };
+ let whitespace = last_element.prev_sibling_or_token().filter(|it| it.kind() == WHITESPACE);
+
+ if let Some(tail_expr) = block_expr.tail_expr()
+ && !self.kind.is_unit()
+ {
+ let early_expr = self.kind.make_early_expr(sema, make, Some(tail_expr.clone()));
+ 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(),
+ };
+ let whitespace =
+ make.whitespace(&whitespace.map_or(String::new(), |it| it.to_string()));
+ let early_expr = self.kind.make_early_expr(sema, make, None).syntax().clone().into();
+ editor.replace_with_many(
+ last_element,
+ vec![last_stmt.into(), whitespace.into(), early_expr],
+ );
+ }
+
+ ast::BlockExpr::cast(editor.finish().new_root().clone()).unwrap()
}
+}
- Some(match parent_container.kind() {
- WHILE_EXPR | LOOP_EXPR | FOR_EXPR => make.expr_continue(None).into(),
- FN | CLOSURE_EXPR => make.expr_return(None).into(),
- _ => return None,
- })
+enum EarlyKind<'db> {
+ Continue,
+ Break(ast::Lifetime, hir::Type<'db>),
+ Return(hir::Type<'db>),
+}
+
+impl<'db> EarlyKind<'db> {
+ fn from_node(
+ parent_container: &SyntaxNode,
+ sema: &Semantics<'db, RootDatabase>,
+ ) -> Option<Self> {
+ match_ast! {
+ match parent_container {
+ ast::Fn(it) => Some(Self::Return(sema.to_def(&it)?.ret_type(sema.db))),
+ ast::ClosureExpr(it) => Some(Self::Return(sema.type_of_expr(&it.body()?)?.original)),
+ ast::BlockExpr(it) => Some(Self::Break(it.label()?.lifetime()?, sema.type_of_expr(&it.into())?.original)),
+ ast::WhileExpr(_) => Some(Self::Continue),
+ ast::LoopExpr(_) => Some(Self::Continue),
+ ast::ForExpr(_) => Some(Self::Continue),
+ _ => None
+ }
+ }
+ }
+
+ fn make_early_expr(
+ &self,
+ sema: &Semantics<'_, RootDatabase>,
+ make: &SyntaxFactory,
+ ret: Option<ast::Expr>,
+ ) -> ast::Expr {
+ match self {
+ EarlyKind::Continue => make.expr_continue(None).into(),
+ EarlyKind::Break(label, _) => make.expr_break(Some(label.clone()), ret).into(),
+ EarlyKind::Return(ty) => {
+ let expr = match TryEnum::from_ty(sema, ty) {
+ Some(TryEnum::Option) => {
+ ret.or_else(|| Some(make.expr_path(make.ident_path("None"))))
+ }
+ _ => ret,
+ };
+ make.expr_return(expr).into()
+ }
+ }
+ }
+
+ fn is_unit(&self) -> bool {
+ match self {
+ EarlyKind::Continue => true,
+ EarlyKind::Break(_, ty) => ty.is_unit(),
+ EarlyKind::Return(ty) => ty.is_unit(),
+ }
+ }
}
fn flat_let_chain(mut expr: ast::Expr, make: &SyntaxFactory) -> Vec<ast::Expr> {
@@ -465,6 +551,74 @@ fn main() {
}
#[test]
+ fn convert_if_let_has_else_block() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() -> i32 {
+ if$0 true {
+ foo();
+ } else {
+ bar()
+ }
+}
+"#,
+ r#"
+fn main() -> i32 {
+ if false {
+ return bar();
+ }
+ foo();
+}
+"#,
+ );
+
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ if$0 true {
+ foo();
+ } else {
+ bar()
+ }
+}
+"#,
+ r#"
+fn main() {
+ if false {
+ bar();
+ return
+ }
+ foo();
+}
+"#,
+ );
+
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ if$0 true {
+ foo();
+ } else {
+ bar();
+ }
+}
+"#,
+ r#"
+fn main() {
+ if false {
+ bar();
+ return
+ }
+ foo();
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_if_let_has_never_type_else_block() {
check_assist(
convert_to_guarded_return,
@@ -512,7 +666,7 @@ fn main() {
}
#[test]
- fn convert_if_let_has_else_block_in_statement() {
+ fn convert_if_let_has_never_type_else_block_in_statement() {
check_assist(
convert_to_guarded_return,
r#"
@@ -923,6 +1077,63 @@ fn main() {
}
#[test]
+ fn convert_let_inside_labeled_block() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ 'l: {
+ if$0 let Some(n) = n {
+ foo(n);
+ bar();
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ 'l: {
+ let Some(n) = n else { break 'l };
+ foo(n);
+ bar();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_let_inside_for_with_else() {
+ check_assist(
+ convert_to_guarded_return,
+ r#"
+fn main() {
+ for n in ns {
+ if$0 let Some(n) = n {
+ foo(n);
+ bar();
+ } else {
+ baz()
+ }
+ }
+}
+"#,
+ r#"
+fn main() {
+ for n in ns {
+ let Some(n) = n else {
+ baz();
+ continue
+ };
+ foo(n);
+ bar();
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn convert_let_stmt_inside_fn() {
check_assist(
convert_to_guarded_return,
@@ -1186,16 +1397,18 @@ fn main() {
}
#[test]
- fn ignore_else_branch() {
+ fn ignore_else_branch_has_non_never_types_in_statement() {
check_assist_not_applicable(
convert_to_guarded_return,
r#"
fn main() {
+ some_statements();
if$0 true {
foo();
} else {
bar()
}
+ some_statements();
}
"#,
);
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 1740cd024a..0af0cbc32a 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
@@ -72,15 +72,15 @@ pub(crate) fn convert_tuple_return_type_to_struct(
"Convert tuple return type to tuple struct",
target,
move |edit| {
- let mut syntax_editor = edit.make_editor(ret_type.syntax());
- let syntax_factory = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(ret_type.syntax());
+ let make = editor.make();
let usages = Definition::Function(fn_def).usages(&ctx.sema).all();
let struct_name = format!("{}Result", stdx::to_camel_case(&fn_name.to_string()));
let parent = fn_.syntax().ancestors().find_map(<Either<ast::Impl, ast::Trait>>::cast);
add_tuple_struct_def(
edit,
- &syntax_factory,
+ make,
ctx,
&usages,
parent.as_ref().map(|it| it.syntax()).unwrap_or(fn_.syntax()),
@@ -89,22 +89,12 @@ pub(crate) fn convert_tuple_return_type_to_struct(
&target_module,
);
- syntax_editor.replace(
- ret_type.syntax(),
- syntax_factory.ret_type(syntax_factory.ty(&struct_name)).syntax(),
- );
+ editor.replace(ret_type.syntax(), make.ret_type(make.ty(&struct_name)).syntax());
if let Some(fn_body) = fn_.body() {
- replace_body_return_values(
- &mut syntax_editor,
- &syntax_factory,
- ast::Expr::BlockExpr(fn_body),
- &struct_name,
- );
+ replace_body_return_values(&editor, ast::Expr::BlockExpr(fn_body), &struct_name);
}
-
- syntax_editor.add_mappings(syntax_factory.finish_with_mappings());
- edit.add_file_edits(ctx.vfs_file_id(), syntax_editor);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
replace_usages(edit, ctx, &usages, &struct_name, &target_module);
},
@@ -122,35 +112,22 @@ fn replace_usages(
for (file_id, references) in usages.iter() {
let Some(first_ref) = references.first() else { continue };
- let mut editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap());
- let syntax_factory = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(first_ref.name.syntax().as_node().unwrap());
+ let make = editor.make();
- let refs_with_imports = augment_references_with_imports(
- &syntax_factory,
- ctx,
- references,
- struct_name,
- target_module,
- );
+ let refs_with_imports =
+ augment_references_with_imports(make, ctx, references, struct_name, target_module);
refs_with_imports.into_iter().rev().for_each(|(name, import_data)| {
if let Some(fn_) = name.syntax().parent().and_then(ast::Fn::cast) {
cov_mark::hit!(replace_trait_impl_fns);
if let Some(ret_type) = fn_.ret_type() {
- editor.replace(
- ret_type.syntax(),
- syntax_factory.ret_type(syntax_factory.ty(struct_name)).syntax(),
- );
+ editor.replace(ret_type.syntax(), make.ret_type(make.ty(struct_name)).syntax());
}
if let Some(fn_body) = fn_.body() {
- replace_body_return_values(
- &mut editor,
- &syntax_factory,
- ast::Expr::BlockExpr(fn_body),
- struct_name,
- );
+ replace_body_return_values(&editor, ast::Expr::BlockExpr(fn_body), struct_name);
}
} else {
// replace tuple patterns
@@ -172,27 +149,15 @@ fn replace_usages(
for tuple_pat in tuple_pats {
editor.replace(
tuple_pat.syntax(),
- syntax_factory
- .tuple_struct_pat(
- syntax_factory.path_from_text(struct_name),
- tuple_pat.fields(),
- )
+ make.tuple_struct_pat(make.path_from_text(struct_name), tuple_pat.fields())
.syntax(),
);
}
}
if let Some((import_scope, path)) = import_data {
- insert_use_with_editor(
- &import_scope,
- path,
- &ctx.config.insert_use,
- &mut editor,
- &syntax_factory,
- );
+ insert_use_with_editor(&import_scope, path, &ctx.config.insert_use, &editor);
}
});
-
- editor.add_mappings(syntax_factory.finish_with_mappings());
edit.add_file_edits(file_id.file_id(ctx.db()), editor);
}
}
@@ -296,12 +261,8 @@ fn add_tuple_struct_def(
}
/// Replaces each returned tuple in `body` with the constructor of the tuple struct named `struct_name`.
-fn replace_body_return_values(
- syntax_editor: &mut SyntaxEditor,
- syntax_factory: &SyntaxFactory,
- body: ast::Expr,
- struct_name: &str,
-) {
+fn replace_body_return_values(editor: &SyntaxEditor, body: ast::Expr, struct_name: &str) {
+ let make = editor.make();
let mut exprs_to_wrap = Vec::new();
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_wrap, e);
@@ -316,11 +277,11 @@ fn replace_body_return_values(
for ret_expr in exprs_to_wrap {
if let ast::Expr::TupleExpr(tuple_expr) = &ret_expr {
- let struct_constructor = syntax_factory.expr_call(
- syntax_factory.expr_path(syntax_factory.ident_path(struct_name)),
- syntax_factory.arg_list(tuple_expr.fields()),
+ let struct_constructor = make.expr_call(
+ make.expr_path(make.ident_path(struct_name)),
+ make.arg_list(tuple_expr.fields()),
);
- syntax_editor.replace(ret_expr.syntax(), struct_constructor.syntax());
+ editor.replace(ret_expr.syntax(), struct_constructor.syntax());
}
}
}
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 ae41e6c015..afbcf024b9 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
@@ -86,32 +86,32 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
"Convert to named struct",
target,
|edit| {
- let names = generate_names(tuple_fields.fields());
+ let editor = edit.make_editor(syntax);
+ let names = generate_names(tuple_fields.fields(), editor.make());
edit_field_references(ctx, edit, tuple_fields.fields(), &names);
- let mut editor = edit.make_editor(syntax);
edit_struct_references(ctx, edit, strukt_def, &names);
- edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names);
+ edit_struct_def(&editor, &strukt_or_variant, tuple_fields, names);
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
fn edit_struct_def(
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
strukt: &Either<ast::Struct, ast::Variant>,
tuple_fields: ast::TupleFieldList,
names: Vec<ast::Name>,
) {
+ let make = editor.make();
let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| {
- let field = ast::make::record_field(f.visibility(), name, f.ty()?);
- let mut field_editor = SyntaxEditor::new(field.syntax().clone());
+ let (field_editor, field) =
+ SyntaxEditor::with_ast_node(&make.record_field(f.visibility(), name, f.ty()?));
field_editor.insert_all(
Position::first_child_of(field.syntax()),
- f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
+ f.attrs().map(|attr| attr.syntax().clone().into()).collect(),
);
ast::RecordField::cast(field_editor.finish().new_root().clone())
});
- let make = SyntaxFactory::without_mappings();
let record_fields = make.record_field_list(record_fields);
let tuple_fields_before = Position::before(tuple_fields.syntax());
@@ -119,21 +119,21 @@ fn edit_struct_def(
if let Some(w) = strukt.where_clause() {
editor.delete(w.syntax());
let mut insert_element = Vec::new();
- insert_element.push(ast::make::tokens::single_newline().syntax_element());
- insert_element.push(w.syntax().clone_for_update().syntax_element());
+ insert_element.push(make.whitespace("\n").syntax_element());
+ insert_element.push(w.syntax().syntax_element());
if w.syntax().last_token().is_none_or(|t| t.kind() != SyntaxKind::COMMA) {
- insert_element.push(ast::make::token(T![,]).into());
+ insert_element.push(make.token(T![,]).into());
}
- insert_element.push(ast::make::tokens::single_newline().syntax_element());
+ insert_element.push(make.whitespace("\n").syntax_element());
editor.insert_all(tuple_fields_before, insert_element);
} else {
- editor.insert(tuple_fields_before, ast::make::tokens::single_space());
+ editor.insert(tuple_fields_before, make.whitespace(" "));
}
if let Some(t) = strukt.semicolon_token() {
editor.delete(t);
}
} else {
- editor.insert(tuple_fields_before, ast::make::tokens::single_space());
+ editor.insert(tuple_fields_before, make.whitespace(" "));
}
editor.replace(tuple_fields.syntax(), record_fields.syntax());
@@ -153,10 +153,10 @@ fn edit_struct_references(
for (file_id, refs) in usages {
let source = ctx.sema.parse(file_id);
- let mut editor = edit.make_editor(source.syntax());
+ let editor = edit.make_editor(source.syntax());
for r in refs {
- process_struct_name_reference(ctx, r, &mut editor, &source, &strukt_def, names);
+ process_struct_name_reference(ctx, r, &editor, &source, &strukt_def, names);
}
edit.add_file_edits(file_id.file_id(ctx.db()), editor);
@@ -166,12 +166,12 @@ fn edit_struct_references(
fn process_struct_name_reference(
ctx: &AssistContext<'_>,
r: FileReference,
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
source: &ast::SourceFile,
strukt_def: &Definition,
names: &[ast::Name],
) -> Option<()> {
- let make = SyntaxFactory::without_mappings();
+ let make = editor.make();
let name_ref = r.name.as_name_ref()?;
let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
let full_path = path_segment.syntax().parent().and_then(ast::Path::cast)?.top_path();
@@ -189,7 +189,7 @@ fn process_struct_name_reference(
let range = ctx.sema.original_range_opt(tuple_struct_pat.syntax())?.range;
let new = make.record_pat_with_fields(
full_path,
- generate_record_pat_list(&tuple_struct_pat, names),
+ generate_record_pat_list(&tuple_struct_pat, names, make),
);
editor.replace_all(cover_edit_range(source.syntax(), range), vec![new.syntax().clone().into()]);
},
@@ -231,10 +231,11 @@ fn process_struct_name_reference(
fn process_delimiter(
ctx: &AssistContext<'_>,
source: &ast::SourceFile,
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
list: &impl AstNode,
first_insert: Vec<syntax::SyntaxElement>,
) {
+ let make = editor.make();
let Some(range) = ctx.sema.original_range_opt(list.syntax()) else { return };
let place = cover_edit_range(source.syntax(), range.range);
@@ -247,7 +248,6 @@ fn process_delimiter(
syntax::NodeOrToken::Token(t) => Some(t.clone()),
};
- let make = SyntaxFactory::without_mappings();
if let Some(l_paren) = l_paren
&& l_paren.kind() == T!['(']
{
@@ -284,7 +284,7 @@ fn edit_field_references(
let usages = def.usages(&ctx.sema).all();
for (file_id, refs) in usages {
let source = ctx.sema.parse(file_id);
- let mut editor = edit.make_editor(source.syntax());
+ let editor = edit.make_editor(source.syntax());
for r in refs {
if let Some(name_ref) = r.name.as_name_ref()
&& let Some(original) = ctx.sema.original_range_opt(name_ref.syntax())
@@ -300,8 +300,10 @@ fn edit_field_references(
}
}
-fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> {
- let make = SyntaxFactory::without_mappings();
+fn generate_names(
+ fields: impl Iterator<Item = ast::TupleField>,
+ make: &SyntaxFactory,
+) -> Vec<ast::Name> {
fields
.enumerate()
.map(|(i, _)| {
@@ -314,6 +316,7 @@ fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Nam
fn generate_record_pat_list(
pat: &ast::TupleStructPat,
names: &[ast::Name],
+ make: &SyntaxFactory,
) -> ast::RecordPatFieldList {
let pure_fields = pat.fields().filter(|p| !matches!(p, ast::Pat::RestPat(_)));
let rest_len = names.len().saturating_sub(pure_fields.clone().count());
@@ -325,8 +328,8 @@ fn generate_record_pat_list(
let fields = before_rest
.chain(after_rest)
- .map(|(pat, name)| ast::make::record_pat_field(ast::make::name_ref(&name.text()), pat));
- ast::make::record_pat_field_list(fields, rest_pat)
+ .map(|(pat, name)| make.record_pat_field(make.name_ref(&name.text()), pat));
+ make.record_pat_field_list(fields, rest_pat)
}
#[cfg(test)]
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 f8215d6723..793e7465c1 100644
--- a/crates/ide-assists/src/handlers/convert_while_to_loop.rs
+++ b/crates/ide-assists/src/handlers/convert_while_to_loop.rs
@@ -6,7 +6,6 @@ use syntax::{
ast::{
self, HasLoopBody,
edit::{AstNodeEdit, IndentLevel},
- syntax_factory::SyntaxFactory,
},
syntax_editor::{Element, Position},
};
@@ -52,8 +51,8 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
"Convert while to loop",
target,
|builder| {
- let make = SyntaxFactory::without_mappings();
- let mut edit = builder.make_editor(while_expr.syntax());
+ let editor = builder.make_editor(while_expr.syntax());
+ let make = editor.make();
let while_indent_level = IndentLevel::from_node(while_expr.syntax());
let break_block = make
@@ -63,7 +62,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
)
.indent(IndentLevel(1));
- edit.replace_all(
+ editor.replace_all(
while_kw.syntax_element()..=while_cond.syntax().syntax_element(),
vec![make.token(T![loop]).syntax_element()],
);
@@ -73,17 +72,17 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
let if_expr = make.expr_if(while_cond, then_branch, Some(break_block.into()));
let stmts = iter::once(make.expr_stmt(if_expr.into()).into());
let block_expr = make.block_expr(stmts, None);
- edit.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax());
+ editor.replace(while_body.syntax(), block_expr.indent(while_indent_level).syntax());
} else {
- let if_cond = invert_boolean_expression(&make, while_cond);
+ let if_cond = invert_boolean_expression(make, while_cond);
let if_expr = make.expr_if(if_cond, break_block, None).indent(while_indent_level);
if !while_body.syntax().text().contains_char('\n') {
- edit.insert(
+ editor.insert(
Position::after(&l_curly),
make.whitespace(&format!("\n{while_indent_level}")),
);
}
- edit.insert_all(
+ editor.insert_all(
Position::after(&l_curly),
vec![
make.whitespace(&format!("\n{}", while_indent_level + 1)).into(),
@@ -91,9 +90,7 @@ pub(crate) fn convert_while_to_loop(acc: &mut Assists, ctx: &AssistContext<'_>)
],
);
};
-
- edit.add_mappings(make.finish_with_mappings());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/destructure_struct_binding.rs b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
index ec4a83b642..9ffce445d1 100644
--- a/crates/ide-assists/src/handlers/destructure_struct_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_struct_binding.rs
@@ -124,9 +124,9 @@ fn destructure_struct_binding_impl(
data: &StructEditData,
) {
let field_names = generate_field_names(ctx, data);
- let mut editor = builder.make_editor(data.target.syntax());
- destructure_pat(ctx, &mut editor, data, &field_names);
- update_usages(ctx, &mut editor, data, &field_names.into_iter().collect());
+ let editor = builder.make_editor(data.target.syntax());
+ destructure_pat(ctx, &editor, data, &field_names);
+ update_usages(ctx, &editor, data, &field_names.into_iter().collect());
builder.add_file_edits(ctx.vfs_file_id(), editor);
}
@@ -145,12 +145,8 @@ struct StructEditData {
}
impl StructEditData {
- fn apply_to_destruct(
- &self,
- new_pat: ast::Pat,
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
- ) {
+ fn apply_to_destruct(&self, new_pat: ast::Pat, editor: &SyntaxEditor) {
+ let make = editor.make();
match &self.target {
Target::IdentPat(pat) => {
// If the binding is nested inside a record, we need to wrap the new
@@ -275,15 +271,15 @@ fn get_names_in_scope(
fn destructure_pat(
_ctx: &AssistContext<'_>,
- editor: &mut SyntaxEditor,
+ 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 is_ref = data.target.is_ref();
let is_mut = data.target.is_mut();
- let make = SyntaxFactory::with_mappings();
let new_pat = match data.kind {
hir::StructKind::Tuple => {
let ident_pats = field_names.iter().map(|(_, new_name)| {
@@ -314,8 +310,7 @@ fn destructure_pat(
hir::StructKind::Unit => make.path_pat(struct_path),
};
- data.apply_to_destruct(new_pat, editor, &make);
- editor.add_mappings(make.finish_with_mappings());
+ data.apply_to_destruct(new_pat, editor);
}
fn generate_field_names(ctx: &AssistContext<'_>, data: &StructEditData) -> Vec<(SmolStr, SmolStr)> {
@@ -354,18 +349,16 @@ fn new_field_name(base_name: SmolStr, names_in_scope: &FxHashSet<SmolStr>) -> Sm
fn update_usages(
ctx: &AssistContext<'_>,
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
data: &StructEditData,
field_names: &FxHashMap<SmolStr, SmolStr>,
) {
let source = ctx.source_file().syntax();
- let make = SyntaxFactory::with_mappings();
let edits = data
.usages
.iter()
- .filter_map(|r| build_usage_edit(ctx, &make, data, r, field_names))
+ .filter_map(|r| build_usage_edit(ctx, editor.make(), data, r, field_names))
.collect_vec();
- editor.add_mappings(make.finish_with_mappings());
for (old, new) in edits {
if let Some(range) = ctx.sema.original_range_opt(&old) {
editor.replace_all(cover_edit_range(source, range.range), vec![new.into()]);
diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
index 23c11b258c..291605056b 100644
--- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
+++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs
@@ -89,22 +89,17 @@ fn destructure_tuple_edit_impl(
data: &TupleData,
in_sub_pattern: bool,
) {
- let mut syntax_editor = edit.make_editor(data.ident_pat.syntax());
- let syntax_factory = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(data.ident_pat.syntax());
+ let make = editor.make();
- let assignment_edit =
- edit_tuple_assignment(ctx, edit, &mut syntax_editor, &syntax_factory, data, in_sub_pattern);
- let current_file_usages_edit = edit_tuple_usages(data, ctx, &syntax_factory, in_sub_pattern);
+ let assignment_edit = edit_tuple_assignment(ctx, edit, &editor, data, in_sub_pattern);
+ let current_file_usages_edit = edit_tuple_usages(data, ctx, make, in_sub_pattern);
- assignment_edit.apply(&mut syntax_editor, &syntax_factory);
+ assignment_edit.apply(&editor);
if let Some(usages_edit) = current_file_usages_edit {
- usages_edit
- .into_iter()
- .for_each(|usage_edit| usage_edit.apply(ctx, edit, &mut syntax_editor))
+ usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(ctx, edit, &editor))
}
-
- syntax_editor.add_mappings(syntax_factory.finish_with_mappings());
- edit.add_file_edits(ctx.vfs_file_id(), syntax_editor);
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
}
fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> {
@@ -175,11 +170,11 @@ struct TupleData {
fn edit_tuple_assignment(
ctx: &AssistContext<'_>,
edit: &mut SourceChangeBuilder,
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
data: &TupleData,
in_sub_pattern: bool,
) -> AssignmentEdit {
+ let make = editor.make();
let tuple_pat = {
let original = &data.ident_pat;
let is_ref = original.ref_token().is_some();
@@ -223,22 +218,17 @@ struct AssignmentEdit {
}
impl AssignmentEdit {
- fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) {
+ fn apply(self, editor: &SyntaxEditor) {
+ let make = editor.make();
// with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
if self.in_sub_pattern {
- self.ident_pat.set_pat_with_editor(
- Some(self.tuple_pat.into()),
- syntax_editor,
- syntax_mapping,
- )
+ self.ident_pat.set_pat(Some(self.tuple_pat.into()), editor);
} else if self.is_shorthand_field {
- syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax());
- syntax_editor
- .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.whitespace(" "));
- syntax_editor
- .insert(Position::after(self.ident_pat.syntax()), syntax_mapping.token(T![:]));
+ editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax());
+ editor.insert(Position::after(self.ident_pat.syntax()), make.whitespace(" "));
+ editor.insert(Position::after(self.ident_pat.syntax()), make.token(T![:]));
} else {
- syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax())
+ editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax())
}
}
}
@@ -317,7 +307,7 @@ impl EditTupleUsage {
self,
ctx: &AssistContext<'_>,
edit: &mut SourceChangeBuilder,
- syntax_editor: &mut SyntaxEditor,
+ syntax_editor: &SyntaxEditor,
) {
match self {
EditTupleUsage::NoIndex(range) => {
@@ -907,6 +897,7 @@ fn main() {
check_assist(
assist,
r#"
+//- minicore: fn
fn main() {
let f = |$0t| t.0 + t.1;
let v = f((1,2));
@@ -1111,6 +1102,7 @@ fn main() {
check_assist(
assist,
r#"
+//- minicore: fn
fn main() {
let $0t = (1,2);
let v = t.1;
diff --git a/crates/ide-assists/src/handlers/desugar_try_expr.rs b/crates/ide-assists/src/handlers/desugar_try_expr.rs
index 865dc86221..fc894f0fe9 100644
--- a/crates/ide-assists/src/handlers/desugar_try_expr.rs
+++ b/crates/ide-assists/src/handlers/desugar_try_expr.rs
@@ -65,9 +65,8 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
"Replace try expression with match",
target,
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(try_expr.syntax());
-
+ let editor = builder.make_editor(try_expr.syntax());
+ let make = editor.make();
let sad_pat = match try_enum {
TryEnum::Option => make.path_pat(make.ident_path("None")),
TryEnum::Result => make
@@ -77,7 +76,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
)
.into(),
};
- let sad_expr = make.expr_return(Some(sad_expr(try_enum, &make, || {
+ let sad_expr = make.expr_return(Some(sad_expr(try_enum, make, || {
make.expr_path(make.ident_path("err"))
})));
@@ -95,7 +94,6 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.indent(IndentLevel::from_node(try_expr.syntax()));
editor.replace(try_expr.syntax(), expr_match.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -109,8 +107,8 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
"Replace try expression with let else",
target,
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(let_stmt.syntax());
+ let editor = builder.make_editor(let_stmt.syntax());
+ let make = editor.make();
let indent_level = IndentLevel::from_node(let_stmt.syntax());
let fill_expr = || crate::utils::expr_fill_default(ctx.config);
@@ -124,7 +122,7 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
make.block_expr(
iter::once(
make.expr_stmt(
- make.expr_return(Some(sad_expr(try_enum, &make, fill_expr))).into(),
+ make.expr_return(Some(sad_expr(try_enum, make, fill_expr))).into(),
)
.into(),
),
@@ -133,7 +131,6 @@ pub(crate) fn desugar_try_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
.indent(indent_level),
);
editor.replace(let_stmt.syntax(), new_let_stmt.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs
index 6c5c21bfc9..1c8cbf5af5 100644
--- a/crates/ide-assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide-assists/src/handlers/expand_glob_import.rs
@@ -8,7 +8,7 @@ use ide_db::{
use stdx::never;
use syntax::{
AstNode, Direction, SyntaxNode, SyntaxToken, T,
- ast::{self, Use, UseTree, VisibilityKind, make},
+ ast::{self, Use, UseTree, VisibilityKind},
};
use crate::{
@@ -148,6 +148,8 @@ fn build_expanded_import(
current_module: Module,
reexport_public_items: bool,
) {
+ let editor = builder.make_editor(use_tree.syntax());
+ let make = editor.make();
let (must_be_pub, visible_from) = if !reexport_public_items {
(false, current_module)
} else {
@@ -167,15 +169,13 @@ fn build_expanded_import(
if reexport_public_items { refs_in_target } else { refs_in_target.used_refs(ctx) };
let names_to_import = find_names_to_import(filtered_defs, imported_defs);
- let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
- let path = make::ext::ident_path(
+ let expanded = make.use_tree_list(names_to_import.iter().map(|n| {
+ let path = make.ident_path(
&n.display(ctx.db(), current_module.krate(ctx.db()).edition(ctx.db())).to_string(),
);
- make::use_tree(path, None, None, false)
- }))
- .clone_for_update();
+ make.use_tree(path, None, None, false)
+ }));
- let mut editor = builder.make_editor(use_tree.syntax());
match use_tree.star_token() {
Some(star) => {
let needs_braces = use_tree.path().is_some() && names_to_import.len() != 1;
diff --git a/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index a7e78dfc9c..dc4976e8c2 100644
--- a/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -51,8 +51,8 @@ fn expand_record_rest_pattern(
"Fill struct fields",
rest_pat.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(rest_pat.syntax());
+ let editor = builder.make_editor(rest_pat.syntax());
+ let make = editor.make();
let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| {
make.record_pat_field_shorthand(
make.ident_pat(
@@ -66,8 +66,6 @@ fn expand_record_rest_pattern(
let new_field_list = make.record_pat_field_list(new_fields, None);
editor.replace(old_field_list.syntax(), new_field_list.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -130,8 +128,8 @@ fn expand_tuple_struct_rest_pattern(
"Fill tuple struct fields",
rest_pat.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(rest_pat.syntax());
+ let editor = builder.make_editor(rest_pat.syntax());
+ let make = editor.make();
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
let new_pat = make.tuple_struct_pat(
@@ -141,7 +139,7 @@ fn expand_tuple_struct_rest_pattern(
.chain(fields[prefix_count..fields.len() - suffix_count].iter().map(|f| {
gen_unnamed_pat(
ctx,
- &make,
+ make,
&mut name_gen,
&f.ty(ctx.db()).to_type(ctx.sema.db),
f.index(),
@@ -151,8 +149,6 @@ fn expand_tuple_struct_rest_pattern(
);
editor.replace(pat.syntax(), new_pat.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -200,24 +196,21 @@ fn expand_tuple_rest_pattern(
"Fill tuple fields",
rest_pat.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(rest_pat.syntax());
-
+ let editor = builder.make_editor(rest_pat.syntax());
+ let make = editor.make();
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
let new_pat = make.tuple_pat(
pat.fields()
.take(prefix_count)
.chain(fields[prefix_count..len - suffix_count].iter().enumerate().map(
|(index, ty)| {
- gen_unnamed_pat(ctx, &make, &mut name_gen, ty, prefix_count + index)
+ gen_unnamed_pat(ctx, make, &mut name_gen, ty, prefix_count + index)
},
))
.chain(pat.fields().skip(prefix_count + 1)),
);
editor.replace(pat.syntax(), new_pat.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -264,8 +257,8 @@ fn expand_slice_rest_pattern(
"Fill slice fields",
rest_pat.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(rest_pat.syntax());
+ let editor = builder.make_editor(rest_pat.syntax());
+ let make = editor.make();
let mut name_gen = NameGenerator::new_from_scope_locals(ctx.sema.scope(pat.syntax()));
let new_pat = make.slice_pat(
@@ -273,14 +266,12 @@ fn expand_slice_rest_pattern(
.take(prefix_count)
.chain(
(prefix_count..len - suffix_count)
- .map(|index| gen_unnamed_pat(ctx, &make, &mut name_gen, &ty, index)),
+ .map(|index| gen_unnamed_pat(ctx, make, &mut name_gen, &ty, index)),
)
.chain(pat.pats().skip(prefix_count + 1)),
);
editor.replace(pat.syntax(), new_pat.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
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 35e8baa18a..c87ded9dc4 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
@@ -8,7 +8,7 @@ use syntax::{
AstNode, AstToken, NodeOrToken,
SyntaxKind::WHITESPACE,
SyntaxToken, T,
- ast::{self, TokenTree, syntax_factory::SyntaxFactory},
+ ast::{self, TokenTree},
};
// Assist: extract_expressions_from_format_string
@@ -57,7 +57,8 @@ pub(crate) fn extract_expressions_from_format_string(
"Extract format expressions",
tt.syntax().text_range(),
|edit| {
- let make = SyntaxFactory::without_mappings();
+ let editor = edit.make_editor(tt.syntax());
+ let make = editor.make();
// Extract existing arguments in macro
let mut raw_tokens = tt.token_trees_and_tokens().skip(1).collect_vec();
let format_string_index = format_str_index(&raw_tokens, &fmt_string);
@@ -110,7 +111,7 @@ pub(crate) fn extract_expressions_from_format_string(
Arg::Expr(s) => {
// insert arg
let expr = ast::Expr::parse(&s, ctx.edition()).syntax_node();
- let mut expr_tt = utils::tt_from_syntax(expr, &make);
+ let mut expr_tt = utils::tt_from_syntax(expr, make);
new_tt_bits.append(&mut expr_tt);
}
Arg::Placeholder => {
@@ -131,7 +132,6 @@ pub(crate) fn extract_expressions_from_format_string(
// Insert new args
let new_tt = make.token_tree(tt_delimiter, new_tt_bits);
- let mut editor = edit.make_editor(tt.syntax());
editor.replace(tt.syntax(), new_tt.syntax());
if let Some(cap) = ctx.config.snippet_cap {
@@ -158,7 +158,6 @@ pub(crate) fn extract_expressions_from_format_string(
editor.add_annotation(literal, annotation);
}
}
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
);
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index 124ef509fb..4219e6845f 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -92,11 +92,13 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let anchor = if self_param.is_some() { Anchor::Method } else { Anchor::Freestanding };
let insert_after = node_to_insert_after(&body, anchor)?;
+ let trait_name = ast::Trait::cast(insert_after.clone()).and_then(|trait_| trait_.name());
let semantics_scope = ctx.sema.scope(&insert_after)?;
let module = semantics_scope.module();
let edition = semantics_scope.krate().edition(ctx.db());
- let (container_info, contains_tail_expr) = body.analyze_container(&ctx.sema, edition)?;
+ let (container_info, contains_tail_expr) =
+ body.analyze_container(&ctx.sema, edition, trait_name)?;
let ret_ty = body.return_ty(ctx)?;
let control_flow = body.external_control_flow(ctx, &container_info)?;
@@ -181,6 +183,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
builder.add_tabstop_before(cap, name);
}
+ // 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());
@@ -377,6 +380,7 @@ struct ControlFlow<'db> {
struct ContainerInfo<'db> {
is_const: bool,
parent_loop: Option<SyntaxNode>,
+ trait_name: Option<ast::Type>,
/// The function's return type, const's type etc.
ret_type: Option<hir::Type<'db>>,
generic_param_lists: Vec<ast::GenericParamList>,
@@ -838,6 +842,7 @@ impl FunctionBody {
&self,
sema: &Semantics<'db, RootDatabase>,
edition: Edition,
+ trait_name: Option<ast::Name>,
) -> Option<(ContainerInfo<'db>, bool)> {
let mut ancestors = self.parent()?.ancestors();
let infer_expr_opt = |expr| sema.type_of_expr(&expr?).map(TypeInfo::adjusted);
@@ -924,6 +929,9 @@ impl FunctionBody {
false
};
+ // FIXME: make trait arguments
+ let trait_name = trait_name.map(|name| make::ty_path(make::ext::ident_path(&name.text())));
+
let parent = self.parent()?;
let parents = generic_parents(&parent);
let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect();
@@ -934,6 +942,7 @@ impl FunctionBody {
ContainerInfo {
is_const,
parent_loop,
+ trait_name,
ret_type: ty,
generic_param_lists,
where_clauses,
@@ -1353,26 +1362,26 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option<SyntaxNod
while let Some(next_ancestor) = ancestors.next() {
match next_ancestor.kind() {
SyntaxKind::SOURCE_FILE => break,
- SyntaxKind::IMPL => {
- if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) {
- let impl_node = find_non_trait_impl(&next_ancestor);
- if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) {
- return target_node;
- }
+ SyntaxKind::IMPL
+ if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) =>
+ {
+ let impl_node = find_non_trait_impl(&next_ancestor);
+ if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) {
+ return target_node;
}
}
SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue,
- SyntaxKind::ITEM_LIST => {
- if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) {
- break;
- }
+ SyntaxKind::ITEM_LIST
+ if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) =>
+ {
+ break;
}
SyntaxKind::ASSOC_ITEM_LIST if !matches!(anchor, Anchor::Method) => continue,
SyntaxKind::ASSOC_ITEM_LIST if body.extracted_from_trait_impl() => continue,
- SyntaxKind::ASSOC_ITEM_LIST => {
- if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) {
- break;
- }
+ SyntaxKind::ASSOC_ITEM_LIST
+ if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::IMPL) =>
+ {
+ break;
}
_ => (),
}
@@ -1419,14 +1428,18 @@ fn fixup_call_site(builder: &mut SourceChangeBuilder, body: &FunctionBody) {
fn make_call(ctx: &AssistContext<'_>, fun: &Function<'_>, indent: IndentLevel) -> SyntaxNode {
let ret_ty = fun.return_type(ctx);
- let args = make::arg_list(fun.params.iter().map(|param| param.to_arg(ctx, fun.mods.edition)));
let name = fun.name.clone();
- let mut call_expr = if fun.self_param.is_some() {
+ let args = fun.params.iter().map(|param| param.to_arg(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()
+ } 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, args).into()
+ 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, args).into()
+ make::expr_call(func, make::arg_list(args)).into()
};
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
@@ -1729,9 +1742,28 @@ impl<'db> Function<'db> {
module: hir::Module,
edition: Edition,
) -> ast::ParamList {
- let self_param = self.self_param.clone();
+ let this_param = self.make_this_param().map(|f| f());
+ 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, params)
+ make::param_list(self_param, this_param.into_iter().chain(params))
+ }
+
+ fn make_this_param(&self) -> Option<impl FnOnce() -> 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();
+ if self_param.amp_token().is_some() {
+ ty = make::ty_ref(ty, self_param.mut_token().is_some());
+ }
+ make::param(pat, ty)
+ })
+ } else {
+ None
+ }
}
fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
@@ -1806,10 +1838,12 @@ fn make_body(
) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx);
let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
+ let to_this_param = fun.self_param.clone().filter(|_| fun.make_this_param().is_some());
let block = match &fun.body {
FunctionBody::Expr(expr) => {
- let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
+ let expr =
+ rewrite_body_segment(ctx, to_this_param, &fun.params, &handler, expr.syntax());
let expr = ast::Expr::cast(expr).expect("Body segment should be an expr");
match expr {
ast::Expr::BlockExpr(block) => {
@@ -1847,7 +1881,7 @@ fn make_body(
.filter(|it| text_range.contains_range(it.text_range()))
.map(|it| match &it {
syntax::NodeOrToken::Node(n) => syntax::NodeOrToken::Node(
- rewrite_body_segment(ctx, &fun.params, &handler, n),
+ rewrite_body_segment(ctx, to_this_param.clone(), &fun.params, &handler, n),
),
_ => it,
})
@@ -1997,11 +2031,13 @@ fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) ->
fn rewrite_body_segment(
ctx: &AssistContext<'_>,
+ to_this_param: Option<ast::SelfParam>,
params: &[Param<'_>],
handler: &FlowHandler<'_>,
syntax: &SyntaxNode,
) -> SyntaxNode {
- let syntax = fix_param_usages(ctx, params, syntax);
+ 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
}
@@ -2009,34 +2045,54 @@ fn rewrite_body_segment(
/// change all usages to account for added `&`/`&mut` for some params
fn fix_param_usages(
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 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))
+ };
+ if let Some(self_param) = to_this_param {
+ usages_for_self_param = LocalUsages::find_local_usages(ctx, self_param)
+ .iter()
+ .filter_map(reference_filter)
+ .collect();
+ }
for param in params {
if !param.kind().is_ref() {
continue;
}
let usages = LocalUsages::find_local_usages(ctx, param.var);
- let usages = usages
- .iter()
- .filter(|reference| syntax.text_range().contains_range(reference.range))
- .filter_map(|reference| path_element_of_reference(syntax, reference))
- .map(|expr| tm.make_mut(&expr));
+ let usages = usages.iter().filter_map(reference_filter);
usages_for_param.push((param, usages.unique().collect()));
}
let res = tm.make_syntax_mut(syntax);
+ 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());
+ }
for (param, usages) in usages_for_param {
for usage in usages {
match usage.syntax().ancestors().skip(1).find_map(ast::Expr::cast) {
- Some(ast::Expr::MethodCallExpr(_) | ast::Expr::FieldExpr(_)) => {
+ Some(
+ ast::Expr::MethodCallExpr(_)
+ | ast::Expr::FieldExpr(_)
+ | ast::Expr::IndexExpr(_),
+ ) => {
// do nothing
}
Some(ast::Expr::RefExpr(node))
@@ -2072,19 +2128,19 @@ fn update_external_control_flow(handler: &FlowHandler<'_>, syntax: &SyntaxNode)
for event in syntax.preorder() {
match event {
WalkEvent::Enter(e) => match e.kind() {
- SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR => {
- if nested_loop.is_none() {
- nested_loop = Some(e.clone());
- }
+ SyntaxKind::LOOP_EXPR | SyntaxKind::WHILE_EXPR | SyntaxKind::FOR_EXPR
+ if nested_loop.is_none() =>
+ {
+ nested_loop = Some(e.clone());
}
SyntaxKind::FN
| SyntaxKind::CONST
| SyntaxKind::STATIC
| SyntaxKind::IMPL
- | SyntaxKind::MODULE => {
- if nested_scope.is_none() {
- nested_scope = Some(e.clone());
- }
+ | SyntaxKind::MODULE
+ if nested_scope.is_none() =>
+ {
+ nested_scope = Some(e.clone());
}
_ => {}
},
@@ -2940,6 +2996,35 @@ impl S {
}
#[test]
+ fn method_in_trait() {
+ check_assist(
+ extract_function,
+ r#"
+trait Foo {
+ fn f(&self) -> i32;
+
+ fn foo(&self) -> i32 {
+ $0self.f()+self.f()$0
+ }
+}
+"#,
+ r#"
+trait Foo {
+ fn f(&self) -> i32;
+
+ fn foo(&self) -> i32 {
+ fun_name(self)
+ }
+}
+
+fn $0fun_name(this: &impl Foo) -> i32 {
+ this.f()+this.f()
+}
+"#,
+ );
+ }
+
+ #[test]
fn variable_defined_inside_and_used_after_no_ret() {
check_assist(
extract_function,
@@ -3131,6 +3216,32 @@ fn $0fun_name(n: &mut i32) {
}
#[test]
+ fn mut_index_from_outer_scope() {
+ check_assist(
+ extract_function,
+ r#"
+//- minicore: index
+fn foo() {
+ let mut arr = [1i32];
+ $0arr[0] = 3;$0
+ let _ = arr;
+}
+"#,
+ r#"
+fn foo() {
+ let mut arr = [1i32];
+ fun_name(&mut arr);
+ let _ = arr;
+}
+
+fn $0fun_name(arr: &mut [i32; 1]) {
+ arr[0] = 3;
+}
+"#,
+ );
+ }
+
+ #[test]
fn mut_field_from_outer_scope() {
check_assist(
extract_function,
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 4c46a51bef..21013e2e61 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
@@ -6,7 +6,7 @@ use ide_db::{
FxHashSet, RootDatabase,
defs::Definition,
helpers::mod_path_to_ast,
- imports::insert_use::{ImportScope, InsertUseConfig, insert_use},
+ imports::insert_use::{ImportScope, InsertUseConfig, insert_use_with_editor},
path_transform::PathTransform,
search::FileReference,
};
@@ -16,12 +16,14 @@ use syntax::{
SyntaxKind::*,
SyntaxNode, T,
ast::{
- self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make,
+ self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit,
+ syntax_factory::SyntaxFactory,
},
- match_ast, ted,
+ match_ast,
+ syntax_editor::{Position, SyntaxEditor},
};
-use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
+use crate::{AssistContext, AssistId, Assists};
// Assist: extract_struct_from_enum_variant
//
@@ -58,6 +60,8 @@ pub(crate) fn extract_struct_from_enum_variant(
"Extract struct from enum variant",
target,
|builder| {
+ let editor = builder.make_editor(variant.syntax());
+ let make = editor.make();
let edition = enum_hir.krate(ctx.db()).edition(ctx.db());
let variant_hir_name = variant_hir.name(ctx.db());
let enum_module_def = ModuleDef::from(enum_hir);
@@ -73,40 +77,46 @@ pub(crate) fn extract_struct_from_enum_variant(
def_file_references = Some(references);
continue;
}
- builder.edit_file(file_id.file_id(ctx.db()));
let processed = process_references(
ctx,
- builder,
&mut visited_modules_set,
&enum_module_def,
&variant_hir_name,
references,
);
+ if processed.is_empty() {
+ continue;
+ }
+ let file_editor = builder.make_editor(processed[0].0.syntax());
processed.into_iter().for_each(|(path, node, import)| {
- apply_references(ctx.config.insert_use, path, node, import, edition)
+ apply_references(
+ ctx.config.insert_use,
+ path,
+ node,
+ import,
+ edition,
+ &file_editor,
+ )
});
+ builder.add_file_edits(file_id.file_id(ctx.db()), file_editor);
}
- builder.edit_file(ctx.vfs_file_id());
- let variant = builder.make_mut(variant.clone());
if let Some(references) = def_file_references {
let processed = process_references(
ctx,
- builder,
&mut visited_modules_set,
&enum_module_def,
&variant_hir_name,
references,
);
processed.into_iter().for_each(|(path, node, import)| {
- apply_references(ctx.config.insert_use, path, node, import, edition)
+ apply_references(ctx.config.insert_use, path, node, import, edition, &editor)
});
}
- let generic_params = enum_ast
- .generic_param_list()
- .and_then(|known_generics| extract_generic_params(&known_generics, &field_list));
- let generics = generic_params.as_ref().map(|generics| generics.clone_for_update());
+ let generic_params = enum_ast.generic_param_list().and_then(|known_generics| {
+ extract_generic_params(make, &known_generics, &field_list)
+ });
// resolve GenericArg in field_list to actual type
let field_list = if let Some((target_scope, source_scope)) =
@@ -124,25 +134,40 @@ pub(crate) fn extract_struct_from_enum_variant(
}
}
} else {
- field_list.clone_for_update()
+ field_list.clone()
};
- let def =
- create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast);
+ let (comments_for_struct, comments_to_delete) =
+ collect_variant_comments(make, variant.syntax());
+ for element in &comments_to_delete {
+ editor.delete(element.clone());
+ }
+
+ let def = create_struct_def(
+ make,
+ variant_name.clone(),
+ &field_list,
+ generic_params.clone(),
+ &enum_ast,
+ );
let enum_ast = variant.parent_enum();
let indent = enum_ast.indent_level();
let def = def.indent(indent);
- ted::insert_all(
- ted::Position::before(enum_ast.syntax()),
- vec![
- def.syntax().clone().into(),
- make::tokens::whitespace(&format!("\n\n{indent}")).into(),
- ],
- );
+ let mut insert_items: Vec<SyntaxElement> = Vec::new();
+ for attr in enum_ast.attrs() {
+ insert_items.push(attr.syntax().clone().into());
+ insert_items.push(make.whitespace("\n").into());
+ }
+ insert_items.extend(comments_for_struct);
+ insert_items.push(def.syntax().clone().into());
+ insert_items.push(make.whitespace(&format!("\n\n{indent}")).into());
+ editor.insert_all_with_whitespace(Position::before(enum_ast.syntax()), insert_items);
+
+ update_variant(&editor, &variant, generic_params);
- update_variant(&variant, generic_params.map(|g| g.clone_for_update()));
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -184,11 +209,13 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &En
}
fn extract_generic_params(
+ make: &SyntaxFactory,
known_generics: &ast::GenericParamList,
field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
) -> Option<ast::GenericParamList> {
let mut generics = known_generics.generic_params().map(|param| (param, false)).collect_vec();
+ #[expect(clippy::unnecessary_fold, reason = "this function has side effects")]
let tagged_one = match field_list {
Either::Left(field_list) => field_list
.fields()
@@ -201,7 +228,7 @@ fn extract_generic_params(
};
let generics = generics.into_iter().filter_map(|(param, tag)| tag.then_some(param));
- tagged_one.then(|| make::generic_param_list(generics))
+ tagged_one.then(|| make.generic_param_list(generics))
}
fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, bool)]) -> bool {
@@ -222,6 +249,10 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b
}
}
param if matches!(token.kind(), T![ident]) => {
+ #[expect(
+ clippy::collapsible_match,
+ reason = "it won't compile since in the guard, `param` is immutable"
+ )]
if match param {
ast::GenericParam::ConstParam(konst) => konst
.name()
@@ -250,82 +281,74 @@ fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, b
}
fn create_struct_def(
+ make: &SyntaxFactory,
name: ast::Name,
- variant: &ast::Variant,
field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
generics: Option<ast::GenericParamList>,
enum_: &ast::Enum,
) -> ast::Struct {
let enum_vis = enum_.visibility();
- let insert_vis = |node: &'_ SyntaxNode, vis: &'_ SyntaxNode| {
- let vis = vis.clone_for_update();
- ted::insert(ted::Position::before(node), vis);
- };
-
// for fields without any existing visibility, use visibility of enum
let field_list: ast::FieldList = match field_list {
Either::Left(field_list) => {
if let Some(vis) = &enum_vis {
- field_list
- .fields()
- .filter(|field| field.visibility().is_none())
- .filter_map(|field| field.name())
- .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
+ let new_fields = field_list.fields().map(|field| {
+ if field.visibility().is_none()
+ && let Some(name) = field.name()
+ && let Some(ty) = field.ty()
+ {
+ make.record_field(Some(vis.clone()), name, ty)
+ } else {
+ field
+ }
+ });
+ make.record_field_list(new_fields).into()
+ } else {
+ field_list.clone().into()
}
-
- field_list.clone().into()
}
Either::Right(field_list) => {
if let Some(vis) = &enum_vis {
- field_list
- .fields()
- .filter(|field| field.visibility().is_none())
- .filter_map(|field| field.ty())
- .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
+ let new_fields = field_list.fields().map(|field| {
+ if field.visibility().is_none()
+ && let Some(ty) = field.ty()
+ {
+ make.tuple_field(Some(vis.clone()), ty)
+ } else {
+ field
+ }
+ });
+ make.tuple_field_list(new_fields).into()
+ } else {
+ field_list.clone().into()
}
-
- field_list.clone().into()
}
};
- let strukt = make::struct_(enum_vis, name, generics, field_list).clone_for_update();
-
- // take comments from variant
- ted::insert_all(
- ted::Position::first_child_of(strukt.syntax()),
- take_all_comments(variant.syntax()),
- );
-
- // copy attributes from enum
- ted::insert_all(
- ted::Position::first_child_of(strukt.syntax()),
- enum_
- .attrs()
- .flat_map(|it| {
- vec![it.syntax().clone_for_update().into(), make::tokens::single_newline().into()]
- })
- .collect(),
- );
-
- strukt
+ make.struct_(enum_vis, name, generics, field_list)
}
-fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList>) -> Option<()> {
+fn update_variant(
+ editor: &SyntaxEditor,
+ variant: &ast::Variant,
+ generics: Option<ast::GenericParamList>,
+) -> Option<()> {
+ let make = editor.make();
let name = variant.name()?;
let generic_args = generics
.filter(|generics| generics.generic_params().count() > 0)
.map(|generics| generics.to_generic_args());
// FIXME: replace with a `ast::make` constructor
let ty = match generic_args {
- Some(generic_args) => make::ty(&format!("{name}{generic_args}")),
- None => make::ty(&name.text()),
+ Some(generic_args) => make.ty(&format!("{name}{generic_args}")),
+ None => make.ty(&name.text()),
};
// change from a record to a tuple field list
- let tuple_field = make::tuple_field(None, ty);
- let field_list = make::tuple_field_list(iter::once(tuple_field)).clone_for_update();
- ted::replace(variant.field_list()?.syntax(), field_list.syntax());
+ let tuple_field = make.tuple_field(None, ty);
+ let field_list = make.tuple_field_list(iter::once(tuple_field));
+ editor.replace(variant.field_list()?.syntax(), field_list.syntax());
// remove any ws after the name
if let Some(ws) = name
@@ -333,35 +356,39 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList
.siblings_with_tokens(syntax::Direction::Next)
.find_map(|tok| tok.into_token().filter(|tok| tok.kind() == WHITESPACE))
{
- ted::remove(SyntaxElement::Token(ws));
+ editor.delete(ws);
}
Some(())
}
-// Note: this also detaches whitespace after comments,
-// since `SyntaxNode::splice_children` (and by extension `ted::insert_all_raw`)
-// detaches nodes. If we only took the comments, we'd leave behind the old whitespace.
-fn take_all_comments(node: &SyntaxNode) -> Vec<SyntaxElement> {
- let mut remove_next_ws = false;
- node.children_with_tokens()
- .filter_map(move |child| match child.kind() {
+fn collect_variant_comments(
+ make: &SyntaxFactory,
+ node: &SyntaxNode,
+) -> (Vec<SyntaxElement>, Vec<SyntaxElement>) {
+ let mut to_insert: Vec<SyntaxElement> = Vec::new();
+ let mut to_delete: Vec<SyntaxElement> = Vec::new();
+ let mut after_comment = false;
+
+ for child in node.children_with_tokens() {
+ match child.kind() {
COMMENT => {
- remove_next_ws = true;
- child.detach();
- Some(child)
+ after_comment = true;
+ to_insert.push(child.clone());
+ to_delete.push(child);
}
- WHITESPACE if remove_next_ws => {
- remove_next_ws = false;
- child.detach();
- Some(make::tokens::single_newline().into())
+ WHITESPACE if after_comment => {
+ after_comment = false;
+ to_insert.push(make.whitespace("\n").into());
+ to_delete.push(child);
}
_ => {
- remove_next_ws = false;
- None
+ after_comment = false;
}
- })
- .collect()
+ }
+ }
+
+ (to_insert, to_delete)
}
fn apply_references(
@@ -370,20 +397,21 @@ fn apply_references(
node: SyntaxNode,
import: Option<(ImportScope, hir::ModPath)>,
edition: Edition,
+ editor: &SyntaxEditor,
) {
+ let make = editor.make();
if let Some((scope, path)) = import {
- insert_use(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg);
+ insert_use_with_editor(&scope, mod_path_to_ast(&path, edition), &insert_use_cfg, editor);
}
// deep clone to prevent cycle
- let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
- ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
- ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
- ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
+ let path = make.path_from_segments(iter::once(segment.clone()), false);
+ editor.insert(Position::before(segment.syntax()), make.token(T!['(']));
+ editor.insert(Position::before(segment.syntax()), path.syntax());
+ editor.insert(Position::after(&node), make.token(T![')']));
}
fn process_references(
ctx: &AssistContext<'_>,
- builder: &mut SourceChangeBuilder,
visited_modules: &mut FxHashSet<Module>,
enum_module_def: &ModuleDef,
variant_hir_name: &Name,
@@ -394,8 +422,6 @@ fn process_references(
refs.into_iter()
.flat_map(|reference| {
let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
- let segment = builder.make_mut(segment);
- let scope_node = builder.make_syntax_mut(scope_node);
if !visited_modules.contains(&module) {
let cfg =
ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db)));
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs
index e4fdac27f4..eda35eba45 100644
--- a/crates/ide-assists/src/handlers/extract_type_alias.rs
+++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -1,11 +1,8 @@
use either::Either;
use hir::HirDisplay;
-use ide_db::syntax_helpers::node_ext::walk_ty;
+use ide_db::syntax_helpers::{node_ext::walk_ty, suggest_name::NameGenerator};
use syntax::{
- ast::{
- self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel,
- syntax_factory::SyntaxFactory,
- },
+ ast::{self, AstNode, HasGenericArgs, HasGenericParams, HasName, edit::IndentLevel},
syntax_editor,
};
@@ -43,9 +40,10 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
);
let target = ty.syntax().text_range();
+ let scope = ctx.sema.scope(ty.syntax())?;
let resolved_ty = ctx.sema.resolve_type(&ty)?;
let resolved_ty = if !resolved_ty.contains_unknown() {
- let module = ctx.sema.scope(ty.syntax())?.module();
+ let module = scope.module();
resolved_ty.display_source_code(ctx.db(), module.into(), false).ok()?
} else {
ty.to_string()
@@ -56,10 +54,11 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
"Extract type as type alias",
target,
|builder| {
- let mut edit = builder.make_editor(node);
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(node);
+ let make = editor.make();
let resolved_ty = make.ty(&resolved_ty);
+ let name = &NameGenerator::new_from_scope_non_locals(Some(scope)).suggest_name("Type");
let mut known_generics = match item.generic_param_list() {
Some(it) => it.generic_params().collect(),
@@ -78,24 +77,24 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
// Replace original type with the alias
let ty_args = generic_params.as_ref().map(|it| it.to_generic_args().generic_args());
let new_ty = if let Some(ty_args) = ty_args {
- make.generic_ty_path_segment(make.name_ref("Type"), ty_args)
+ make.generic_ty_path_segment(make.name_ref(name), ty_args)
} else {
- make.path_segment(make.name_ref("Type"))
+ make.path_segment(make.name_ref(name))
};
- edit.replace(ty.syntax(), new_ty.syntax());
+ editor.replace(ty.syntax(), new_ty.syntax());
// Insert new alias
let ty_alias =
- make.ty_alias(None, "Type", generic_params, None, None, Some((resolved_ty, None)));
+ make.ty_alias(None, name, generic_params, None, None, Some((resolved_ty, None)));
if let Some(cap) = ctx.config.snippet_cap
&& let Some(name) = ty_alias.name()
{
- edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap));
+ editor.add_annotation(name.syntax(), builder.make_tabstop_before(cap));
}
let indent = IndentLevel::from_node(node);
- edit.insert_all(
+ editor.insert_all(
syntax_editor::Position::before(node),
vec![
ty_alias.syntax().clone().into(),
@@ -103,7 +102,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) ->
],
);
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -450,4 +449,41 @@ fn main() {
"#,
)
}
+
+ #[test]
+ fn duplicate_names() {
+ check_assist(
+ extract_type_alias,
+ r"
+struct Type;
+struct S {
+ field: $0u8$0,
+}
+ ",
+ r#"
+struct Type;
+type $0Type1 = u8;
+
+struct S {
+ field: Type1,
+}
+ "#,
+ );
+
+ check_assist(
+ extract_type_alias,
+ r"
+struct S<Type> {
+ field: $0u8$0,
+}
+ ",
+ r#"
+type $0Type1 = u8;
+
+struct S<Type> {
+ field: Type1,
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs
index e5ce02cf53..c5c57c76b4 100644
--- a/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/crates/ide-assists/src/handlers/extract_variable.rs
@@ -4,14 +4,13 @@ use ide_db::{
syntax_helpers::{LexedStr, suggest_name},
};
use syntax::{
- NodeOrToken, SyntaxKind, SyntaxNode, T,
- algo::ancestors_at_offset,
+ Direction, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange,
+ algo::{ancestors_at_offset, skip_trivia_token},
ast::{
self, AstNode,
edit::{AstNodeEdit, IndentLevel},
- syntax_factory::SyntaxFactory,
},
- syntax_editor::Position,
+ syntax_editor::{Element, Position},
};
use crate::{AssistContext, AssistId, Assists, utils::is_body_const};
@@ -92,27 +91,54 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
let range = node.text_range();
- let to_extract = node
- .descendants()
- .take_while(|it| range.contains_range(it.text_range()))
- .find_map(valid_target_expr(ctx))?;
+ let (to_replace, analysis) = if node.kind() == SyntaxKind::TOKEN_TREE {
+ let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?;
- let ty = ctx.sema.type_of_expr(&to_extract).map(TypeInfo::adjusted);
+ let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone());
+ let last_descend = ctx.sema.descend_into_macros_single_exact(last.clone());
+ let range = first_descend.text_range().cover(last_descend.text_range());
+
+ if first_descend.parent_ancestors().last() != last_descend.parent_ancestors().last() {
+ return None;
+ }
+
+ let expr = first_descend
+ .parent_ancestors()
+ .skip_while(|it| !it.text_range().contains_range(range))
+ .find_map(valid_target_expr(ctx))?;
+ let original_range = ctx.sema.original_range(expr.syntax());
+ let (first, last) = extract_token_range_of(&node, original_range.range)?;
+ let to_extract = first.syntax_element()..=last.syntax_element();
+ (to_extract, expr)
+ } else {
+ let expr = node
+ .descendants()
+ .take_while(|it| range.contains_range(it.text_range()))
+ .find_map(valid_target_expr(ctx))?;
+ let to_extract = expr.syntax().syntax_element();
+ (to_extract.clone()..=to_extract, expr)
+ };
+ let place = match to_replace.start() {
+ NodeOrToken::Node(node) => node.clone(),
+ NodeOrToken::Token(t) => t.parent()?,
+ };
+
+ let ty = ctx.sema.type_of_expr(&analysis).map(TypeInfo::adjusted);
if matches!(&ty, Some(ty_info) if ty_info.is_unit()) {
return None;
}
- let parent = to_extract.syntax().parent().and_then(ast::Expr::cast);
+ let parent = analysis.syntax().parent().and_then(ast::Expr::cast);
// Any expression that autoderefs may need adjustment.
let mut needs_adjust = parent.as_ref().is_some_and(|it| match it {
ast::Expr::FieldExpr(_)
| ast::Expr::MethodCallExpr(_)
| ast::Expr::CallExpr(_)
| ast::Expr::AwaitExpr(_) => true,
- ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&to_extract) => true,
+ ast::Expr::IndexExpr(index) if index.base().as_ref() == Some(&analysis) => true,
_ => false,
});
- let mut to_extract_no_ref = peel_parens(to_extract.clone());
+ let mut to_extract_no_ref = peel_parens(analysis.clone());
let needs_ref = needs_adjust
&& match &to_extract_no_ref {
ast::Expr::FieldExpr(_)
@@ -127,14 +153,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
}
_ => false,
};
- let module = ctx.sema.scope(to_extract.syntax())?.module();
- let target = to_extract.syntax().text_range();
+ let module = ctx.sema.scope(analysis.syntax())?.module();
+ let target = to_replace.start().text_range().cover(to_replace.end().text_range());
let needs_mut = match &parent {
Some(ast::Expr::RefExpr(expr)) => expr.mut_token().is_some(),
_ => needs_adjust && !needs_ref && ty.as_ref().is_some_and(|ty| ty.is_mutable_reference()),
};
for kind in ExtractionKind::ALL {
- let Some(anchor) = Anchor::from(&to_extract, kind) else {
+ let Some(anchor) = Anchor::from(&place, kind) else {
continue;
};
@@ -169,10 +195,18 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
kind.label(),
target,
|edit| {
- let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &to_extract);
+ let (var_name, expr_replace) = kind.get_name_and_expr(ctx, &analysis);
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(&expr_replace);
+ let to_replace =
+ if expr_replace.ancestors().last() == to_replace.start().ancestors().last() {
+ let element = expr_replace.clone().syntax_element();
+ element.clone()..=element
+ } else {
+ to_replace.clone()
+ };
+
+ let editor = edit.make_editor(&place);
+ let make = editor.make();
let pat_name = make.name(&var_name);
let name_expr = make.expr_path(make.ident_path(&var_name));
@@ -236,7 +270,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
],
);
- editor.replace(expr_replace, name_expr.syntax());
+ editor.replace_all(to_replace, vec![name_expr.syntax().syntax_element()]);
}
Anchor::Replace(stmt) => {
cov_mark::hit!(test_extract_var_expr_stmt);
@@ -252,17 +286,16 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
make.block_expr([new_stmt], Some(name_expr))
} else {
// `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
- editor.replace(expr_replace, name_expr.syntax());
+ editor
+ .replace_all(to_replace, vec![name_expr.syntax().syntax_element()]);
make.block_expr([new_stmt], Some(to_wrap.clone()))
}
// fixup indentation of block
- .indent_with_mapping(indent_to, &make);
+ .indent_with_mapping(indent_to, make);
editor.replace(to_wrap.syntax(), block.syntax());
}
}
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
edit.rename();
},
@@ -272,6 +305,23 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
Some(())
}
+fn extract_token_range_of(
+ node: &SyntaxNode,
+ range: TextRange,
+) -> Option<(SyntaxToken, SyntaxToken)> {
+ let first = node.token_at_offset(range.start()).right_biased()?;
+ let last = node.token_at_offset(range.end()).left_biased()?;
+
+ let first = skip_trivia_token(first, Direction::Next)?;
+ let last = skip_trivia_token(last, Direction::Next)?;
+
+ if first.text_range().ordering(last.text_range()).is_gt() {
+ return None;
+ }
+
+ Some((first, last))
+}
+
fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
while let ast::Expr::ParenExpr(parens) = &expr {
let Some(expr_inside) = parens.expr() else { break };
@@ -283,13 +333,17 @@ 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> {
- |node| match node.kind() {
+ let selection = ctx.selection_trimmed();
+ move |node| match node.kind() {
SyntaxKind::LOOP_EXPR | SyntaxKind::LET_EXPR => None,
SyntaxKind::BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
SyntaxKind::RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()),
SyntaxKind::BLOCK_EXPR => {
ast::BlockExpr::cast(node).filter(|it| it.is_standalone()).map(ast::Expr::from)
}
+ SyntaxKind::ARG_LIST => ast::ArgList::cast(node)?
+ .args()
+ .find(|expr| crate::utils::is_selected(expr, selection, false)),
SyntaxKind::PATH_EXPR => {
let path_expr = ast::PathExpr::cast(node)?;
let path_resolution = ctx.sema.resolve_path(&path_expr.path()?)?;
@@ -397,9 +451,8 @@ enum Anchor {
}
impl Anchor {
- fn from(to_extract: &ast::Expr, kind: &ExtractionKind) -> Option<Anchor> {
- let result = to_extract
- .syntax()
+ fn from(place: &SyntaxNode, kind: &ExtractionKind) -> Option<Anchor> {
+ let result = place
.ancestors()
.take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind()))
.find_map(|node| {
@@ -431,7 +484,7 @@ impl Anchor {
if let Some(stmt) = ast::Stmt::cast(node.clone()) {
if let ast::Stmt::ExprStmt(stmt) = stmt
- && stmt.expr().as_ref() == Some(to_extract)
+ && stmt.expr().is_some_and(|it| it.syntax() == place)
{
return Some(Anchor::Replace(stmt));
}
@@ -442,7 +495,7 @@ impl Anchor {
match kind {
ExtractionKind::Constant | ExtractionKind::Static if result.is_none() => {
- to_extract.syntax().ancestors().find_map(|node| {
+ place.ancestors().find_map(|node| {
let item = ast::Item::cast(node.clone())?;
let parent = item.syntax().parent()?;
match parent.kind() {
@@ -1286,6 +1339,33 @@ fn main() {
}
#[test]
+ fn extract_var_in_arglist_with_comma() {
+ check_assist_by_label(
+ extract_variable,
+ r#"
+fn main() {
+ let x = 2;
+ foo(
+ x + x,
+ $0x - x,$0
+ )
+}
+"#,
+ r#"
+fn main() {
+ let x = 2;
+ let $0var_name = x - x;
+ foo(
+ x + x,
+ var_name,
+ )
+}
+"#,
+ "Extract into variable",
+ );
+ }
+
+ #[test]
fn extract_var_path_simple() {
check_assist_by_label(
extract_variable,
@@ -2744,4 +2824,184 @@ fn main() {
"Extract into variable",
);
}
+
+ #[test]
+ fn extract_variable_in_token_tree() {
+ // FIXME: Keep the original trivia instead of extracting macro expanded?
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= $02 + 3$0 + 4);
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0var_name = 2+3;
+ let x = foo!(= var_name + 4);
+}
+"#,
+ "Extract into variable",
+ );
+
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= $02 +$0 3 + 4);
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0var_name = 2+3;
+ let x = foo!(= var_name + 4);
+}
+"#,
+ "Extract into variable",
+ );
+
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= $02 + 3 + 4$0);
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0var_name = 2+3+4;
+ let x = foo!(= var_name);
+}
+"#,
+ "Extract into variable",
+ );
+
+ // FIXME: Extract to inside the macro instead of outside the macro
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= {
+ $02 + 3 + 4$0
+ });
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0var_name = 2+3+4;
+ let x = foo!(= {
+ var_name
+ });
+}
+"#,
+ "Extract into variable",
+ );
+ }
+
+ #[test]
+ fn extract_variable_in_token_tree_record_expr() {
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= Foo { x: $02 + 3$0 });
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0x = 2+3;
+ let x = foo!(= Foo { x: x });
+}
+"#,
+ "Extract into variable",
+ );
+
+ check_assist_by_label(
+ extract_variable,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let x = foo!(= Foo { x: $02 + 3$0 + 4 });
+}
+"#,
+ r#"
+macro_rules! foo {
+ (= $($t:tt)*) => {
+ $($t)*
+ };
+}
+
+fn main() {
+ let $0var_name = 2+3;
+ let x = foo!(= Foo { x: var_name + 4 });
+}
+"#,
+ "Extract into variable",
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs
index 440f2d5f17..d8714dd49c 100644
--- a/crates/ide-assists/src/handlers/fix_visibility.rs
+++ b/crates/ide-assists/src/handlers/fix_visibility.rs
@@ -78,7 +78,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
};
acc.add(AssistId::quick_fix("fix_visibility"), assist_label, target, |builder| {
- let mut editor = builder.make_editor(vis_owner.syntax());
+ let editor = builder.make_editor(vis_owner.syntax());
if let Some(current_visibility) = vis_owner.visibility() {
editor.replace(current_visibility.syntax(), missing_visibility.syntax());
diff --git a/crates/ide-assists/src/handlers/flip_binexpr.rs b/crates/ide-assists/src/handlers/flip_binexpr.rs
index 8f2306e903..17911150f5 100644
--- a/crates/ide-assists/src/handlers/flip_binexpr.rs
+++ b/crates/ide-assists/src/handlers/flip_binexpr.rs
@@ -1,6 +1,6 @@
use syntax::{
SyntaxKind, T,
- ast::{self, AstNode, BinExpr, RangeItem, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, BinExpr, RangeItem},
syntax_editor::Position,
};
@@ -48,14 +48,13 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
"Flip binary expression",
op_token.text_range(),
|builder| {
- let mut editor = builder.make_editor(&expr.syntax().parent().unwrap());
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(&expr.syntax().parent().unwrap());
+ let make = editor.make();
if let FlipAction::FlipAndReplaceOp(binary_op) = action {
editor.replace(op_token, make.token(binary_op))
};
editor.replace(lhs.syntax(), rhs.syntax());
editor.replace(rhs.syntax(), lhs.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -133,25 +132,25 @@ pub(crate) fn flip_range_expr(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
"Flip range expression",
op.text_range(),
|builder| {
- let mut edit = builder.make_editor(range_expr.syntax());
+ let editor = builder.make_editor(range_expr.syntax());
match (start, end) {
(Some(start), Some(end)) => {
- edit.replace(start.syntax(), end.syntax());
- edit.replace(end.syntax(), start.syntax());
+ editor.replace(start.syntax(), end.syntax());
+ editor.replace(end.syntax(), start.syntax());
}
(Some(start), None) => {
- edit.delete(start.syntax());
- edit.insert(Position::after(&op), start.syntax().clone_for_update());
+ editor.delete(start.syntax());
+ editor.insert(Position::after(&op), start.syntax());
}
(None, Some(end)) => {
- edit.delete(end.syntax());
- edit.insert(Position::before(&op), end.syntax().clone_for_update());
+ editor.delete(end.syntax());
+ editor.insert(Position::before(&op), end.syntax());
}
(None, None) => (),
}
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs
index 1e95d47723..65dc36cdca 100644
--- a/crates/ide-assists/src/handlers/flip_comma.rs
+++ b/crates/ide-assists/src/handlers/flip_comma.rs
@@ -2,7 +2,6 @@ use syntax::{
AstNode, Direction, NodeOrToken, SyntaxKind, SyntaxToken, T,
algo::non_trivia_sibling,
ast::{self, syntax_factory::SyntaxFactory},
- syntax_editor::SyntaxMapping,
};
use crate::{AssistContext, AssistId, Assists};
@@ -42,14 +41,13 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let target = comma.text_range();
acc.add(AssistId::refactor_rewrite("flip_comma"), "Flip comma", target, |builder| {
let parent = comma.parent().unwrap();
- let mut editor = builder.make_editor(&parent);
+ let editor = builder.make_editor(&parent);
if let Some(parent) = ast::TokenTree::cast(parent) {
// An attribute. It often contains a path followed by a
// token tree (e.g. `align(2)`), so we have to be smarter.
- let (new_tree, mapping) = flip_tree(parent.clone(), comma);
+ let new_tree = flip_tree(parent.clone(), comma, editor.make());
editor.replace(parent.syntax(), new_tree.syntax());
- editor.add_mappings(mapping);
} else {
editor.replace(prev.clone(), next.clone());
editor.replace(next.clone(), prev.clone());
@@ -59,7 +57,7 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
})
}
-fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, SyntaxMapping) {
+fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken, make: &SyntaxFactory) -> ast::TokenTree {
let mut tree_iter = tree.token_trees_and_tokens();
let before: Vec<_> =
tree_iter.by_ref().take_while(|it| it.as_token() != Some(&comma)).collect();
@@ -100,10 +98,7 @@ fn flip_tree(tree: ast::TokenTree, comma: SyntaxToken) -> (ast::TokenTree, Synta
&after[next_end..after.len() - 1],
]
.concat();
-
- let make = SyntaxFactory::with_mappings();
- let new_token_tree = make.token_tree(tree.left_delimiter_token().unwrap().kind(), result);
- (new_token_tree, make.finish_with_mappings())
+ make.token_tree(tree.left_delimiter_token().unwrap().kind(), result)
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/flip_or_pattern.rs b/crates/ide-assists/src/handlers/flip_or_pattern.rs
index 4829f5bec2..bd56331f41 100644
--- a/crates/ide-assists/src/handlers/flip_or_pattern.rs
+++ b/crates/ide-assists/src/handlers/flip_or_pattern.rs
@@ -32,7 +32,7 @@ pub(crate) fn flip_or_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let target = pipe.text_range();
acc.add(AssistId::refactor_rewrite("flip_or_pattern"), "Flip patterns", target, |builder| {
- let mut editor = builder.make_editor(parent.syntax());
+ let editor = builder.make_editor(parent.syntax());
editor.replace(before.clone(), after.clone());
editor.replace(after, before);
builder.add_file_edits(ctx.vfs_file_id(), editor);
diff --git a/crates/ide-assists/src/handlers/flip_trait_bound.rs b/crates/ide-assists/src/handlers/flip_trait_bound.rs
index 9756268c7c..dfd280efa6 100644
--- a/crates/ide-assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ide-assists/src/handlers/flip_trait_bound.rs
@@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
"Flip trait bounds",
target,
|builder| {
- let mut editor = builder.make_editor(parent.syntax());
+ let editor = builder.make_editor(parent.syntax());
editor.replace(before.clone(), after.clone());
editor.replace(after, before);
builder.add_file_edits(ctx.vfs_file_id(), editor);
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 e022a27e51..0bb90f187c 100644
--- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -13,7 +13,7 @@ use syntax::{
AstNode,
ast::{
self, AssocItem, GenericParam, HasAttrs, HasGenericParams, HasName, HasTypeBounds,
- HasVisibility, edit::AstNodeEdit, make,
+ HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory,
},
syntax_editor::Position,
};
@@ -73,24 +73,25 @@ pub(crate) fn generate_blanket_trait_impl(
"Generate blanket trait implementation",
name.syntax().text_range(),
|builder| {
- let mut edit = builder.make_editor(traitd.syntax());
- let namety = make::ty_path(make::path_from_text(&name.text()));
+ let editor = builder.make_editor(traitd.syntax());
+ let make = editor.make();
+ let namety = make.ty_path(make.path_from_text(&name.text()));
let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent());
- let bounds = traitd.type_bound_list().and_then(exlucde_sized);
+ let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list));
let is_unsafe = traitd.unsafe_token().is_some();
- let thisname = this_name(&traitd);
- let thisty = make::ty_path(make::path_from_text(&thisname.text()));
+ let thisname = this_name(make, &traitd);
+ let thisty = make.ty_path(make.path_from_text(&thisname.text()));
let indent = traitd.indent_level();
- let gendecl = make::generic_param_list([GenericParam::TypeParam(make::type_param(
+ let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param(
thisname.clone(),
- apply_sized(has_sized(&traitd, &ctx.sema), bounds),
+ apply_sized(make, has_sized(&traitd, &ctx.sema), bounds),
))]);
let trait_gen_args =
traitd.generic_param_list().map(|param_list| param_list.to_generic_args());
- let impl_ = make::impl_trait(
+ let impl_ = make.impl_trait(
cfg_attrs(&traitd),
is_unsafe,
traitd.generic_param_list(),
@@ -98,20 +99,19 @@ pub(crate) fn generate_blanket_trait_impl(
Some(gendecl),
None,
false,
- namety,
- thisty.clone(),
+ namety.into(),
+ thisty.into(),
trait_where_clause,
None,
None,
- )
- .clone_for_update();
+ );
if let Some(trait_assoc_list) = traitd.assoc_item_list() {
- let assoc_item_list = impl_.get_or_create_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(&method, ctx.config).into()
+ todo_fn(make, &method, ctx.config).into()
}
ast::AssocItem::Const(_) | ast::AssocItem::TypeAlias(_) => item,
_ => continue,
@@ -122,10 +122,10 @@ pub(crate) fn generate_blanket_trait_impl(
let impl_ = impl_.indent(indent);
- edit.insert_all(
+ editor.insert_all(
Position::after(traitd.syntax()),
vec![
- make::tokens::whitespace(&format!("\n\n{indent}")).into(),
+ make.whitespace(&format!("\n\n{indent}")).into(),
impl_.syntax().clone().into(),
],
);
@@ -135,8 +135,7 @@ pub(crate) fn generate_blanket_trait_impl(
{
builder.add_tabstop_before(cap, self_ty);
}
-
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -212,22 +211,26 @@ fn where_clause_sized(where_clause: Option<ast::WhereClause>) -> Option<bool> {
})
}
-fn apply_sized(has_sized: bool, bounds: Option<ast::TypeBoundList>) -> Option<ast::TypeBoundList> {
+fn apply_sized(
+ make: &SyntaxFactory,
+ has_sized: bool,
+ bounds: Option<ast::TypeBoundList>,
+) -> Option<ast::TypeBoundList> {
if has_sized {
return bounds;
}
let bounds = bounds
.into_iter()
.flat_map(|bounds| bounds.bounds())
- .chain([make::type_bound_text("?Sized")]);
- make::type_bound_list(bounds)
+ .chain([make.type_bound_text("?Sized")]);
+ make.type_bound_list(bounds)
}
-fn exlucde_sized(bounds: ast::TypeBoundList) -> Option<ast::TypeBoundList> {
- make::type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized")))
+fn exclude_sized(make: &SyntaxFactory, bounds: ast::TypeBoundList) -> Option<ast::TypeBoundList> {
+ make.type_bound_list(bounds.bounds().filter(|bound| !ty_bound_is(bound, "Sized")))
}
-fn this_name(traitd: &ast::Trait) -> ast::Name {
+fn this_name(make: &SyntaxFactory, traitd: &ast::Trait) -> ast::Name {
let has_iter = find_bound("Iterator", traitd.type_bound_list()).is_some();
let params = traitd
@@ -245,7 +248,7 @@ fn this_name(traitd: &ast::Trait) -> ast::Name {
let mut name_gen =
suggest_name::NameGenerator::new_with_names(params.iter().map(String::as_str));
- make::name(&name_gen.suggest_name(if has_iter { "I" } else { "T" }))
+ make.name(&name_gen.suggest_name(if has_iter { "I" } else { "T" }))
}
fn find_bound(s: &str, bounds: Option<ast::TypeBoundList>) -> Option<ast::TypeBound> {
@@ -260,16 +263,16 @@ fn ty_bound_is(bound: &ast::TypeBound, s: &str) -> bool {
.is_some_and(|name| name.text() == s))
}
-fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn {
- let params = f.param_list().unwrap_or_else(|| make::param_list(None, None));
- make::fn_(
+fn todo_fn(make: &SyntaxFactory, f: &ast::Fn, config: &AssistConfig) -> ast::Fn {
+ let params = f.param_list().unwrap_or_else(|| make.param_list(None, None));
+ make.fn_(
cfg_attrs(f),
f.visibility(),
- f.name().unwrap_or_else(|| make::name("unnamed")),
+ f.name().unwrap_or_else(|| make.name("unnamed")),
f.generic_param_list(),
f.where_clause(),
params,
- make::block_expr(None, Some(crate::utils::expr_fill_default(config))),
+ make.block_expr(None, Some(crate::utils::expr_fill_default(config))),
f.ret_type(),
f.async_token().is_some(),
f.const_token().is_some(),
@@ -279,7 +282,7 @@ fn todo_fn(f: &ast::Fn, config: &AssistConfig) -> ast::Fn {
}
fn cfg_attrs(node: &impl HasAttrs) -> impl Iterator<Item = ast::Attr> {
- node.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"))
+ node.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))))
}
#[cfg(test)]
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 2d92bf5146..739b631736 100644
--- a/crates/ide-assists/src/handlers/generate_default_from_new.rs
+++ b/crates/ide-assists/src/handlers/generate_default_from_new.rs
@@ -73,12 +73,12 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<'
"Generate a Default impl from a new fn",
target,
move |builder| {
- let make = SyntaxFactory::without_mappings();
- let default_impl = generate_default_impl(&make, &impl_, self_ty);
+ let editor = builder.make_editor(impl_.syntax());
+ let make = editor.make();
+ let default_impl = generate_default_impl(make, &impl_, self_ty);
let indent = IndentLevel::from_node(impl_.syntax());
let default_impl = default_impl.indent(indent);
- let mut editor = builder.make_editor(impl_.syntax());
editor.insert_all(
Position::after(impl_.syntax()),
vec![
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 63033c7d5e..9486aa6f01 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -4,7 +4,6 @@ use syntax::{
ast::{
self, AstNode, HasGenericParams, HasName, HasVisibility as _,
edit::{AstNodeEdit, IndentLevel},
- syntax_factory::SyntaxFactory,
},
syntax_editor::Position,
};
@@ -107,7 +106,8 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
format!("Generate delegate for `{field_name}.{name}()`",),
target,
|edit| {
- let make = SyntaxFactory::without_mappings();
+ let editor = edit.make_editor(strukt.syntax());
+ let make = editor.make();
let field = make
.field_from_idents(["self", &field_name])
.expect("always be a valid expression");
@@ -145,7 +145,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
// compute the `body`
let arg_list = method_source
.param_list()
- .map(|v| convert_param_list_to_arg_list(v, &make))
+ .map(|v| convert_param_list_to_arg_list(v, make))
.unwrap_or_else(|| make.arg_list([]));
let tail_expr = make.expr_method_call(field, make.name_ref(&name), arg_list).into();
@@ -173,12 +173,11 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
.indent(IndentLevel(1));
let item = ast::AssocItem::Fn(f.clone());
- let mut editor = edit.make_editor(strukt.syntax());
let fn_: Option<ast::AssocItem> = match impl_def {
Some(impl_def) => match impl_def.assoc_item_list() {
Some(assoc_item_list) => {
let item = item.indent(IndentLevel::from_node(impl_def.syntax()));
- assoc_item_list.add_items(&mut editor, vec![item.clone()]);
+ assoc_item_list.add_items(&editor, vec![item.clone()]);
Some(item)
}
None => {
@@ -229,7 +228,6 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let tabstop = edit.make_tabstop_before(cap);
editor.add_annotation(fn_.syntax(), tabstop);
}
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)?;
diff --git a/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
index f703e4dc4a..6639f10c1f 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_trait.rs
@@ -363,9 +363,9 @@ fn generate_impl(
ast_strukt,
&old_impl,
&transform_args,
- trait_args.clone_subtree(),
+ trait_args.clone(),
) {
- *trait_args = new_args.clone_subtree();
+ *trait_args = new_args.clone();
Some(new_args)
} else {
None
@@ -494,7 +494,7 @@ fn remove_instantiated_params(
}
}
-fn remove_useless_where_clauses(editor: &mut SyntaxEditor, delegate: &ast::Impl) {
+fn remove_useless_where_clauses(editor: &SyntaxEditor, delegate: &ast::Impl) {
let Some(wc) = delegate.where_clause() else {
return;
};
@@ -563,7 +563,7 @@ fn finalize_delegate(
return Some(delegate.clone());
}
- let mut editor = SyntaxEditor::new(delegate.syntax().clone_subtree());
+ let (editor, delegate) = SyntaxEditor::with_ast_node(delegate);
// 1. Replace assoc_item_list if we have new items
if let Some(items) = assoc_items
@@ -577,7 +577,7 @@ fn finalize_delegate(
// 2. Remove useless where clauses
if remove_where_clauses {
- remove_useless_where_clauses(&mut editor, delegate);
+ remove_useless_where_clauses(&editor, &delegate);
}
ast::Impl::cast(editor.finish().new_root().clone())
@@ -703,7 +703,7 @@ fn resolve_name_conflicts(
}
}
p @ ast::GenericParam::LifetimeParam(_) => {
- new_params.push(p.clone_for_update());
+ new_params.push(p);
}
ast::GenericParam::TypeParam(t) => {
let type_bounds = t.type_bound_list();
diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs
index 5534dc1cd3..a5bdf80ac7 100644
--- a/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/crates/ide-assists/src/handlers/generate_deref.rs
@@ -2,7 +2,7 @@ use hir::{ModPath, ModuleDef};
use ide_db::{FileId, RootDatabase, famous_defs::FamousDefs};
use syntax::{
Edition,
- ast::{self, AstNode, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, HasName, edit::AstNodeEdit},
syntax_editor::Position,
};
@@ -138,7 +138,8 @@ fn generate_edit(
trait_path: ModPath,
edition: Edition,
) {
- let make = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(strukt.syntax());
+ let make = editor.make();
let strukt_adt = ast::Adt::Struct(strukt.clone());
let trait_ty = make.ty(&trait_path.display(db, edition).to_string());
@@ -195,15 +196,12 @@ fn generate_edit(
let body = make.assoc_item_list(assoc_items);
let indent = strukt.indent_level();
- let impl_ = generate_trait_impl_intransitive_with_item(&make, &strukt_adt, trait_ty, body)
+ let impl_ = generate_trait_impl_intransitive_with_item(make, &strukt_adt, trait_ty, body)
.indent(indent);
-
- let mut editor = edit.make_editor(strukt.syntax());
editor.insert_all(
Position::after(strukt.syntax()),
vec![make.whitespace(&format!("\n\n{indent}")).into(), impl_.syntax().clone().into()],
);
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(file_id, editor);
}
diff --git a/crates/ide-assists/src/handlers/generate_derive.rs b/crates/ide-assists/src/handlers/generate_derive.rs
index 3ef68f06e4..0129b1db39 100644
--- a/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/crates/ide-assists/src/handlers/generate_derive.rs
@@ -1,7 +1,7 @@
use syntax::{
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
T,
- ast::{self, AstNode, HasAttrs, edit::IndentLevel, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, HasAttrs, edit::IndentLevel},
syntax_editor::{Element, Position},
};
@@ -42,17 +42,15 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
};
acc.add(AssistId::generate("generate_derive"), "Add `#[derive]`", target, |edit| {
- let make = SyntaxFactory::without_mappings();
-
match derive_attr {
None => {
+ let editor = edit.make_editor(nominal.syntax());
+ let make = editor.make();
let derive =
make.attr_outer(make.meta_token_tree(
make.ident_path("derive"),
make.token_tree(T!['('], vec![]),
));
-
- let mut editor = edit.make_editor(nominal.syntax());
let indent = IndentLevel::from_node(nominal.syntax());
let after_attrs_and_comments = nominal
.syntax()
@@ -68,9 +66,11 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
],
);
- let delimiter = derive
- .meta()
- .expect("make::attr_outer was expected to have Meta")
+ let meta = derive.meta().expect("make::attr_outer was expected to have Meta");
+ let ast::Meta::TokenTreeMeta(meta) = meta else {
+ unreachable!("make::attr_outer was passed a token tree meta");
+ };
+ let delimiter = meta
.token_tree()
.expect("failed to get token tree out of Meta")
.r_paren_token()
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 b866022a7d..e2783811f7 100644
--- a/crates/ide-assists/src/handlers/generate_enum_is_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
@@ -1,12 +1,14 @@
use ide_db::assists::GroupLabel;
-use itertools::Itertools;
use stdx::to_lower_snake_case;
-use syntax::ast::HasVisibility;
-use syntax::ast::{self, AstNode, HasName};
+use syntax::{
+ AstNode, Edition,
+ ast::{self, HasName, HasVisibility, edit::AstNodeEdit},
+ syntax_editor::Position,
+};
use crate::{
AssistContext, AssistId, Assists,
- utils::{add_method_to_adt, find_struct_impl, is_selected},
+ utils::{find_struct_impl, generate_impl_with_item, is_selected},
};
// Assist: generate_enum_is_method
@@ -64,27 +66,63 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>
target,
|builder| {
let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
- let method = methods
+
+ let fn_items: Vec<ast::AssocItem> = methods
.iter()
- .map(|Method { pattern_suffix, fn_name, variant_name }| {
- format!(
- " \
- /// Returns `true` if the {enum_lowercase_name} is [`{variant_name}`].
- ///
- /// [`{variant_name}`]: {enum_name}::{variant_name}
- #[must_use]
- {vis}fn {fn_name}(&self) -> bool {{
- matches!(self, Self::{variant_name}{pattern_suffix})
- }}",
- )
- })
- .join("\n\n");
-
- add_method_to_adt(builder, &parent_enum, impl_def, &method);
+ .map(|method| build_fn_item(method, &enum_lowercase_name, &enum_name, &vis))
+ .collect();
+
+ if let Some(impl_def) = &impl_def {
+ let editor = builder.make_editor(impl_def.syntax());
+ impl_def.assoc_item_list().unwrap().add_items(&editor, fn_items);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ return;
+ }
+
+ let editor = builder.make_editor(parent_enum.syntax());
+ let make = editor.make();
+ let indent = parent_enum.indent_level();
+ let assoc_list = make.assoc_item_list(fn_items);
+ let new_impl = generate_impl_with_item(make, &parent_enum, Some(assoc_list));
+ editor.insert_all(
+ Position::after(parent_enum.syntax()),
+ vec![
+ make.whitespace(&format!("\n\n{indent}")).into(),
+ new_impl.syntax().clone().into(),
+ ],
+ );
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
+fn build_fn_item(
+ method: &Method,
+ enum_lowercase_name: &str,
+ enum_name: &ast::Name,
+ vis: &str,
+) -> ast::AssocItem {
+ let Method { pattern_suffix, fn_name, variant_name } = method;
+ let fn_text = format!(
+ "/// Returns `true` if the {enum_lowercase_name} is [`{variant_name}`].
+///
+/// [`{variant_name}`]: {enum_name}::{variant_name}
+#[must_use]
+{vis}fn {fn_name}(&self) -> bool {{
+ matches!(self, Self::{variant_name}{pattern_suffix})
+}}"
+ );
+ let wrapped = format!("impl X {{ {fn_text} }}");
+ let parse = syntax::SourceFile::parse(&wrapped, Edition::CURRENT);
+ let fn_ = parse
+ .tree()
+ .syntax()
+ .descendants()
+ .find_map(ast::Fn::cast)
+ .expect("fn text must produce a valid fn node");
+ ast::AssocItem::Fn(fn_.indent(1.into()))
+}
+
struct Method {
pattern_suffix: &'static str,
fn_name: String,
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 39a6382b7c..9a97ad1e8f 100644
--- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -1,12 +1,14 @@
use ide_db::assists::GroupLabel;
-use itertools::Itertools;
use stdx::to_lower_snake_case;
-use syntax::ast::HasVisibility;
-use syntax::ast::{self, AstNode, HasName};
+use syntax::{
+ AstNode, Edition,
+ ast::{self, HasName, HasVisibility, edit::AstNodeEdit},
+ syntax_editor::Position,
+};
use crate::{
AssistContext, AssistId, Assists,
- utils::{add_method_to_adt, find_struct_impl, is_selected},
+ utils::{find_struct_impl, generate_impl_with_item, is_selected},
};
// Assist: generate_enum_try_into_method
@@ -116,15 +118,6 @@ fn generate_enum_projection_method(
assist_description: &str,
props: ProjectionProps,
) -> Option<()> {
- let ProjectionProps {
- fn_name_prefix,
- self_param,
- return_prefix,
- return_suffix,
- happy_case,
- sad_case,
- } = props;
-
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let parent_enum = ast::Adt::Enum(variant.parent_enum());
let variants = variant
@@ -135,7 +128,7 @@ fn generate_enum_projection_method(
.collect::<Vec<_>>();
let methods = variants
.iter()
- .map(|variant| Method::new(variant, fn_name_prefix))
+ .map(|variant| Method::new(variant, props.fn_name_prefix))
.collect::<Option<Vec<_>>>()?;
let fn_names = methods.iter().map(|it| it.fn_name.clone()).collect::<Vec<_>>();
stdx::never!(variants.is_empty());
@@ -151,30 +144,66 @@ fn generate_enum_projection_method(
target,
|builder| {
let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
+ let must_use = if ctx.config.assist_emit_must_use { "#[must_use]\n" } else { "" };
- let must_use = if ctx.config.assist_emit_must_use { "#[must_use]\n " } else { "" };
-
- let method = methods
+ let fn_items: Vec<ast::AssocItem> = methods
.iter()
- .map(|Method { pattern_suffix, field_type, bound_name, fn_name, variant_name }| {
- format!(
- " \
- {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
- if let Self::{variant_name}{pattern_suffix} = self {{
- {happy_case}({bound_name})
- }} else {{
- {sad_case}
- }}
- }}"
- )
- })
- .join("\n\n");
+ .map(|method| build_fn_item(method, &vis, must_use, &props))
+ .collect();
+
+ if let Some(impl_def) = &impl_def {
+ let editor = builder.make_editor(impl_def.syntax());
+ impl_def.assoc_item_list().unwrap().add_items(&editor, fn_items);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ return;
+ }
- add_method_to_adt(builder, &parent_enum, impl_def, &method);
+ let editor = builder.make_editor(parent_enum.syntax());
+ let make = editor.make();
+ let indent = parent_enum.indent_level();
+ let assoc_list = make.assoc_item_list(fn_items);
+ let new_impl = generate_impl_with_item(make, &parent_enum, Some(assoc_list));
+ editor.insert_all(
+ Position::after(parent_enum.syntax()),
+ vec![
+ make.whitespace(&format!("\n\n{indent}")).into(),
+ new_impl.syntax().clone().into(),
+ ],
+ );
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
+fn build_fn_item(
+ method: &Method,
+ vis: &str,
+ must_use: &str,
+ props: &ProjectionProps,
+) -> ast::AssocItem {
+ let Method { pattern_suffix, field_type, bound_name, fn_name, variant_name } = method;
+ let ProjectionProps { self_param, return_prefix, return_suffix, happy_case, sad_case, .. } =
+ props;
+ let fn_text = format!(
+ "{must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
+ if let Self::{variant_name}{pattern_suffix} = self {{
+ {happy_case}({bound_name})
+ }} else {{
+ {sad_case}
+ }}
+}}"
+ );
+ let wrapped = format!("impl X {{ {fn_text} }}");
+ let parse = syntax::SourceFile::parse(&wrapped, Edition::CURRENT);
+ let fn_ = parse
+ .tree()
+ .syntax()
+ .descendants()
+ .find_map(ast::Fn::cast)
+ .expect("fn text must produce a valid fn node");
+ ast::AssocItem::Fn(fn_.indent(1.into()))
+}
+
struct Method {
pattern_suffix: String,
field_type: ast::Type,
@@ -185,6 +214,7 @@ struct Method {
impl Method {
fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option<Self> {
+ use itertools::Itertools as _;
let variant_name = variant.name()?;
let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text()));
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs
index 3514ebb811..9b4d44d8b5 100644
--- a/crates/ide-assists/src/handlers/generate_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -59,12 +59,12 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>)
let InRealFile { file_id, value: enum_node } = e.source(db)?.original_ast_node_rooted(db)?;
acc.add(AssistId::generate("generate_enum_variant"), "Generate variant", target, |builder| {
- let mut editor = builder.make_editor(enum_node.syntax());
- let make = SyntaxFactory::with_mappings();
- let field_list = parent.make_field_list(ctx, &make);
+ let editor = builder.make_editor(enum_node.syntax());
+ let make = editor.make();
+ let field_list = parent.make_field_list(ctx, make);
let variant = make.variant(None, make.name(&name_ref.text()), field_list, None);
if let Some(it) = enum_node.variant_list() {
- it.add_variant(&mut editor, &variant);
+ it.add_variant(&editor, &variant);
}
builder.add_file_edits(file_id.file_id(ctx.db()), editor);
})
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 6bcbd9b0cc..55e5083811 100644
--- a/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
+++ b/crates/ide-assists/src/handlers/generate_fn_type_alias.rs
@@ -2,7 +2,7 @@ use either::Either;
use ide_db::assists::{AssistId, GroupLabel};
use syntax::{
AstNode,
- ast::{self, HasGenericParams, HasName, edit::IndentLevel, syntax_factory::SyntaxFactory},
+ ast::{self, HasGenericParams, HasName, edit::IndentLevel},
syntax_editor,
};
@@ -55,9 +55,8 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>)
style.label(),
func_node.syntax().text_range(),
|builder| {
- let mut edit = builder.make_editor(func);
- let make = SyntaxFactory::without_mappings();
-
+ let editor = builder.make_editor(func);
+ let make = editor.make();
let alias_name = format!("{}Fn", stdx::to_camel_case(&name.to_string()));
let mut fn_params_vec = Vec::new();
@@ -104,7 +103,7 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>)
);
let indent = IndentLevel::from_node(insertion_node);
- edit.insert_all(
+ editor.insert_all(
syntax_editor::Position::before(insertion_node),
vec![
ty_alias.syntax().clone().into(),
@@ -115,10 +114,10 @@ pub(crate) fn generate_fn_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>)
if let Some(cap) = ctx.config.snippet_cap
&& let Some(name) = ty_alias.name()
{
- edit.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap));
+ editor.add_annotation(name.syntax(), builder.make_placeholder_snippet(cap));
}
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
}
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 1adb3f4fe4..76246c3e8e 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
@@ -40,19 +40,18 @@ pub(crate) fn generate_from_impl_for_enum(
"Generate `From` impl for this enum variant(s)",
target,
|edit| {
- let make = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(adt.syntax());
+ let make = editor.make();
let indent = adt.indent_level();
let mut elements = Vec::new();
for variant_info in variants {
- let impl_ = build_from_impl(&make, &adt, variant_info).indent(indent);
+ let impl_ = build_from_impl(make, &adt, variant_info).indent(indent);
elements.push(make.whitespace(&format!("\n\n{indent}")).into());
elements.push(impl_.syntax().clone().into());
}
- let mut editor = edit.make_editor(adt.syntax());
editor.insert_all(Position::after(adt.syntax()), elements);
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(file_id, editor);
},
)
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index fbf6241e43..6ef492619b 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -1166,10 +1166,10 @@ fn next_space_for_fn_after_call_site(expr: ast::CallableExpr) -> Option<Generate
SyntaxKind::SOURCE_FILE => {
break;
}
- SyntaxKind::ITEM_LIST => {
- if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) {
- break;
- }
+ SyntaxKind::ITEM_LIST
+ if ancestors.peek().map(|a| a.kind()) == Some(SyntaxKind::MODULE) =>
+ {
+ break;
}
_ => {}
}
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 62ffd3d965..b884581041 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -410,35 +410,37 @@ fn parse_record_field(
Some(RecordFieldInfo { field_name, field_ty, fn_name, target })
}
-fn build_source_change(
- builder: &mut SourceChangeBuilder,
+fn items(
ctx: &AssistContext<'_>,
info_of_record_fields: Vec<RecordFieldInfo>,
- assist_info: AssistInfo,
-) {
- let syntax_factory = SyntaxFactory::without_mappings();
-
- let items: Vec<ast::AssocItem> = info_of_record_fields
+ assist_info: &AssistInfo,
+ make: &SyntaxFactory,
+) -> Vec<ast::AssocItem> {
+ info_of_record_fields
.iter()
.map(|record_field_info| {
let method = match assist_info.assist_type {
- AssistType::Set => {
- generate_setter_from_info(&assist_info, record_field_info, &syntax_factory)
- }
- _ => {
- generate_getter_from_info(ctx, &assist_info, record_field_info, &syntax_factory)
- }
+ AssistType::Set => generate_setter_from_info(assist_info, record_field_info, make),
+ _ => generate_getter_from_info(ctx, assist_info, record_field_info, make),
};
- let new_fn = method.clone_for_update();
+ let new_fn = method;
let new_fn = new_fn.indent(1.into());
new_fn.into()
})
- .collect();
+ .collect()
+}
+fn build_source_change(
+ builder: &mut SourceChangeBuilder,
+ ctx: &AssistContext<'_>,
+ info_of_record_fields: Vec<RecordFieldInfo>,
+ assist_info: AssistInfo,
+) {
if let Some(impl_def) = &assist_info.impl_def {
// We have an existing impl to add to
- let mut editor = builder.make_editor(impl_def.syntax());
- impl_def.assoc_item_list().unwrap().add_items(&mut editor, items.clone());
+ let editor = builder.make_editor(impl_def.syntax());
+ let items = items(ctx, info_of_record_fields, &assist_info, editor.make());
+ impl_def.assoc_item_list().unwrap().add_items(&editor, items.clone());
if let Some(cap) = ctx.config.snippet_cap
&& let Some(ast::AssocItem::Fn(fn_)) = items.last()
@@ -451,22 +453,23 @@ fn build_source_change(
builder.add_file_edits(ctx.vfs_file_id(), editor);
return;
}
+
+ let editor = builder.make_editor(assist_info.strukt.syntax());
+ 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 impl_def = syntax_factory.impl_(
+ let impl_def = make.impl_(
None,
ty_params,
ty_args,
- syntax_factory
- .ty_path(syntax_factory.ident_path(&assist_info.strukt.name().unwrap().to_string()))
- .into(),
+ make.ty_path(make.ident_path(&assist_info.strukt.name().unwrap().to_string())).into(),
None,
- Some(syntax_factory.assoc_item_list(items)),
+ Some(make.assoc_item_list(items)),
);
- let mut editor = builder.make_editor(assist_info.strukt.syntax());
editor.insert_all(
Position::after(assist_info.strukt.syntax()),
- vec![syntax_factory.whitespace("\n\n").into(), impl_def.syntax().clone().into()],
+ vec![make.whitespace("\n\n").into(), impl_def.syntax().clone().into()],
);
if let Some(cap) = ctx.config.snippet_cap
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index 2d1235792d..c5a46f6981 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,24 +1,18 @@
use syntax::{
- ast::{
- self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit, syntax_factory::SyntaxFactory,
- },
+ ast::{self, AstNode, HasGenericParams, HasName, edit::AstNodeEdit},
syntax_editor::{Position, SyntaxEditor},
};
use crate::{
AssistContext, AssistId, Assists,
utils::{
- self, DefaultMethods, IgnoreAssocItems, generate_impl_with_factory,
- generate_trait_impl_intransitive,
+ self, DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl,
+ generate_impl_with_factory, generate_trait_impl_intransitive,
},
};
-fn insert_impl(
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
- impl_: &ast::Impl,
- nominal: &impl AstNodeEdit,
-) -> ast::Impl {
+fn insert_impl(editor: &SyntaxEditor, impl_: &ast::Impl, nominal: &impl AstNodeEdit) -> ast::Impl {
+ let make = editor.make();
let indent = nominal.indent_level();
let impl_ = impl_.indent(indent);
@@ -65,13 +59,11 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
format!("Generate impl for `{name}`"),
target,
|edit| {
- let make = SyntaxFactory::with_mappings();
- // Generate the impl
- let impl_ = generate_impl_with_factory(&make, &nominal);
-
- let mut editor = edit.make_editor(nominal.syntax());
+ let editor = edit.make_editor(nominal.syntax());
+ let make = editor.make();
+ let impl_ = generate_impl_with_factory(make, &nominal);
- let impl_ = insert_impl(&mut editor, &make, &impl_, &nominal);
+ let impl_ = insert_impl(&editor, &impl_, &nominal);
// Add a tabstop after the left curly brace
if let Some(cap) = ctx.config.snippet_cap
&& let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token())
@@ -79,8 +71,6 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
let tabstop = edit.make_tabstop_after(cap);
editor.add_annotation(l_curly, tabstop);
}
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -117,13 +107,10 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
format!("Generate trait impl for `{name}`"),
target,
|edit| {
- let make = SyntaxFactory::with_mappings();
- // Generate the impl
- let impl_ = generate_trait_impl_intransitive(&make, &nominal, make.ty_placeholder());
-
- let mut editor = edit.make_editor(nominal.syntax());
-
- let impl_ = insert_impl(&mut editor, &make, &impl_, &nominal);
+ let editor = edit.make_editor(nominal.syntax());
+ let make = editor.make();
+ let impl_ = generate_trait_impl_intransitive(make, &nominal, make.ty_placeholder());
+ let impl_ = insert_impl(&editor, &impl_, &nominal);
// Make the trait type a placeholder snippet
if let Some(cap) = ctx.config.snippet_cap {
if let Some(trait_) = impl_.trait_() {
@@ -136,8 +123,6 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
editor.add_annotation(l_curly, tabstop);
}
}
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -176,8 +161,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
format!("Generate `{name}` impl for type"),
target,
|edit| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(trait_.syntax());
+ let editor = edit.make_editor(trait_.syntax());
+ let make = editor.make();
let holder_arg = ast::GenericArg::TypeArg(make.type_arg(make.ty_placeholder()));
let missing_items = utils::filter_assoc_items(
@@ -212,7 +197,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
make_impl_(None)
} else {
let impl_ = make_impl_(None);
- let assoc_items = utils::add_trait_assoc_items_to_impl(
+ let assoc_items = add_trait_assoc_items_to_impl(
+ make,
&ctx.sema,
ctx.config,
&missing_items,
@@ -224,8 +210,7 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) ->
make_impl_(Some(assoc_item_list))
};
- let impl_ = insert_impl(&mut editor, &make, &impl_, &trait_);
- editor.add_mappings(make.finish_with_mappings());
+ let impl_ = insert_impl(&editor, &impl_, &trait_);
if let Some(cap) = ctx.config.snippet_cap {
if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
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 3a62a8853e..acf0819222 100644
--- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -67,11 +67,9 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
target,
|edit| {
- let impl_clone = impl_def.reset_indent().clone_subtree();
- let mut editor = SyntaxEditor::new(impl_clone.syntax().clone());
- let factory = SyntaxFactory::without_mappings();
+ let (editor, impl_clone) = SyntaxEditor::with_ast_node(&impl_def.reset_indent());
- apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new);
+ apply_generate_mut_impl(&editor, &impl_clone, trait_new);
let new_root = editor.finish();
let new_root = new_root.new_root();
@@ -80,12 +78,13 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
let new_impl = new_impl.indent(indent);
- let mut editor = edit.make_editor(impl_def.syntax());
+ let editor = edit.make_editor(impl_def.syntax());
+ let make = editor.make();
editor.insert_all(
Position::before(impl_def.syntax()),
vec![
new_impl.syntax().syntax_element(),
- factory.whitespace(&format!("\n\n{indent}")).syntax_element(),
+ make.whitespace(&format!("\n\n{indent}")).syntax_element(),
],
);
@@ -99,7 +98,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
)
}
-fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) {
+fn delete_with_trivia(editor: &SyntaxEditor, node: &SyntaxNode) {
let mut end: SyntaxElement = node.clone().into();
if let Some(next) = node.next_sibling_or_token()
@@ -113,23 +112,23 @@ fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) {
}
fn apply_generate_mut_impl(
- editor: &mut SyntaxEditor,
- factory: &SyntaxFactory,
+ editor: &SyntaxEditor,
impl_def: &ast::Impl,
trait_new: &str,
) -> Option<()> {
+ let make = editor.make();
let path =
impl_def.trait_().and_then(|t| t.syntax().descendants().find_map(ast::Path::cast))?;
let seg = path.segment()?;
let name_ref = seg.name_ref()?;
- let new_name_ref = factory.name_ref(trait_new);
+ let new_name_ref = make.name_ref(trait_new);
editor.replace(name_ref.syntax(), new_name_ref.syntax());
if let Some((name, new_name)) =
impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name)
{
- let new_name_node = factory.name(new_name);
+ let new_name_node = make.name(new_name);
editor.replace(name.syntax(), new_name_node.syntax());
}
@@ -138,14 +137,14 @@ fn apply_generate_mut_impl(
}
if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) {
- let mut_self = factory.mut_self_param();
+ let mut_self = make.mut_self_param();
editor.replace(self_param.syntax(), mut_self.syntax());
}
if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast)
- && let Some(new_ty) = process_ret_type(factory, &ret_type)
+ && let Some(new_ty) = process_ret_type(make, &ret_type)
{
- let new_ret = factory.ret_type(new_ty);
+ let new_ret = make.ret_type(new_ty);
editor.replace(ret_type.syntax(), new_ret.syntax())
}
@@ -155,13 +154,14 @@ fn apply_generate_mut_impl(
_ => None,
})
}) {
- process_ref_mut(editor, factory, &fn_);
+ process_ref_mut(editor, &fn_);
}
Some(())
}
-fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast::Fn) {
+fn process_ref_mut(editor: &SyntaxEditor, fn_: &ast::Fn) {
+ let make = editor.make();
let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return };
let ast::Expr::RefExpr(ref_expr) = expr else { return };
@@ -172,8 +172,8 @@ fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast
let Some(amp) = ref_expr.amp_token() else { return };
- let mut_kw = factory.token(T![mut]);
- let space = factory.whitespace(" ");
+ let mut_kw = make.token(T![mut]);
+ let space = make.whitespace(" ");
editor.insert(Position::after(amp.clone()), space.syntax_element());
editor.insert(Position::after(amp), mut_kw.syntax_element());
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 301d13c095..520709adc5 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -3,10 +3,7 @@ use ide_db::{
use_trivial_constructor::use_trivial_constructor,
};
use syntax::{
- ast::{
- self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit,
- syntax_factory::SyntaxFactory,
- },
+ ast::{self, AstNode, HasName, HasVisibility, StructKind, edit::AstNodeEdit},
syntax_editor::Position,
};
@@ -38,11 +35,9 @@ use crate::{
// ```
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
-
- let make = SyntaxFactory::without_mappings();
- let field_list = match strukt.kind() {
+ let field_list: Vec<(String, ast::Type)> = match strukt.kind() {
StructKind::Record(named) => {
- named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::<Vec<_>>()
+ named.fields().filter_map(|f| Some((f.name()?.to_string(), f.ty()?))).collect()
}
StructKind::Tuple(tuple) => {
let mut name_generator = NameGenerator::default();
@@ -56,12 +51,12 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
ctx.db(),
ctx.edition(),
) {
- Some(name) => name,
- None => name_generator.suggest_name(&format!("_{i}")),
+ Some(name) => name.to_string(),
+ None => name_generator.suggest_name(&format!("_{i}")).to_string(),
};
- Some((make.name(name.as_str()), f.ty()?))
+ Some((name, ty))
})
- .collect::<Vec<_>>()
+ .collect()
}
StructKind::Unit => return None,
};
@@ -74,7 +69,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let target = strukt.syntax().text_range();
acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(strukt.syntax());
+ let make = editor.make();
+
let trivial_constructors = field_list
.iter()
.map(|(name, ty)| {
@@ -100,13 +97,13 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
edition,
)?;
- Some((make.name_ref(&name.text()), Some(expr)))
+ Some((make.name_ref(name), Some(expr)))
})
.collect::<Vec<_>>();
let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
if trivial_constructors[i].is_none() {
- Some(make.param(make.ident_pat(false, false, name.clone()).into(), ty.clone()))
+ Some(make.param(make.ident_pat(false, false, make.name(name)).into(), ty.clone()))
} else {
None
}
@@ -117,7 +114,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
if let Some(constructor) = trivial_constructors[i].clone() {
constructor
} else {
- (make.name_ref(&name.text()), None)
+ (make.name_ref(name), None)
}
});
@@ -158,8 +155,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
)
.indent(1.into());
- let mut editor = builder.make_editor(strukt.syntax());
-
// Get the node for set annotation
let contain_fn = if let Some(impl_def) = impl_def {
let fn_ = fn_.indent(impl_def.indent_level());
@@ -185,7 +180,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let indent_level = strukt.indent_level();
let list = make.assoc_item_list([ast::AssocItem::Fn(fn_)]);
let impl_def =
- generate_impl_with_item(&make, &ast::Adt::Struct(strukt.clone()), Some(list))
+ generate_impl_with_item(make, &ast::Adt::Struct(strukt.clone()), Some(list))
.indent(strukt.indent_level());
// Insert it after the adt
@@ -235,8 +230,6 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
editor.add_annotation(name.syntax(), tabstop_before);
}
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
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 d3022ceda3..10c009a2ea 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,14 +1,16 @@
-use ast::make;
use hir::next_solver::{DbInterner, TypingMode};
use hir::{HasCrate, ModuleDef, Semantics};
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,
};
-use syntax::syntax_editor::{Element, Position};
+use syntax::syntax_editor::{Position, SyntaxEditor};
use syntax::{
TokenText,
- ast::{self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit},
+ ast::{
+ self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit,
+ syntax_factory::SyntaxFactory,
+ },
};
use crate::{
@@ -78,47 +80,51 @@ pub(crate) fn generate_single_field_struct_from(
"Generate single field `From`",
strukt.syntax().text_range(),
|builder| {
+ let editor = builder.make_editor(strukt.syntax());
+ let make = editor.make();
+
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 trait_gen_args = Some(make::generic_arg_list([ast::GenericArg::TypeArg(
- make::type_arg(main_field_ty.clone()),
- )]));
+ let trait_gen_args = Some(make.generic_arg_list(
+ [ast::GenericArg::TypeArg(make.type_arg(main_field_ty.clone()))],
+ false,
+ ));
- let ty = make::ty(&strukt_name.text());
+ let ty = make.ty(&strukt_name.text());
let constructor =
- make_adt_constructor(names.as_deref(), constructors, &main_field_name);
- let body = make::block_expr([], Some(constructor));
+ make_adt_constructor(names.as_deref(), constructors, &main_field_name, make);
+ let body = make.block_expr([], Some(constructor));
- let fn_ = make::fn_(
- None,
- None,
- make::name("from"),
- None,
- None,
- make::param_list(
+ let fn_ = make
+ .fn_(
+ [],
None,
- [make::param(
- make::path_pat(make::path_from_text(&main_field_name)),
- main_field_ty,
- )],
- ),
- body,
- Some(make::ret_type(make::ty("Self"))),
- false,
- false,
- false,
- false,
- )
- .indent(1.into());
-
- let cfg_attrs = strukt
- .attrs()
- .filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
-
- let impl_ = make::impl_trait(
+ make.name("from"),
+ None,
+ None,
+ make.param_list(
+ None,
+ [make.param(
+ make.path_pat(make.path_from_text(&main_field_name)),
+ main_field_ty,
+ )],
+ ),
+ body,
+ Some(make.ret_type(make.ty("Self"))),
+ false,
+ false,
+ false,
+ false,
+ )
+ .indent_with_mapping(1.into(), make);
+
+ let cfg_attrs =
+ strukt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
+
+ let impl_ = make.impl_trait(
cfg_attrs,
false,
None,
@@ -126,28 +132,28 @@ pub(crate) fn generate_single_field_struct_from(
type_gen_params,
type_gen_args,
false,
- make::ty("From"),
+ make.ty("From"),
ty.clone(),
None,
ty_where_clause.map(|wc| wc.reset_indent()),
None,
- )
- .clone_for_update();
-
- impl_.get_or_create_assoc_item_list().add_item(fn_.into());
- let impl_ = impl_.indent(indent);
+ );
- let mut edit = builder.make_editor(strukt.syntax());
+ let (impl_editor, impl_root) = SyntaxEditor::with_ast_node(&impl_);
+ let assoc_list = impl_root.get_or_create_assoc_item_list_with_editor(&impl_editor);
+ assoc_list.add_items(&impl_editor, vec![fn_.into()]);
+ let impl_ = ast::Impl::cast(impl_editor.finish().new_root().clone())
+ .unwrap()
+ .indent_with_mapping(indent, make);
- edit.insert_all(
+ editor.insert_all(
Position::after(strukt.syntax()),
vec![
- make::tokens::whitespace(&format!("\n\n{indent}")).syntax_element(),
- impl_.syntax().syntax_element(),
+ make.whitespace(&format!("\n\n{indent}")).into(),
+ impl_.syntax().clone().into(),
],
);
-
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -156,19 +162,18 @@ fn make_adt_constructor(
names: Option<&[ast::Name]>,
constructors: Vec<Option<ast::Expr>>,
main_field_name: &TokenText<'_>,
+ make: &SyntaxFactory,
) -> ast::Expr {
if let Some(names) = names {
- let fields = make::record_expr_field_list(names.iter().zip(constructors).map(
- |(name, initializer)| {
- make::record_expr_field(make::name_ref(&name.text()), initializer)
- },
+ let fields = make.record_expr_field_list(names.iter().zip(constructors).map(
+ |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer),
));
- make::record_expr(make::path_from_text("Self"), fields).into()
+ make.record_expr(make.path_from_text("Self"), fields).into()
} else {
- let arg_list = make::arg_list(constructors.into_iter().map(|expr| {
- expr.unwrap_or_else(|| make::expr_path(make::path_from_text(main_field_name)))
+ let arg_list = make.arg_list(constructors.into_iter().map(|expr| {
+ expr.unwrap_or_else(|| make.expr_path(make.path_from_text(main_field_name)))
}));
- make::expr_call(make::expr_path(make::path_from_text("Self")), arg_list).into()
+ make.expr_call(make.expr_path(make.path_from_text("Self")), arg_list).into()
}
}
@@ -177,6 +182,7 @@ fn make_constructors(
module: hir::Module,
types: &[ast::Type],
) -> Vec<Option<ast::Expr>> {
+ let make = SyntaxFactory::without_mappings();
let (db, sema) = (ctx.db(), &ctx.sema);
let cfg = ctx.config.find_path_config(ctx.sema.is_nightly(module.krate(ctx.sema.db)));
types
@@ -184,7 +190,7 @@ fn make_constructors(
.map(|ty| {
let ty = sema.resolve_type(ty)?;
if ty.is_unit() {
- return Some(make::expr_tuple([]).into());
+ return Some(make.expr_tuple([]).into());
}
let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into();
let edition = module.krate(db).edition(db);
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 1286abe356..049398de8c 100644
--- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -3,7 +3,8 @@ use ide_db::assists::AssistId;
use syntax::{
AstNode, AstToken, SyntaxKind, T,
ast::{
- self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, make,
+ self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit,
+ syntax_factory::SyntaxFactory,
},
syntax_editor::{Position, SyntaxEditor},
};
@@ -98,44 +99,45 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
impl_ast.syntax().text_range(),
|builder| {
let trait_items: ast::AssocItemList = {
- let trait_items = impl_assoc_items.clone_subtree();
- let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone());
+ let (trait_items_editor, trait_items) =
+ SyntaxEditor::with_ast_node(&impl_assoc_items);
trait_items.assoc_items().for_each(|item| {
- strip_body(&mut trait_items_editor, &item);
- remove_items_visibility(&mut trait_items_editor, &item);
+ strip_body(&trait_items_editor, &item);
+ remove_items_visibility(&trait_items_editor, &item);
});
ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap()
};
- let trait_ast = make::trait_(
+
+ let editor = builder.make_editor(impl_ast.syntax());
+ let make = editor.make();
+ let trait_ast = make.trait_(
false,
- &trait_name(&impl_assoc_items).text(),
+ &trait_name(&impl_assoc_items, make).text(),
impl_ast.generic_param_list(),
impl_ast.where_clause(),
trait_items,
- )
- .clone_for_update();
+ );
let trait_name = trait_ast.name().expect("new trait should have a name");
- let trait_name_ref = make::name_ref(&trait_name.to_string()).clone_for_update();
+ let trait_name_ref = make.name_ref(&trait_name.to_string());
// Change `impl Foo` to `impl NewTrait for Foo`
let mut elements = vec![
trait_name_ref.syntax().clone().into(),
- make::tokens::single_space().into(),
- make::token(T![for]).into(),
- make::tokens::single_space().into(),
+ make.whitespace(" ").into(),
+ make.token(T![for]).into(),
+ make.whitespace(" ").into(),
];
if let Some(params) = impl_ast.generic_param_list() {
- let gen_args = &params.to_generic_args().clone_for_update();
+ let gen_args = &params.to_generic_args();
elements.insert(1, gen_args.syntax().clone().into());
}
- let mut editor = builder.make_editor(impl_ast.syntax());
impl_assoc_items.assoc_items().for_each(|item| {
- remove_items_visibility(&mut editor, &item);
- remove_doc_comments(&mut editor, &item);
+ remove_items_visibility(&editor, &item);
+ remove_doc_comments(&editor, &item);
});
editor.insert_all(Position::before(impl_name.syntax()), elements);
@@ -145,7 +147,7 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
Position::before(impl_ast.syntax()),
vec![
trait_ast.syntax().clone().into(),
- make::tokens::whitespace(&format!("\n\n{}", impl_ast.indent_level())).into(),
+ make.whitespace(&format!("\n\n{}", impl_ast.indent_level())).into(),
],
);
@@ -155,7 +157,6 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
editor.add_annotation(trait_name.syntax(), placeholder);
editor.add_annotation(trait_name_ref.syntax(), placeholder);
}
-
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -163,20 +164,20 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_
Some(())
}
-fn trait_name(items: &ast::AssocItemList) -> ast::Name {
+fn trait_name(items: &ast::AssocItemList, make: &SyntaxFactory) -> ast::Name {
let mut fn_names = items
.assoc_items()
.filter_map(|x| if let ast::AssocItem::Fn(f) = x { f.name() } else { None });
fn_names
.next()
.and_then(|name| {
- fn_names.next().is_none().then(|| make::name(&stdx::to_camel_case(&name.text())))
+ fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(&name.text())))
})
- .unwrap_or_else(|| make::name("NewTrait"))
+ .unwrap_or_else(|| make.name("NewTrait"))
}
/// `E0449` Trait items always share the visibility of their trait
-fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
+fn remove_items_visibility(editor: &SyntaxEditor, item: &ast::AssocItem) {
if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
if let Some(vis) = has_vis.visibility()
&& let Some(token) = vis.syntax().next_sibling_or_token()
@@ -190,7 +191,7 @@ fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
}
}
-fn remove_doc_comments(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
+fn remove_doc_comments(editor: &SyntaxEditor, item: &ast::AssocItem) {
for doc in item.doc_comments() {
if let Some(next) = doc.syntax().next_token()
&& next.kind() == SyntaxKind::WHITESPACE
@@ -201,7 +202,8 @@ fn remove_doc_comments(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
}
}
-fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
+fn strip_body(editor: &SyntaxEditor, item: &ast::AssocItem) {
+ let make = editor.make();
if let ast::AssocItem::Fn(f) = item
&& let Some(body) = f.body()
{
@@ -213,7 +215,7 @@ fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
editor.delete(prev);
}
- editor.replace(body.syntax(), make::tokens::semicolon());
+ editor.replace(body.syntax(), make.token(T![;]));
};
}
diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs
index f55ef4229e..2af074f1fc 100644
--- a/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -7,7 +7,7 @@ use ide_db::{
};
use syntax::{
Direction, TextRange,
- ast::{self, AstNode, AstToken, HasName, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, AstToken, HasName},
syntax_editor::{Element, SyntaxEditor},
};
@@ -83,7 +83,8 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
"Inline variable",
target.text_range(),
move |builder| {
- let mut editor = builder.make_editor(&target);
+ let editor = builder.make_editor(&target);
+ let make = editor.make();
if delete_let {
editor.delete(let_stmt.syntax());
@@ -91,15 +92,13 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
&& let Some(op_token) = bin_expr.op_token()
{
editor.delete(&op_token);
- remove_whitespace(op_token, Direction::Prev, &mut editor);
- remove_whitespace(let_stmt.syntax(), Direction::Prev, &mut editor);
+ remove_whitespace(op_token, Direction::Prev, &editor);
+ remove_whitespace(let_stmt.syntax(), Direction::Prev, &editor);
} else {
- remove_whitespace(let_stmt.syntax(), Direction::Next, &mut editor);
+ remove_whitespace(let_stmt.syntax(), Direction::Next, &editor);
}
}
- let make = SyntaxFactory::with_mappings();
-
for (name, should_wrap) in wrap_in_parens {
let replacement = if should_wrap {
make.expr_paren(initializer_expr.clone()).into()
@@ -115,8 +114,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>)
editor.replace(name.syntax(), replacement.syntax());
}
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -204,7 +201,7 @@ fn inline_usage(
Some(InlineData { let_stmt, delete_let, target: ast::NameOrNameRef::NameRef(name), references })
}
-fn remove_whitespace(elem: impl Element, dir: Direction, editor: &mut SyntaxEditor) {
+fn remove_whitespace(elem: impl Element, dir: Direction, editor: &SyntaxEditor) {
let token = match elem.syntax_element() {
syntax::NodeOrToken::Node(node) => match dir {
Direction::Next => node.last_token(),
@@ -1054,6 +1051,7 @@ fn f() {
check_assist(
inline_local_variable,
r#"
+//- minicore: fn
fn main() {
let $0f = || 2;
let _ = f();
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs
index f3ebe61078..6d8750afdc 100644
--- a/crates/ide-assists/src/handlers/inline_type_alias.rs
+++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -70,7 +70,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
let source = ctx.sema.parse(file_id);
- let mut editor = builder.make_editor(source.syntax());
+ let editor = builder.make_editor(source.syntax());
let (path_types, path_type_uses) =
split_refs_and_uses(builder, refs, |path_type| {
@@ -101,7 +101,7 @@ pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>)
inline_refs_for_file(file_id, refs);
}
if !definition_deleted {
- let mut editor = builder.make_editor(ast_alias.syntax());
+ let editor = builder.make_editor(ast_alias.syntax());
editor.delete(ast_alias.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor)
}
@@ -156,7 +156,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
"Inline type alias",
alias_instance.syntax().text_range(),
|builder| {
- let mut editor = builder.make_editor(alias_instance.syntax());
+ let editor = builder.make_editor(alias_instance.syntax());
let replace = replacement.replace_generic(&concrete_type);
editor.replace(alias_instance.syntax(), replace);
builder.add_file_edits(ctx.vfs_file_id(), editor);
@@ -170,7 +170,7 @@ impl Replacement {
Replacement::Generic { lifetime_map, const_and_type_map } => {
create_replacement(lifetime_map, const_and_type_map, concrete_type)
}
- Replacement::Plain => concrete_type.syntax().clone_subtree().clone_for_update(),
+ Replacement::Plain => concrete_type.syntax().clone(),
}
}
}
@@ -312,9 +312,8 @@ fn create_replacement(
const_and_type_map: &ConstAndTypeMap,
concrete_type: &ast::Type,
) -> SyntaxNode {
- let updated_concrete_type = concrete_type.syntax().clone_subtree();
- let mut editor = SyntaxEditor::new(updated_concrete_type.clone());
-
+ let (editor, updated_concrete_type) = SyntaxEditor::new(concrete_type.syntax().clone());
+ let make = editor.make();
let mut replacements: Vec<(SyntaxNode, SyntaxNode)> = Vec::new();
let mut removals: Vec<NodeOrToken<SyntaxNode, _>> = Vec::new();
@@ -361,7 +360,7 @@ fn create_replacement(
continue;
}
- replacements.push((syntax.clone(), new_lifetime.syntax().clone_for_update()));
+ replacements.push((syntax.clone(), new_lifetime.syntax().clone()));
}
} else if let Some(name_ref) = ast::NameRef::cast(syntax.clone()) {
let Some(replacement_syntax) = const_and_type_map.0.get(&name_ref.to_string()) else {
@@ -369,7 +368,6 @@ fn create_replacement(
};
let new_string = replacement_syntax.to_string();
let new = if new_string == "_" {
- let make = SyntaxFactory::without_mappings();
make.wildcard_pat().syntax().clone()
} else {
replacement_syntax.clone()
@@ -449,15 +447,12 @@ impl ConstOrTypeGeneric {
}
fn replacement_value(&self) -> Option<SyntaxNode> {
- Some(
- match self {
- ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(),
- ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(),
- ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(),
- ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(),
- }
- .clone_for_update(),
- )
+ Some(match self {
+ ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(),
+ ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(),
+ ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(),
+ ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(),
+ })
}
}
diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
index 854e9561d2..2cbeae1d19 100644
--- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs
@@ -1,7 +1,7 @@
use ide_db::{FileId, FxHashSet};
use syntax::{
AstNode, SmolStr, T, TextRange, ToSmolStr,
- ast::{self, HasGenericParams, HasName, syntax_factory::SyntaxFactory},
+ ast::{self, HasGenericParams, HasName},
format_smolstr,
syntax_editor::{Element, Position, SyntaxEditor},
};
@@ -97,24 +97,23 @@ fn generate_fn_def_assist(
};
acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| {
- let root = fn_def.syntax().ancestors().last().unwrap().clone();
- let mut editor = SyntaxEditor::new(root);
- let factory = SyntaxFactory::with_mappings();
+ let editor = edit.make_editor(fn_def.syntax());
+ let make = editor.make();
if let Some(generic_list) = fn_def.generic_param_list() {
- insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name);
+ insert_lifetime_param(&editor, &generic_list, &new_lifetime_name);
} else {
- insert_new_generic_param_list_fn(&mut editor, &factory, &fn_def, &new_lifetime_name);
+ insert_new_generic_param_list_fn(&editor, &fn_def, &new_lifetime_name);
}
- editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax());
+ editor.replace(lifetime.syntax(), make.lifetime(&new_lifetime_name).syntax());
if let Some(pos) = loc_needing_lifetime.and_then(|l| l.to_position()) {
editor.insert_all(
pos,
vec![
- factory.lifetime(&new_lifetime_name).syntax().clone().into(),
- factory.whitespace(" ").into(),
+ make.lifetime(&new_lifetime_name).syntax().clone().into(),
+ make.whitespace(" ").into(),
],
);
}
@@ -124,19 +123,19 @@ fn generate_fn_def_assist(
}
fn insert_new_generic_param_list_fn(
- editor: &mut SyntaxEditor,
- factory: &SyntaxFactory,
+ editor: &SyntaxEditor,
fn_def: &ast::Fn,
lifetime_name: &str,
) -> Option<()> {
+ let make = editor.make();
let name = fn_def.name()?;
editor.insert_all(
Position::after(name.syntax()),
vec![
- factory.token(T![<]).syntax_element(),
- factory.lifetime(lifetime_name).syntax().syntax_element(),
- factory.token(T![>]).syntax_element(),
+ make.token(T![<]).syntax_element(),
+ make.lifetime(lifetime_name).syntax().syntax_element(),
+ make.token(T![>]).syntax_element(),
],
);
@@ -167,36 +166,35 @@ fn generate_impl_def_assist(
let new_lifetime_name = generate_unique_lifetime_param_name(impl_def.generic_param_list())?;
acc.add(AssistId::refactor(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |edit| {
- let root = impl_def.syntax().ancestors().last().unwrap().clone();
- let mut editor = SyntaxEditor::new(root);
- let factory = SyntaxFactory::without_mappings();
+ let editor = edit.make_editor(impl_def.syntax());
+ let make = editor.make();
if let Some(generic_list) = impl_def.generic_param_list() {
- insert_lifetime_param(&mut editor, &factory, &generic_list, &new_lifetime_name);
+ insert_lifetime_param(&editor, &generic_list, &new_lifetime_name);
} else {
- insert_new_generic_param_list_imp(&mut editor, &factory, &impl_def, &new_lifetime_name);
+ insert_new_generic_param_list_imp(&editor, &impl_def, &new_lifetime_name);
}
- editor.replace(lifetime.syntax(), factory.lifetime(&new_lifetime_name).syntax());
+ editor.replace(lifetime.syntax(), make.lifetime(&new_lifetime_name).syntax());
edit.add_file_edits(file_id, editor);
})
}
fn insert_new_generic_param_list_imp(
- editor: &mut SyntaxEditor,
- factory: &SyntaxFactory,
+ editor: &SyntaxEditor,
impl_: &ast::Impl,
lifetime_name: &str,
) -> Option<()> {
+ let make = editor.make();
let impl_kw = impl_.impl_token()?;
editor.insert_all(
Position::after(impl_kw),
vec![
- factory.token(T![<]).syntax_element(),
- factory.lifetime(lifetime_name).syntax().syntax_element(),
- factory.token(T![>]).syntax_element(),
+ make.token(T![<]).syntax_element(),
+ make.lifetime(lifetime_name).syntax().syntax_element(),
+ make.token(T![>]).syntax_element(),
],
);
@@ -204,22 +202,22 @@ fn insert_new_generic_param_list_imp(
}
fn insert_lifetime_param(
- editor: &mut SyntaxEditor,
- factory: &SyntaxFactory,
+ editor: &SyntaxEditor,
generic_list: &ast::GenericParamList,
lifetime_name: &str,
) -> Option<()> {
+ let make = editor.make();
let r_angle = generic_list.r_angle_token()?;
let needs_comma = generic_list.generic_params().next().is_some();
let mut elements = Vec::new();
if needs_comma {
- elements.push(factory.token(T![,]).syntax_element());
- elements.push(factory.whitespace(" ").syntax_element());
+ elements.push(make.token(T![,]).syntax_element());
+ elements.push(make.whitespace(" ").syntax_element());
}
- let lifetime = factory.lifetime(lifetime_name);
+ let lifetime = make.lifetime(lifetime_name);
elements.push(lifetime.syntax().clone().into());
editor.insert_all(Position::before(r_angle), elements);
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 db51070a64..95f223420b 100644
--- a/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
+++ b/crates/ide-assists/src/handlers/introduce_named_type_parameter.rs
@@ -1,6 +1,6 @@
use ide_db::syntax_helpers::suggest_name;
use itertools::Itertools;
-use syntax::ast::{self, AstNode, HasGenericParams, HasName, syntax_factory::SyntaxFactory};
+use syntax::ast::{self, AstNode, HasGenericParams, HasName};
use crate::{AssistContext, AssistId, Assists};
@@ -24,14 +24,14 @@ pub(crate) fn introduce_named_type_parameter(
let fn_ = param.syntax().ancestors().nth(2).and_then(ast::Fn::cast)?;
let type_bound_list = impl_trait_type.type_bound_list()?;
- let make = SyntaxFactory::with_mappings();
let target = fn_.syntax().text_range();
acc.add(
AssistId::refactor_rewrite("introduce_named_type_parameter"),
"Replace impl trait with type parameter",
target,
|builder| {
- let mut editor = builder.make_editor(fn_.syntax());
+ let editor = builder.make_editor(fn_.syntax());
+ let make = editor.make();
let existing_names = match fn_.generic_param_list() {
Some(generic_param_list) => generic_param_list
@@ -58,7 +58,6 @@ pub(crate) fn introduce_named_type_parameter(
editor.add_annotation(type_param.syntax(), builder.make_tabstop_before(cap));
}
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/merge_imports.rs b/crates/ide-assists/src/handlers/merge_imports.rs
index 42bc05811f..1dd0833fad 100644
--- a/crates/ide-assists/src/handlers/merge_imports.rs
+++ b/crates/ide-assists/src/handlers/merge_imports.rs
@@ -4,11 +4,7 @@ use ide_db::imports::{
merge_imports::{MergeBehavior, try_merge_imports, try_merge_trees},
};
use syntax::{
- AstNode, SyntaxElement, SyntaxNode,
- algo::neighbor,
- ast::{self, syntax_factory::SyntaxFactory},
- match_ast,
- syntax_editor::Removable,
+ AstNode, SyntaxElement, SyntaxNode, algo::neighbor, ast, match_ast, syntax_editor::Removable,
};
use crate::{
@@ -76,17 +72,16 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
};
acc.add(AssistId::refactor_rewrite("merge_imports"), "Merge imports", target, |builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(&parent_node);
+ let editor = builder.make_editor(&parent_node);
for edit in edits {
match edit {
Remove(it) => {
let node = it.as_ref();
if let Some(left) = node.left() {
- left.remove(&mut editor);
+ left.remove(&editor);
} else if let Some(right) = node.right() {
- right.remove(&mut editor);
+ right.remove(&editor);
}
}
Replace(old, new) => {
@@ -94,7 +89,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
}
}
}
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
diff --git a/crates/ide-assists/src/handlers/move_bounds.rs b/crates/ide-assists/src/handlers/move_bounds.rs
index 79b8bd5d3d..e044068ff7 100644
--- a/crates/ide-assists/src/handlers/move_bounds.rs
+++ b/crates/ide-assists/src/handlers/move_bounds.rs
@@ -45,22 +45,21 @@ pub(crate) fn move_bounds_to_where_clause(
"Move to where clause",
target,
|builder| {
- let mut edit = builder.make_editor(&parent);
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(&parent);
let new_preds: Vec<ast::WherePred> = type_param_list
.generic_params()
- .filter_map(|param| build_predicate(param, &make))
+ .filter_map(|param| build_predicate(param, editor.make()))
.collect();
match_ast! {
match (&parent) {
- ast::Fn(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
- ast::Trait(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
- ast::Impl(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
- ast::Enum(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
- ast::Struct(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
- ast::TypeAlias(it) => it.get_or_create_where_clause(&mut edit, &make, new_preds.into_iter()),
+ ast::Fn(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
+ ast::Trait(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
+ ast::Impl(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
+ ast::Enum(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
+ ast::Struct(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
+ ast::TypeAlias(it) => it.get_or_create_where_clause(&editor, new_preds.into_iter()),
_ => return,
}
};
@@ -72,11 +71,11 @@ pub(crate) fn move_bounds_to_where_clause(
ast::GenericParam::ConstParam(_) => continue,
};
if let Some(tbl) = param.type_bound_list() {
- tbl.remove(&mut edit);
+ tbl.remove(&editor);
}
}
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
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 b3e79e4663..86bdf3f8b4 100644
--- a/crates/ide-assists/src/handlers/move_const_to_impl.rs
+++ b/crates/ide-assists/src/handlers/move_const_to_impl.rs
@@ -138,7 +138,6 @@ pub(crate) fn move_const_to_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let fixup = if last_const.is_none() { "\n" } else { "" };
let indent = IndentLevel::from_node(parent_fn.syntax());
- let const_ = const_.clone_for_update();
let const_ = const_.reset_indent();
let const_ = const_.indent(indent);
builder.insert(insert_offset, format!("\n{indent}{const_}{fixup}"));
diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs
index 80587372e5..7309cc6d06 100644
--- a/crates/ide-assists/src/handlers/move_guard.rs
+++ b/crates/ide-assists/src/handlers/move_guard.rs
@@ -73,24 +73,24 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
"Move guard to arm body",
target,
|builder| {
- let mut edit = builder.make_editor(match_arm.syntax());
+ let editor = builder.make_editor(match_arm.syntax());
for element in space_before_delete {
if element.kind() == WHITESPACE {
- edit.delete(element);
+ editor.delete(element);
}
}
for rest_arm in &rest_arms {
- edit.delete(rest_arm.syntax());
+ editor.delete(rest_arm.syntax());
}
if let Some(element) = space_after_arrow
&& element.kind() == WHITESPACE
{
- edit.replace(element, make.whitespace(" "));
+ editor.replace(element, make.whitespace(" "));
}
- edit.delete(guard.syntax());
- edit.replace(arm_expr.syntax(), if_expr.syntax());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ editor.delete(guard.syntax());
+ editor.replace(arm_expr.syntax(), if_expr.syntax());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
@@ -156,7 +156,8 @@ pub(crate) fn move_arm_cond_to_match_guard(
"Move condition to match guard",
replace_node.text_range(),
|builder| {
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(match_arm.syntax());
+ let make = editor.make();
let mut replace_arms = vec![];
// Dedent if if_expr is in a BlockExpr
@@ -227,14 +228,12 @@ pub(crate) fn move_arm_cond_to_match_guard(
}
}
- let mut edit = builder.make_editor(match_arm.syntax());
-
let newline = make.whitespace(&format!("\n{indent_level}"));
let replace_arms = replace_arms.iter().map(|it| it.syntax().syntax_element());
let replace_arms = Itertools::intersperse(replace_arms, newline.syntax_element());
- edit.replace_with_many(match_arm.syntax(), replace_arms.collect());
+ editor.replace_with_many(match_arm.syntax(), replace_arms.collect());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
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 483c90d103..ed61d32eb6 100644
--- a/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -3,7 +3,7 @@ use ide_db::{assists::AssistId, defs::Definition};
use stdx::to_upper_snake_case;
use syntax::{
AstNode,
- ast::{self, HasName, syntax_factory::SyntaxFactory},
+ ast::{self, HasName},
};
use crate::{
@@ -68,8 +68,8 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
"Promote local to constant",
let_stmt.syntax().text_range(),
|edit| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(let_stmt.syntax());
+ let editor = edit.make_editor(let_stmt.syntax());
+ let make = editor.make();
let name = to_upper_snake_case(&name.to_string());
let usages = Definition::Local(local).usages(&ctx.sema).all();
if let Some(usages) = usages.references.get(&ctx.file_id()) {
@@ -97,7 +97,6 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
editor.replace(let_stmt.syntax(), item.syntax());
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/pull_assignment_up.rs b/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 812ebf6c6e..082052c9d4 100644
--- a/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -1,10 +1,5 @@
use either::Either;
-use syntax::{
- AstNode,
- algo::find_node_at_range,
- ast::{self, syntax_factory::SyntaxFactory},
- syntax_editor::SyntaxEditor,
-};
+use syntax::{AstNode, algo::find_node_at_range, ast, syntax_editor::SyntaxEditor};
use crate::{
AssistId,
@@ -75,7 +70,7 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
}
let target = tgt.syntax().text_range();
- let edit_tgt = tgt.syntax().clone_subtree();
+ let (editor, edit_tgt) = SyntaxEditor::new(tgt.syntax().clone());
let assignments: Vec<_> = collector
.assignments
.into_iter()
@@ -93,7 +88,6 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
})
.collect();
- let mut editor = SyntaxEditor::new(edit_tgt);
for (stmt, rhs) in assignments {
let mut stmt = stmt.syntax().clone();
if let Some(parent) = stmt.parent()
@@ -110,13 +104,12 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
"Pull assignment up",
target,
move |edit| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(tgt.syntax());
+ let editor = edit.make_editor(tgt.syntax());
+ let make = editor.make();
let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone());
let assign_stmt = make.expr_stmt(assign_expr.into());
editor.replace(tgt.syntax(), assign_stmt.syntax());
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs
index 8b9e6570e9..d7885d5065 100644
--- a/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -1,6 +1,6 @@
use hir::{AsAssocItem, AssocItem, AssocItemContainer, ItemInNs, ModuleDef, db::HirDatabase};
use ide_db::assists::AssistId;
-use syntax::{AstNode, ast, ast::syntax_factory::SyntaxFactory};
+use syntax::{AstNode, ast};
use crate::{
assist_context::{AssistContext, Assists},
@@ -59,17 +59,8 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
format!("Qualify `{ident}` method call"),
range,
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(call.syntax());
- qualify_candidate.qualify(
- |_| {},
- &mut editor,
- &make,
- &receiver_path,
- item_in_ns,
- current_edition,
- );
- editor.add_mappings(make.finish_with_mappings());
+ let editor = builder.make_editor(call.syntax());
+ qualify_candidate.qualify(|_| {}, &editor, &receiver_path, item_in_ns, current_edition);
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs
index c059f758c4..e3dd77360c 100644
--- a/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/crates/ide-assists/src/handlers/qualify_path.rs
@@ -9,11 +9,7 @@ use ide_db::{
};
use syntax::Edition;
use syntax::ast::HasGenericArgs;
-use syntax::{
- AstNode, ast,
- ast::{HasArgList, syntax_factory::SyntaxFactory},
- syntax_editor::SyntaxEditor,
-};
+use syntax::{AstNode, ast, ast::HasArgList, syntax_editor::SyntaxEditor};
use crate::{
AssistId, GroupLabel,
@@ -102,17 +98,14 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
label(ctx.db(), candidate, &import, current_edition),
range,
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(&syntax_under_caret);
+ let editor = builder.make_editor(&syntax_under_caret);
qualify_candidate.qualify(
|replace_with: String| builder.replace(range, replace_with),
- &mut editor,
- &make,
+ &editor,
&import.import_path,
import.item_to_import,
current_edition,
);
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -131,8 +124,7 @@ impl QualifyCandidate<'_> {
pub(crate) fn qualify(
&self,
mut replacer: impl FnMut(String),
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
import: &hir::ModPath,
item: hir::ItemInNs,
edition: Edition,
@@ -151,10 +143,10 @@ impl QualifyCandidate<'_> {
replacer(format!("<{qualifier} as {import}>::{segment}"));
}
QualifyCandidate::TraitMethod(db, mcall_expr) => {
- Self::qualify_trait_method(db, mcall_expr, editor, make, import, item);
+ Self::qualify_trait_method(db, mcall_expr, editor, import, item);
}
QualifyCandidate::ImplMethod(db, mcall_expr, hir_fn) => {
- Self::qualify_fn_call(db, mcall_expr, editor, make, import, hir_fn);
+ Self::qualify_fn_call(db, mcall_expr, editor, import, hir_fn);
}
}
}
@@ -162,11 +154,11 @@ impl QualifyCandidate<'_> {
fn qualify_fn_call(
db: &RootDatabase,
mcall_expr: &ast::MethodCallExpr,
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
import: ast::Path,
hir_fn: &hir::Function,
) -> Option<()> {
+ let make = editor.make();
let receiver = mcall_expr.receiver()?;
let method_name = mcall_expr.name_ref()?;
let generics =
@@ -193,15 +185,14 @@ impl QualifyCandidate<'_> {
fn qualify_trait_method(
db: &RootDatabase,
mcall_expr: &ast::MethodCallExpr,
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
import: ast::Path,
item: hir::ItemInNs,
) -> Option<()> {
let trait_method_name = mcall_expr.name_ref()?;
let trait_ = item_as_trait(db, item)?;
let method = find_trait_method(db, trait_, &trait_method_name)?;
- Self::qualify_fn_call(db, mcall_expr, editor, make, import, &method)
+ Self::qualify_fn_call(db, mcall_expr, editor, import, &method)
}
}
diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs
index d6d99b8b6d..8234a0374e 100644
--- a/crates/ide-assists/src/handlers/raw_string.rs
+++ b/crates/ide-assists/src/handlers/raw_string.rs
@@ -1,7 +1,7 @@
use ide_db::source_change::SourceChangeBuilder;
use syntax::{
- AstToken,
- ast::{self, IsString, make::tokens::literal},
+ AstNode, AstToken,
+ ast::{self, IsString},
};
use crate::{
@@ -162,23 +162,18 @@ fn replace_literal(
builder: &mut SourceChangeBuilder,
ctx: &AssistContext<'_>,
) {
- let token = token.syntax();
- let node = token.parent().expect("no parent token");
- let mut edit = builder.make_editor(&node);
- let new_literal = literal(new);
-
- edit.replace(token, mut_token(new_literal));
-
- builder.add_file_edits(ctx.vfs_file_id(), edit);
-}
-
-fn mut_token(token: syntax::SyntaxToken) -> syntax::SyntaxToken {
- let node = token.parent().expect("no parent token");
- node.clone_for_update()
- .children_with_tokens()
- .filter_map(|it| it.into_token())
- .find(|it| it.text_range() == token.text_range() && it.text() == token.text())
- .unwrap()
+ let old_token = token.syntax();
+ let parent = old_token.parent().expect("no parent token");
+ let editor = builder.make_editor(&parent);
+ let make = editor.make();
+ let new_literal = make.expr_literal(new);
+ let new_token = new_literal
+ .syntax()
+ .first_child_or_token()
+ .and_then(|it| it.into_token())
+ .expect("literal has no token child");
+ editor.replace(old_token, new_token);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
}
#[cfg(test)]
diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs
index 08779a3ed1..778533be5a 100644
--- a/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,7 +1,7 @@
use itertools::Itertools;
use syntax::{
Edition, NodeOrToken, SyntaxNode, SyntaxToken, T,
- ast::{self, AstNode, make},
+ ast::{self, AstNode, syntax_factory::SyntaxFactory},
match_ast,
syntax_editor::{Position, SyntaxEditor},
};
@@ -24,6 +24,8 @@ use crate::{AssistContext, AssistId, Assists};
// }
// ```
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() {
vec![ctx.find_node_at_offset::<ast::MacroExpr>()?]
} else {
@@ -39,18 +41,19 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
.collect()
};
- let replacements =
- macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
+ let replacements = macro_calls
+ .into_iter()
+ .filter_map(|macro_expr| compute_dbg_replacement(macro_expr, make))
+ .collect::<Vec<_>>();
let target = replacements
.iter()
.flat_map(|(node_or_token, _)| node_or_token.iter())
.map(|t| t.text_range())
.reduce(|acc, range| acc.cover(range))?;
acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| {
- let mut editor = builder.make_editor(ctx.source_file().syntax());
for (range, expr) in replacements {
if let Some(expr) = expr {
- editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update());
+ editor.insert(Position::before(range[0].clone()), expr.syntax());
}
for node_or_token in range {
editor.delete(node_or_token);
@@ -68,6 +71,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
/// Returns `Some(_, None)` when the macro call should just be removed.
fn compute_dbg_replacement(
macro_expr: ast::MacroExpr,
+ make: &SyntaxFactory,
) -> Option<(Vec<NodeOrToken<SyntaxNode, SyntaxToken>>, Option<ast::Expr>)> {
let macro_call = macro_expr.macro_call()?;
let tt = macro_call.token_tree()?;
@@ -110,7 +114,7 @@ fn compute_dbg_replacement(
}
(replace, None)
},
- _ => (vec![macro_call.syntax().clone().into()], Some(make::ext::expr_unit())),
+ _ => (vec![macro_call.syntax().clone().into()], Some(make.expr_unit())),
}
}
}
@@ -162,14 +166,14 @@ fn compute_dbg_replacement(
},
None => false,
};
- let expr = replace_nested_dbgs(expr.clone());
- let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
+ let expr = replace_nested_dbgs(expr.clone(), make);
+ let expr = if wrap { make.expr_paren(expr).into() } else { expr };
(vec![macro_call.syntax().clone().into()], Some(expr))
}
// dbg!(expr0, expr1, ...)
exprs => {
- let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
- let expr = make::expr_tuple(exprs);
+ let exprs = exprs.iter().cloned().map(|expr| replace_nested_dbgs(expr, make));
+ let expr = make.expr_tuple(exprs);
(vec![macro_call.syntax().clone().into()], Some(expr.into()))
}
})
@@ -189,12 +193,12 @@ fn pure_expr(expr: &ast::Expr) -> bool {
}
}
-fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
+fn replace_nested_dbgs(expanded: ast::Expr, make: &SyntaxFactory) -> ast::Expr {
if let ast::Expr::MacroExpr(mac) = &expanded {
// Special-case when `expanded` itself is `dbg!()` since we cannot replace the whole tree
// with `ted`. It should be fairly rare as it means the user wrote `dbg!(dbg!(..))` but you
// never know how code ends up being!
- let replaced = if let Some((_, expr_opt)) = compute_dbg_replacement(mac.clone()) {
+ let replaced = if let Some((_, expr_opt)) = compute_dbg_replacement(mac.clone(), make) {
match expr_opt {
Some(expr) => expr,
None => {
@@ -209,20 +213,19 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
return replaced;
}
- let expanded = expanded.clone_subtree();
- let mut editor = SyntaxEditor::new(expanded.syntax().clone());
+ let (editor, expanded) = SyntaxEditor::with_ast_node(&expanded);
// We need to collect to avoid mutation during traversal.
let macro_exprs: Vec<_> =
expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
for mac in macro_exprs {
- let expr_opt = match compute_dbg_replacement(mac.clone()) {
+ let expr_opt = match compute_dbg_replacement(mac.clone(), make) {
Some((_, expr)) => expr,
None => continue,
};
if let Some(expr) = expr_opt {
- editor.replace(mac.syntax(), expr.syntax().clone_for_update());
+ editor.replace(mac.syntax(), expr.syntax());
} else {
editor.delete(mac.syntax());
}
diff --git a/crates/ide-assists/src/handlers/remove_else_branches.rs b/crates/ide-assists/src/handlers/remove_else_branches.rs
index 6a02c37015..0c03856417 100644
--- a/crates/ide-assists/src/handlers/remove_else_branches.rs
+++ b/crates/ide-assists/src/handlers/remove_else_branches.rs
@@ -55,7 +55,7 @@ pub(crate) fn remove_else_branches(acc: &mut Assists, ctx: &AssistContext<'_>) -
"Remove `else` branches",
target,
|builder| {
- let mut editor = builder.make_editor(&else_token.parent().unwrap());
+ let editor = builder.make_editor(&else_token.parent().unwrap());
match else_token.prev_token() {
Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it),
_ => (),
diff --git a/crates/ide-assists/src/handlers/remove_mut.rs b/crates/ide-assists/src/handlers/remove_mut.rs
index b07a361adf..2a6024339f 100644
--- a/crates/ide-assists/src/handlers/remove_mut.rs
+++ b/crates/ide-assists/src/handlers/remove_mut.rs
@@ -22,7 +22,7 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let target = mut_token.text_range();
acc.add(AssistId::refactor("remove_mut"), "Remove `mut` keyword", target, |builder| {
- let mut editor = builder.make_editor(&mut_token.parent().unwrap());
+ let editor = builder.make_editor(&mut_token.parent().unwrap());
match mut_token.next_token() {
Some(it) if it.kind() == SyntaxKind::WHITESPACE => editor.delete(it),
_ => (),
diff --git a/crates/ide-assists/src/handlers/remove_parentheses.rs b/crates/ide-assists/src/handlers/remove_parentheses.rs
index f07da489e2..af249c97b9 100644
--- a/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -1,8 +1,4 @@
-use syntax::{
- AstNode, SyntaxKind, T,
- ast::{self, syntax_factory::SyntaxFactory},
- syntax_editor::Position,
-};
+use syntax::{AstNode, SyntaxKind, T, ast, syntax_editor::Position};
use crate::{AssistContext, AssistId, Assists};
@@ -44,7 +40,8 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) ->
"Remove redundant parentheses",
target,
|builder| {
- let mut editor = builder.make_editor(parens.syntax());
+ let editor = builder.make_editor(parens.syntax());
+ let make = editor.make();
let prev_token = parens.syntax().first_token().and_then(|it| it.prev_token());
let need_to_add_ws = match prev_token {
Some(it) => {
@@ -54,9 +51,7 @@ pub(crate) fn remove_parentheses(acc: &mut Assists, ctx: &AssistContext<'_>) ->
None => false,
};
if need_to_add_ws {
- let make = SyntaxFactory::with_mappings();
editor.insert(Position::before(parens.syntax()), make.whitespace(" "));
- editor.add_mappings(make.finish_with_mappings());
}
editor.replace(parens.syntax(), expr.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor);
diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs
index 8b824c7c7f..b91d678c93 100644
--- a/crates/ide-assists/src/handlers/remove_unused_param.rs
+++ b/crates/ide-assists/src/handlers/remove_unused_param.rs
@@ -80,7 +80,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) ->
"Remove unused parameter",
param.syntax().text_range(),
|builder| {
- let mut editor = builder.make_editor(&parent);
+ let editor = builder.make_editor(&parent);
let elements = elements_to_remove(param.syntax());
for element in elements {
editor.delete(element);
@@ -116,7 +116,7 @@ fn process_usages(
else {
continue;
};
- let mut editor = builder.make_editor(&parent);
+ let editor = builder.make_editor(&parent);
for element in element_range {
editor.delete(element);
}
diff --git a/crates/ide-assists/src/handlers/reorder_fields.rs b/crates/ide-assists/src/handlers/reorder_fields.rs
index 990677d372..facbab8019 100644
--- a/crates/ide-assists/src/handlers/reorder_fields.rs
+++ b/crates/ide-assists/src/handlers/reorder_fields.rs
@@ -71,14 +71,12 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
"Reorder record fields",
target,
|builder| {
- let mut editor = builder.make_editor(&parent_node);
+ let editor = builder.make_editor(&parent_node);
match fields {
- Either::Left((sorted, field_list)) => {
- replace(&mut editor, field_list.fields(), sorted)
- }
+ Either::Left((sorted, field_list)) => replace(&editor, field_list.fields(), sorted),
Either::Right((sorted, field_list)) => {
- replace(&mut editor, field_list.fields(), sorted)
+ replace(&editor, field_list.fields(), sorted)
}
}
@@ -88,7 +86,7 @@ pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
fn replace<T: AstNode + PartialEq>(
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
fields: impl Iterator<Item = T>,
sorted_fields: impl IntoIterator<Item = T>,
) {
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs
index 0ad5ec9d44..df5281895a 100644
--- a/crates/ide-assists/src/handlers/reorder_impl_items.rs
+++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -99,7 +99,7 @@ pub(crate) fn reorder_impl_items(acc: &mut Assists, ctx: &AssistContext<'_>) ->
"Sort items by trait definition",
target,
|builder| {
- let mut editor = builder.make_editor(&parent_node);
+ let editor = builder.make_editor(&parent_node);
assoc_items
.into_iter()
diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs
index b686dc0566..5ad5efac05 100644
--- a/crates/ide-assists/src/handlers/replace_arith_op.rs
+++ b/crates/ide-assists/src/handlers/replace_arith_op.rs
@@ -1,7 +1,7 @@
use ide_db::assists::{AssistId, GroupLabel};
use syntax::{
AstNode,
- ast::{self, ArithOp, BinaryOp, syntax_factory::SyntaxFactory},
+ ast::{self, ArithOp, BinaryOp},
};
use crate::assist_context::{AssistContext, Assists};
@@ -83,8 +83,8 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) ->
kind.label(),
op_expr.text_range(),
|builder| {
- let mut edit = builder.make_editor(rhs.syntax());
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(rhs.syntax());
+ let make = editor.make();
let method_name = kind.method_name(op);
let needs_parentheses =
@@ -92,10 +92,8 @@ fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) ->
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]));
- edit.replace(op_expr, arith_expr.syntax());
-
- edit.add_mappings(make.finish_with_mappings());
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ editor.replace(op_expr, arith_expr.syntax());
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
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 f54f7a02d2..751cd42f6e 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
@@ -4,7 +4,7 @@ use itertools::Itertools;
use syntax::{
SyntaxKind::WHITESPACE,
T,
- ast::{self, AstNode, HasName, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, HasName},
syntax_editor::{Position, SyntaxEditor},
};
@@ -12,8 +12,8 @@ use crate::{
AssistConfig, AssistId,
assist_context::{AssistContext, Assists},
utils::{
- DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl_with_factory,
- filter_assoc_items, gen_trait_fn_body, generate_trait_impl,
+ DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items,
+ gen_trait_fn_body, generate_trait_impl, generate_trait_impl_with_item,
},
};
@@ -64,9 +64,10 @@ pub(crate) fn replace_derive_with_manual_impl(
.filter_map(|attr| attr.path())
.collect::<Vec<_>>();
- let adt = value.parent().and_then(ast::Adt::cast)?;
- let attr = ast::Attr::cast(value)?;
- let args = attr.token_tree()?;
+ let attr = ast::Meta::cast(value)?.parent_attr()?;
+ let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
+ let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None };
+ let args = meta.token_tree()?;
let current_module = ctx.sema.scope(adt.syntax())?.module();
let current_crate = current_module.krate(ctx.db());
@@ -127,10 +128,12 @@ fn add_assist(
let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| {
- let make = SyntaxFactory::without_mappings();
+ let editor = builder.make_editor(attr.syntax());
+ let make = editor.make();
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(
+ &editor,
&ctx.sema,
ctx.config,
adt,
@@ -139,9 +142,7 @@ fn add_assist(
replace_trait_path,
impl_is_unsafe,
);
-
- let mut editor = builder.make_editor(attr.syntax());
- update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr);
+ update_attribute(&editor, old_derives, old_tree, old_trait_path, attr);
let trait_path = make.ty_path(replace_trait_path.clone()).into();
@@ -151,7 +152,7 @@ fn add_assist(
impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()),
)
} else {
- (generate_trait_impl(&make, impl_is_unsafe, adt, trait_path), None)
+ (generate_trait_impl(make, impl_is_unsafe, adt, trait_path), None)
};
if let Some(cap) = ctx.config.snippet_cap {
@@ -182,6 +183,7 @@ fn add_assist(
}
fn impl_def_from_trait(
+ editor: &SyntaxEditor,
sema: &hir::Semantics<'_, ide_db::RootDatabase>,
config: &AssistConfig,
adt: &ast::Adt,
@@ -190,6 +192,7 @@ fn impl_def_from_trait(
trait_path: &ast::Path,
impl_is_unsafe: bool,
) -> Option<ast::Impl> {
+ let make = editor.make();
let trait_ = trait_?;
let target_scope = sema.scope(annotated_name.syntax())?;
@@ -206,12 +209,11 @@ fn impl_def_from_trait(
if trait_items.is_empty() {
return None;
}
- let make = SyntaxFactory::without_mappings();
- let trait_ty = make.ty_path(trait_path.clone()).into();
- let impl_def = generate_trait_impl(&make, impl_is_unsafe, adt, trait_ty);
+ let trait_ty: ast::Type = make.ty_path(trait_path.clone()).into();
+ let impl_def = generate_trait_impl(make, impl_is_unsafe, adt, trait_ty.clone());
- let assoc_items = add_trait_assoc_items_to_impl_with_factory(
- &make,
+ let assoc_items = add_trait_assoc_items_to_impl(
+ make,
sema,
config,
&trait_items,
@@ -219,14 +221,12 @@ fn impl_def_from_trait(
&impl_def,
&target_scope,
);
- let assoc_item_list = if let Some((first, other)) =
- assoc_items.split_first().map(|(first, other)| (first.clone_subtree(), other))
- {
- let first_item = if let ast::AssocItem::Fn(ref func) = first
- && let Some(body) = gen_trait_fn_body(func, trait_path, adt, None)
+ let assoc_item_list = if let Some((first, other)) = assoc_items.split_first() {
+ let first_item = if let ast::AssocItem::Fn(func) = first
+ && let Some(body) = gen_trait_fn_body(make, func, trait_path, adt, None)
&& let Some(func_body) = func.body()
{
- let mut editor = SyntaxEditor::new(first.syntax().clone());
+ let (editor, _) = SyntaxEditor::new(first.syntax().clone());
editor.replace(func_body.syntax(), body.syntax());
ast::AssocItem::cast(editor.finish().new_root().clone())
} else {
@@ -239,21 +239,17 @@ fn impl_def_from_trait(
make.assoc_item_list_empty()
};
- let impl_def = impl_def.clone_subtree();
- let mut editor = SyntaxEditor::new(impl_def.syntax().clone());
- editor.replace(impl_def.assoc_item_list()?.syntax(), assoc_item_list.syntax());
- let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?;
- Some(impl_def)
+ Some(generate_trait_impl_with_item(make, impl_is_unsafe, adt, trait_ty, assoc_item_list))
}
fn update_attribute(
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
old_derives: &[ast::Path],
old_tree: &ast::TokenTree,
old_trait_path: &ast::Path,
attr: &ast::Attr,
) {
- let make = SyntaxFactory::without_mappings();
+ let make = editor.make();
let new_derives = old_derives
.iter()
.filter(|t| t.to_string() != old_trait_path.to_string())
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 8ff30fce5b..0badad7d0c 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
@@ -13,7 +13,10 @@ use syntax::{
use crate::{
AssistContext, AssistId, Assists,
- utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block},
+ utils::{
+ does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block,
+ wrap_paren,
+ },
};
// Assist: replace_if_let_with_match
@@ -108,9 +111,10 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
format!("Replace if{let_} with match"),
available_range,
move |builder| {
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(if_expr.syntax());
+ let make = editor.make();
let match_expr: ast::Expr = {
- let else_arm = make_else_arm(ctx, &make, else_block, &cond_bodies);
+ let else_arm = make_else_arm(ctx, make, else_block, &cond_bodies);
let make_match_arm =
|(pat, guard, body): (_, Option<ast::Expr>, ast::BlockExpr)| {
// Dedent from original position, then indent for match arm
@@ -128,6 +132,11 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
};
let arms = cond_bodies.into_iter().map(make_match_arm).chain([else_arm]);
let expr = scrutinee_to_be_expr.reset_indent();
+ let expr = if match_scrutinee_needs_paren(&expr) {
+ make.expr_paren(expr).into()
+ } else {
+ expr
+ };
let match_expr = make.expr_match(expr, make.match_arm_list(arms)).indent(indent);
match_expr.into()
};
@@ -143,10 +152,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext<'
} else {
match_expr
};
-
- let mut editor = builder.make_editor(if_expr.syntax());
editor.replace(if_expr.syntax(), expr.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -264,7 +270,8 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
format!("Replace match with if{let_}"),
match_expr.syntax().text_range(),
move |builder| {
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(match_expr.syntax());
+ let make = editor.make();
let make_block_expr = |expr: ast::Expr| {
// Blocks with modifiers (unsafe, async, etc.) are parsed as BlockExpr, but are
// formatted without enclosing braces. If we encounter such block exprs,
@@ -289,6 +296,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
_ => make.expr_let(if_let_pat, scrutinee).into(),
};
let condition = if let Some(guard) = guard {
+ let guard = wrap_paren(guard, make, ast::prec::ExprPrecedence::LAnd);
make.expr_bin(condition, ast::BinaryOp::LogicOp(ast::LogicOp::And), guard).into()
} else {
condition
@@ -305,9 +313,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext<'
)
.indent(IndentLevel::from_node(match_expr.syntax()));
- let mut editor = builder.make_editor(match_expr.syntax());
editor.replace(match_expr.syntax(), if_let_expr.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -398,28 +404,37 @@ fn let_and_guard(cond: &ast::Expr) -> (Option<ast::LetExpr>, Option<ast::Expr>)
} else if let ast::Expr::BinExpr(bin_expr) = cond
&& let Some(ast::Expr::LetExpr(let_expr)) = and_bin_expr_left(bin_expr).lhs()
{
- let new_expr = bin_expr.clone_subtree();
- let mut edit = SyntaxEditor::new(new_expr.syntax().clone());
-
+ let (editor, new_expr) = SyntaxEditor::with_ast_node(bin_expr);
let left_bin = and_bin_expr_left(&new_expr);
if let Some(rhs) = left_bin.rhs() {
- edit.replace(left_bin.syntax(), rhs.syntax());
+ editor.replace(left_bin.syntax(), rhs.syntax());
} else {
if let Some(next) = left_bin.syntax().next_sibling_or_token()
&& next.kind() == SyntaxKind::WHITESPACE
{
- edit.delete(next);
+ editor.delete(next);
}
- edit.delete(left_bin.syntax());
+ editor.delete(left_bin.syntax());
}
- let new_expr = edit.finish().new_root().clone();
+ let new_expr = editor.finish().new_root().clone();
(Some(let_expr), ast::Expr::cast(new_expr))
} else {
(None, Some(cond.clone()))
}
}
+fn match_scrutinee_needs_paren(expr: &ast::Expr) -> bool {
+ let make = SyntaxFactory::without_mappings();
+ let fake_scrutinee = make.expr_unit();
+ let fake_match = make.expr_match(fake_scrutinee, make.match_arm_list(std::iter::empty()));
+ let Some(fake_expr) = fake_match.expr() else {
+ stdx::never!();
+ return false;
+ };
+ expr.needs_parens_in_place_of(fake_match.syntax(), fake_expr.syntax())
+}
+
fn and_bin_expr_left(expr: &ast::BinExpr) -> ast::BinExpr {
if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And))
&& let Some(ast::Expr::BinExpr(left)) = expr.lhs()
@@ -449,6 +464,26 @@ fn main() {
}
#[test]
+ fn test_if_with_match_paren_jump_scrutinee() {
+ check_assist(
+ replace_if_let_with_match,
+ r#"
+fn f() {
+ if $0(return) {}
+}
+"#,
+ r#"
+fn f() {
+ match (return) {
+ true => {}
+ false => (),
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn test_if_with_match_no_else() {
check_assist(
replace_if_let_with_match,
@@ -2268,14 +2303,35 @@ fn main() {
"#,
r#"
fn main() {
- if let Some(n) = Some(0) && n % 2 == 0 && n != 6 {
+ if let Some(n) = Some(0) && (n % 2 == 0 && n != 6) {
()
} else {
code()
}
}
"#,
- )
+ );
+
+ check_assist(
+ replace_match_with_if_let,
+ r#"
+fn main() {
+ match$0 Some(0) {
+ Some(n) if n % 2 == 0 || n == 7 => (),
+ _ => code(),
+ }
+}
+"#,
+ r#"
+fn main() {
+ if let Some(n) = Some(0) && (n % 2 == 0 || n == 7) {
+ ()
+ } else {
+ code()
+ }
+}
+"#,
+ );
}
#[test]
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 38d8c38ef2..802d5f72b9 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
@@ -1,6 +1,9 @@
use either::Either;
use ide_db::syntax_helpers::suggest_name;
-use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory};
+use syntax::{
+ ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory},
+ syntax_editor::SyntaxEditor,
+};
use crate::{
AssistContext, AssistId, Assists,
@@ -41,9 +44,9 @@ pub(crate) fn replace_is_method_with_if_let_method(
let method_kind = token.text().strip_suffix("_and").unwrap_or(token.text());
match method_kind {
"is_some" | "is_ok" => {
+ let (editor, _) = SyntaxEditor::new(ctx.source_file().syntax().clone());
+ let make = editor.make();
let receiver = call_expr.receiver()?;
- let make = SyntaxFactory::with_mappings();
-
let mut name_generator = suggest_name::NameGenerator::new_from_scope_locals(
ctx.sema.scope(has_cond.syntax()),
);
@@ -52,7 +55,7 @@ pub(crate) fn replace_is_method_with_if_let_method(
} else {
name_generator.for_variable(&receiver, &ctx.sema)
};
- let (pat, predicate) = method_predicate(&call_expr, &var_name, &make);
+ let (pat, predicate) = method_predicate(&call_expr, &var_name, make);
let (assist_id, message, text) = if method_kind == "is_some" {
("replace_is_some_with_if_let_some", "Replace `is_some` with `let Some`", "Some")
@@ -65,8 +68,7 @@ pub(crate) fn replace_is_method_with_if_let_method(
message,
call_expr.syntax().text_range(),
|edit| {
- let mut editor = edit.make_editor(call_expr.syntax());
-
+ let make = editor.make();
let pat = make.tuple_struct_pat(make.ident_path(text), [pat]).into();
let let_expr = make.expr_let(pat, receiver);
@@ -81,14 +83,12 @@ pub(crate) fn replace_is_method_with_if_let_method(
let new_expr = if let Some(predicate) = predicate {
let op = ast::BinaryOp::LogicOp(ast::LogicOp::And);
- let predicate = wrap_paren(predicate, &make, ExprPrecedence::LAnd);
+ let predicate = wrap_paren(predicate, make, ExprPrecedence::LAnd);
make.expr_bin(let_expr.into(), op, predicate).into()
} else {
ast::Expr::from(let_expr)
};
editor.replace(call_expr.syntax(), new_expr.syntax());
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/replace_let_with_if_let.rs b/crates/ide-assists/src/handlers/replace_let_with_if_let.rs
index 6ff5f0bbd3..85e72130e0 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
@@ -46,8 +46,8 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
"Replace let with if let",
target,
|builder| {
- let mut editor = builder.make_editor(let_stmt.syntax());
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(let_stmt.syntax());
+ let make = editor.make();
let ty = ctx.sema.type_of_expr(&init);
let pat = if let_stmt.let_else().is_some() {
// Do not add the wrapper type that implements `Try`,
@@ -79,7 +79,6 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
let if_stmt = make.expr_stmt(if_expr.into());
editor.replace(let_stmt.syntax(), if_stmt.syntax());
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -88,6 +87,11 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext<'_>
fn let_expr_needs_paren(expr: &ast::Expr) -> bool {
let make = SyntaxFactory::without_mappings();
let fake_expr_let = make.expr_let(make.tuple_pat(None).into(), make.expr_unit());
+ let fake_if = make.expr_if(fake_expr_let.into(), make.expr_empty_block(), None);
+ let Some(ast::Expr::LetExpr(fake_expr_let)) = fake_if.condition() else {
+ stdx::never!();
+ return false;
+ };
let Some(fake_expr) = fake_expr_let.expr() else {
stdx::never!();
return false;
@@ -183,6 +187,24 @@ fn main() {
}
#[test]
+ fn replace_let_record_expr() {
+ check_assist(
+ replace_let_with_if_let,
+ r"
+fn main() {
+ $0let x = Foo { x };
+}
+ ",
+ r"
+fn main() {
+ if let x = (Foo { x }) {
+ }
+}
+ ",
+ )
+ }
+
+ #[test]
fn replace_let_else() {
check_assist(
replace_let_with_if_let,
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 018642a047..17ef7727ec 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
@@ -5,7 +5,6 @@ use ide_db::{
defs::Definition,
search::{SearchScope, UsageSearchResult},
};
-use syntax::ast::syntax_factory::SyntaxFactory;
use syntax::{
AstNode,
ast::{self, HasGenericParams, HasName, HasTypeBounds, Name, NameLike, PathType},
@@ -72,8 +71,8 @@ pub(crate) fn replace_named_generic_with_impl(
"Replace named generic with impl trait",
target,
|edit| {
- let mut editor = edit.make_editor(type_param.syntax());
- let make = SyntaxFactory::without_mappings();
+ let editor = edit.make_editor(type_param.syntax());
+ let make = editor.make();
// remove trait from generic param list
if let Some(generic_params) = fn_.generic_param_list() {
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 cdf20586ef..eebe93f005 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
@@ -73,8 +73,8 @@ pub(crate) fn replace_qualified_name_with_use(
// Now that we've brought the name into scope, re-qualify all paths that could be
// affected (that is, all paths inside the node we added the `use` to).
let scope_node = scope.as_syntax_node();
- let mut editor = builder.make_editor(scope_node);
- shorten_paths(&mut editor, scope_node, &original_path);
+ let editor = builder.make_editor(scope_node);
+ shorten_paths(&editor, scope_node, &original_path);
builder.add_file_edits(ctx.vfs_file_id(), editor);
let path = drop_generic_args(&original_path);
let edition = ctx
@@ -111,8 +111,7 @@ fn target_path(ctx: &AssistContext<'_>, mut original_path: ast::Path) -> Option<
}
fn drop_generic_args(path: &ast::Path) -> ast::Path {
- let path = path.clone_subtree();
- let mut editor = SyntaxEditor::new(path.syntax().clone());
+ let (editor, path) = SyntaxEditor::with_ast_node(path);
if let Some(segment) = path.segment()
&& let Some(generic_args) = segment.generic_arg_list()
{
@@ -123,7 +122,7 @@ fn drop_generic_args(path: &ast::Path) -> ast::Path {
}
/// Mutates `node` to shorten `path` in all descendants of `node`.
-fn shorten_paths(editor: &mut SyntaxEditor, node: &SyntaxNode, path: &ast::Path) {
+fn shorten_paths(editor: &SyntaxEditor, node: &SyntaxNode, path: &ast::Path) {
for child in node.children() {
match_ast! {
match child {
@@ -142,7 +141,7 @@ fn shorten_paths(editor: &mut SyntaxEditor, node: &SyntaxNode, path: &ast::Path)
}
}
-fn maybe_replace_path(editor: &mut SyntaxEditor, path: ast::Path, target: ast::Path) -> Option<()> {
+fn maybe_replace_path(editor: &SyntaxEditor, path: ast::Path, target: ast::Path) -> Option<()> {
if !path_eq_no_generics(path.clone(), target) {
return None;
}
diff --git a/crates/ide-assists/src/handlers/sort_items.rs b/crates/ide-assists/src/handlers/sort_items.rs
index e973e70345..911fa9d14b 100644
--- a/crates/ide-assists/src/handlers/sort_items.rs
+++ b/crates/ide-assists/src/handlers/sort_items.rs
@@ -127,7 +127,7 @@ impl AddRewrite for Assists {
target: &SyntaxNode,
) -> Option<()> {
self.add(AssistId::refactor_rewrite("sort_items"), label, target.text_range(), |builder| {
- let mut editor = builder.make_editor(target);
+ let editor = builder.make_editor(target);
old.into_iter()
.zip(new)
diff --git a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
index 15143575e7..4d375080f5 100644
--- a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
+++ b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -2,7 +2,7 @@ use ide_db::assists::AssistId;
use syntax::{
AstNode, SyntaxKind, SyntaxToken, T,
algo::{previous_non_trivia_token, skip_trivia_token},
- ast::{self, syntax_factory::SyntaxFactory},
+ ast,
};
use crate::{AssistContext, Assists};
@@ -73,8 +73,8 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
},
token_tree.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(token_tree.syntax());
+ let editor = builder.make_editor(token_tree.syntax());
+ let make = editor.make();
match token {
MacroDelims::LPar | MacroDelims::RPar => {
@@ -100,7 +100,6 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
}
}
}
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/unmerge_imports.rs b/crates/ide-assists/src/handlers/unmerge_imports.rs
index accb5c28d6..ab6317ad44 100644
--- a/crates/ide-assists/src/handlers/unmerge_imports.rs
+++ b/crates/ide-assists/src/handlers/unmerge_imports.rs
@@ -1,7 +1,7 @@
use syntax::{
AstNode, SyntaxKind,
- ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
- syntax_editor::{Element, Position, Removable},
+ ast::{self, HasAttrs, HasVisibility, edit::IndentLevel, syntax_factory::SyntaxFactory},
+ syntax_editor::{Element, Position, Removable, SyntaxEditor},
};
use crate::{
@@ -22,6 +22,8 @@ use crate::{
// use std::fmt::Display;
// ```
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>()?;
let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?;
@@ -31,7 +33,7 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
}
let use_ = tree_list.syntax().ancestors().find_map(ast::Use::cast)?;
- let path = resolve_full_path(&tree)?;
+ let path = resolve_full_path(&tree, make)?;
// If possible, explain what is going to be done.
let label = match tree.path().and_then(|path| path.first_segment()) {
@@ -41,16 +43,15 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let target = tree.syntax().text_range();
acc.add(AssistId::refactor_rewrite("unmerge_imports"), label, target, |builder| {
- let make = SyntaxFactory::with_mappings();
+ let make = editor.make();
let new_use = make.use_(
use_.attrs(),
use_.visibility(),
make.use_tree(path, tree.use_tree_list(), tree.rename(), tree.star_token().is_some()),
);
- let mut editor = builder.make_editor(use_.syntax());
// Remove the use tree from the current use item
- tree.remove(&mut editor);
+ tree.remove(&editor);
// Insert a newline and indentation, followed by the new use item
editor.insert_all(
Position::after(use_.syntax()),
@@ -60,12 +61,11 @@ pub(crate) fn unmerge_imports(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
new_use.syntax().syntax_element(),
],
);
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
-fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
+fn resolve_full_path(tree: &ast::UseTree, make: &SyntaxFactory) -> Option<ast::Path> {
let paths = tree
.syntax()
.ancestors()
@@ -73,7 +73,7 @@ fn resolve_full_path(tree: &ast::UseTree) -> Option<ast::Path> {
.filter_map(ast::UseTree::cast)
.filter_map(|t| t.path());
- let final_path = paths.reduce(|prev, next| make::path_concat(next, prev))?;
+ let final_path = paths.reduce(|prev, next| make.path_concat(next, prev))?;
if final_path.segment().is_some_and(|it| it.self_token().is_some()) {
final_path.qualifier()
} else {
diff --git a/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index c4c03d3e35..65300ccefd 100644
--- a/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -1,6 +1,6 @@
use syntax::{
Direction, SyntaxKind, T,
- ast::{self, AstNode, edit::IndentLevel, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, edit::IndentLevel},
syntax_editor::{Element, Position},
};
@@ -56,8 +56,8 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
"Unmerge match arm",
pipe_token.text_range(),
|edit| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(&new_parent);
+ let editor = edit.make_editor(&new_parent);
+ let make = editor.make();
// It is guaranteed that `pats_after` has at least one element
let new_pat = if pats_after.len() == 1 {
pats_after[0].clone()
@@ -101,7 +101,6 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
insert_after_old_arm.push(new_match_arm.syntax().clone().into());
editor.insert_all(Position::after(match_arm.syntax()), insert_after_old_arm);
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)
diff --git a/crates/ide-assists/src/handlers/unqualify_method_call.rs b/crates/ide-assists/src/handlers/unqualify_method_call.rs
index ef395791e2..045a272952 100644
--- a/crates/ide-assists/src/handlers/unqualify_method_call.rs
+++ b/crates/ide-assists/src/handlers/unqualify_method_call.rs
@@ -1,5 +1,5 @@
use hir::AsAssocItem;
-use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence, syntax_factory::SyntaxFactory};
+use syntax::ast::{self, AstNode, HasArgList, prec::ExprPrecedence};
use crate::{AssistContext, AssistId, Assists};
@@ -50,8 +50,8 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
"Unqualify method call",
call.syntax().text_range(),
|builder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = builder.make_editor(call.syntax());
+ let editor = builder.make_editor(call.syntax());
+ let make = editor.make();
let new_arg_list = make.arg_list(args.args().skip(1));
let receiver = if first_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix) {
@@ -67,10 +67,9 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
&& let Some(trait_) = fun.container_or_implemented_trait(ctx.db())
&& !scope.can_use_trait_methods(trait_)
{
- add_import(qualifier, ctx, &make, &mut editor);
+ add_import(qualifier, ctx, &editor);
}
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
@@ -79,8 +78,7 @@ pub(crate) fn unqualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>)
fn add_import(
qualifier: ast::Path,
ctx: &AssistContext<'_>,
- make: &SyntaxFactory,
- editor: &mut syntax::syntax_editor::SyntaxEditor,
+ editor: &syntax::syntax_editor::SyntaxEditor,
) {
if let Some(path_segment) = qualifier.segment() {
// for `<i32 as std::ops::Add>`
@@ -112,7 +110,6 @@ fn add_import(
import,
&ctx.config.insert_use,
editor,
- make,
);
}
}
diff --git a/crates/ide-assists/src/handlers/unwrap_block.rs b/crates/ide-assists/src/handlers/unwrap_block.rs
index e029d7884f..77941bcfb2 100644
--- a/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -3,7 +3,6 @@ use syntax::{
ast::{
self,
edit::{AstNodeEdit, IndentLevel},
- make,
},
match_ast,
syntax_editor::{Element, Position, SyntaxEditor},
@@ -72,18 +71,19 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
let replacement = replacement.stmt_list()?;
acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| {
- let mut edit = builder.make_editor(block.syntax());
+ let editor = builder.make_editor(block.syntax());
let replacement = replacement.dedent(from_indent).indent(into_indent);
let container = prefer_container.unwrap_or(container);
- edit.replace_with_many(&container, extract_statements(replacement));
- delete_else_before(container, &mut edit);
+ editor.replace_with_many(&container, extract_statements(replacement));
+ delete_else_before(container, &editor);
- builder.add_file_edits(ctx.vfs_file_id(), edit);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
-fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) {
+fn delete_else_before(container: SyntaxNode, editor: &SyntaxEditor) {
+ let make = editor.make();
let Some(else_token) = container
.siblings_with_tokens(syntax::Direction::Prev)
.skip(1)
@@ -94,17 +94,16 @@ fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) {
};
itertools::chain(else_token.prev_token(), else_token.next_token())
.filter(|it| it.kind() == SyntaxKind::WHITESPACE)
- .for_each(|it| edit.delete(it));
+ .for_each(|it| editor.delete(it));
let indent = IndentLevel::from_node(&container);
- let newline = make::tokens::whitespace(&format!("\n{indent}"));
- edit.replace(else_token, newline);
+ let newline = make.whitespace(&format!("\n{indent}"));
+ editor.replace(else_token, newline);
}
fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr {
let try_wrap_assign = || {
let initializer = assign.initializer()?.syntax().syntax_element();
- let replacement = replacement.clone_subtree();
- let assign = assign.clone_for_update();
+ let (editor, replacement) = SyntaxEditor::with_ast_node(&replacement);
let tail_expr = replacement.tail_expr()?;
let before =
assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect();
@@ -115,10 +114,9 @@ fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExp
.skip(1)
.collect();
- let mut edit = SyntaxEditor::new(replacement.syntax().clone());
- edit.insert_all(Position::before(tail_expr.syntax()), before);
- edit.insert_all(Position::after(tail_expr.syntax()), after);
- ast::BlockExpr::cast(edit.finish().new_root().clone())
+ editor.insert_all(Position::before(tail_expr.syntax()), before);
+ editor.insert_all(Position::after(tail_expr.syntax()), after);
+ ast::BlockExpr::cast(editor.finish().new_root().clone())
};
try_wrap_assign().unwrap_or(replacement)
}
diff --git a/crates/ide-assists/src/handlers/unwrap_return_type.rs b/crates/ide-assists/src/handlers/unwrap_return_type.rs
index eea6c85e8d..1fe9ea4eb8 100644
--- a/crates/ide-assists/src/handlers/unwrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/unwrap_return_type.rs
@@ -5,7 +5,7 @@ use ide_db::{
};
use syntax::{
AstNode, NodeOrToken, SyntaxKind,
- ast::{self, HasArgList, HasGenericArgs, syntax_factory::SyntaxFactory},
+ ast::{self, HasArgList, HasGenericArgs},
match_ast,
};
@@ -66,8 +66,8 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let happy_type = extract_wrapped_type(type_ref)?;
acc.add(kind.assist_id(), kind.label(), type_ref.syntax().text_range(), |builder| {
- let mut editor = builder.make_editor(&parent);
- let make = SyntaxFactory::with_mappings();
+ let editor = builder.make_editor(&parent);
+ let make = editor.make();
let mut exprs_to_unwrap = Vec::new();
let tail_cb = &mut |e: &_| tail_cb_impl(&mut exprs_to_unwrap, e);
@@ -168,7 +168,6 @@ pub(crate) fn unwrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) ->
editor.add_annotation(final_placeholder.syntax(), builder.make_tabstop_after(cap));
}
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
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 7b5adc1858..935ae18905 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
@@ -46,7 +46,7 @@ pub(crate) fn unwrap_type_to_generic_arg(acc: &mut Assists, ctx: &AssistContext<
format!("Unwrap type to type argument {generic_arg}"),
path_type.syntax().text_range(),
|builder| {
- let mut editor = builder.make_editor(path_type.syntax());
+ let editor = builder.make_editor(path_type.syntax());
editor.replace(path_type.syntax(), generic_arg.syntax());
builder.add_file_edits(ctx.vfs_file_id(), editor);
diff --git a/crates/ide-assists/src/handlers/wrap_return_type.rs b/crates/ide-assists/src/handlers/wrap_return_type.rs
index 0f089c9b66..ddc0af31c3 100644
--- a/crates/ide-assists/src/handlers/wrap_return_type.rs
+++ b/crates/ide-assists/src/handlers/wrap_return_type.rs
@@ -77,9 +77,9 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
kind.label(),
type_ref.syntax().text_range(),
|builder| {
- let mut editor = builder.make_editor(&parent);
- let make = SyntaxFactory::with_mappings();
- let alias = wrapper_alias(ctx, &make, core_wrapper, type_ref, &ty, kind.symbol());
+ let editor = builder.make_editor(&parent);
+ let make = editor.make();
+ let alias = wrapper_alias(ctx, make, core_wrapper, type_ref, &ty, kind.symbol());
let (ast_new_return_ty, semantic_new_return_ty) = alias.unwrap_or_else(|| {
let (ast_ty, ty_constructor) = match kind {
WrapperKind::Option => {
@@ -156,8 +156,6 @@ pub(crate) fn wrap_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
);
}
}
-
- editor.add_mappings(make.finish_with_mappings());
builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -964,7 +962,7 @@ fn foo(num: i32) -> Option<i32> {
check_assist_by_label(
wrap_return_type,
r#"
-//- minicore: option
+//- minicore: option, fn
fn foo(the_field: u32) ->$0 u32 {
let true_closure = || { return true; };
if the_field < 5 {
@@ -998,7 +996,7 @@ fn foo(the_field: u32) -> Option<u32> {
check_assist_by_label(
wrap_return_type,
r#"
-//- minicore: option
+//- minicore: option, fn
fn foo(the_field: u32) -> u32$0 {
let true_closure = || {
return true;
@@ -2035,7 +2033,7 @@ fn foo(num: i32) -> Result<i32, ${0:_}> {
check_assist_by_label(
wrap_return_type,
r#"
-//- minicore: result
+//- minicore: result, fn
fn foo(the_field: u32) ->$0 u32 {
let true_closure = || { return true; };
if the_field < 5 {
@@ -2069,7 +2067,7 @@ fn foo(the_field: u32) -> Result<u32, ${0:_}> {
check_assist_by_label(
wrap_return_type,
r#"
-//- minicore: result
+//- minicore: result, fn
fn foo(the_field: u32) -> u32$0 {
let true_closure = || {
return true;
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 36df4af31d..635fab857d 100644
--- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
+++ b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -1,8 +1,7 @@
use ide_db::source_change::SourceChangeBuilder;
-use itertools::Itertools;
use syntax::{
NodeOrToken, SyntaxToken, T, TextRange, algo,
- ast::{self, AstNode, edit::AstNodeEdit, make, syntax_factory::SyntaxFactory},
+ ast::{self, AstNode, edit::AstNodeEdit},
};
use crate::{AssistContext, AssistId, Assists};
@@ -19,7 +18,7 @@ use crate::{AssistContext, AssistId, Assists};
// ```
// ->
// ```
-// #[cfg_attr($0, derive(Debug))]
+// #[cfg_attr(${0:cfg}, derive(Debug))]
// struct S {
// field: i32
// }
@@ -147,12 +146,15 @@ pub(crate) fn wrap_unwrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>) -
}
}?;
match option {
- WrapUnwrapOption::WrapAttr(attrs) => match &attrs[..] {
- [attr] if attr.simple_name().as_deref() == Some("cfg_attr") => {
- unwrap_cfg_attr(acc, attrs.into_iter().next().unwrap())
+ WrapUnwrapOption::WrapAttr(attrs) => {
+ if let [attr] = &attrs[..]
+ && let Some(ast::Meta::CfgAttrMeta(meta)) = attr.meta()
+ {
+ unwrap_cfg_attr(acc, ctx, meta)
+ } else {
+ wrap_cfg_attrs(acc, ctx, attrs)
}
- _ => wrap_cfg_attrs(acc, ctx, attrs),
- },
+ }
WrapUnwrapOption::WrapDerive { derive, attr } => wrap_derive(acc, ctx, attr, derive),
}
}
@@ -164,7 +166,8 @@ fn wrap_derive(
derive_element: TextRange,
) -> Option<()> {
let range = attr.syntax().text_range();
- let token_tree = attr.token_tree()?;
+ let ast::Meta::TokenTreeMeta(meta) = attr.meta()? else { return None };
+ let token_tree = meta.token_tree()?;
let mut path_text = String::new();
let mut cfg_derive_tokens = Vec::new();
@@ -188,25 +191,20 @@ fn wrap_derive(
}
}
let handle_source_change = |edit: &mut SourceChangeBuilder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(attr.syntax());
+ let editor = edit.make_editor(attr.syntax());
+ let make = editor.make();
let new_derive = make.attr_outer(
make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)),
);
- let meta = make.meta_token_tree(
- make.ident_path("cfg_attr"),
- make.token_tree(
- T!['('],
- vec![
- NodeOrToken::Token(make.token(T![,])),
- NodeOrToken::Token(make.whitespace(" ")),
- NodeOrToken::Token(make.ident("derive")),
- NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)),
- ],
- ),
+ let meta = make.cfg_attr_meta(
+ make.cfg_flag("cfg"),
+ [make.meta_token_tree(
+ make.ident_path("derive"),
+ make.token_tree(T!['('], cfg_derive_tokens),
+ )],
);
- let cfg_attr = make.attr_outer(meta);
+ let cfg_attr = make.attr_outer(meta.clone().into());
editor.replace_with_many(
attr.syntax(),
vec![
@@ -217,14 +215,11 @@ fn wrap_derive(
);
if let Some(snippet_cap) = ctx.config.snippet_cap
- && let Some(first_meta) =
- cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+ && let Some(cfg_predicate) = meta.cfg_predicate()
{
- let tabstop = edit.make_tabstop_after(snippet_cap);
- editor.add_annotation(first_meta, tabstop);
+ let tabstop = edit.make_placeholder_snippet(snippet_cap);
+ editor.add_annotation(cfg_predicate.syntax(), tabstop);
}
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
};
@@ -236,61 +231,30 @@ fn wrap_derive(
);
Some(())
}
+
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 path_attrs =
- attrs.iter().map(|attr| Some((attr.path()?, attr.clone()))).collect::<Option<Vec<_>>>()?;
let handle_source_change = |edit: &mut SourceChangeBuilder| {
- let make = SyntaxFactory::with_mappings();
- let mut editor = edit.make_editor(first_attr.syntax());
- let mut raw_tokens = vec![];
- for (path, attr) in path_attrs {
- raw_tokens.extend([
- NodeOrToken::Token(make.token(T![,])),
- NodeOrToken::Token(make.whitespace(" ")),
- ]);
- path.syntax().descendants_with_tokens().for_each(|it| {
- if let NodeOrToken::Token(token) = it {
- raw_tokens.push(NodeOrToken::Token(token));
- }
- });
- if let Some(meta) = attr.meta() {
- if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
- raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
- raw_tokens.push(NodeOrToken::Token(eq));
- raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
-
- expr.syntax().descendants_with_tokens().for_each(|it| {
- if let NodeOrToken::Token(token) = it {
- raw_tokens.push(NodeOrToken::Token(token));
- }
- });
- } else if let Some(tt) = meta.token_tree() {
- raw_tokens.extend(tt.token_trees_and_tokens());
- }
- }
- }
+ let editor = edit.make_editor(first_attr.syntax());
+ let make = editor.make();
let meta =
- make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens));
+ make.cfg_attr_meta(make.cfg_flag("cfg"), attrs.iter().filter_map(|attr| attr.meta()));
let cfg_attr = if first_attr.excl_token().is_some() {
- make.attr_inner(meta)
+ make.attr_inner(meta.clone().into())
} else {
- make.attr_outer(meta)
+ make.attr_outer(meta.clone().into())
};
let syntax_range = first_attr.syntax().clone().into()..=last_attr.syntax().clone().into();
editor.replace_all(syntax_range, vec![cfg_attr.syntax().clone().into()]);
if let Some(snippet_cap) = ctx.config.snippet_cap
- && let Some(first_meta) =
- cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
+ && let Some(cfg_flag) = meta.cfg_predicate()
{
- let tabstop = edit.make_tabstop_after(snippet_cap);
- editor.add_annotation(first_meta, tabstop);
+ let tabstop = edit.make_placeholder_snippet(snippet_cap);
+ editor.add_annotation(cfg_flag.syntax(), tabstop);
}
-
- editor.add_mappings(make.finish_with_mappings());
edit.add_file_edits(ctx.vfs_file_id(), editor);
};
acc.add(
@@ -301,76 +265,42 @@ fn wrap_cfg_attrs(acc: &mut Assists, ctx: &AssistContext<'_>, attrs: Vec<ast::At
);
Some(())
}
-fn unwrap_cfg_attr(acc: &mut Assists, attr: ast::Attr) -> Option<()> {
- let range = attr.syntax().text_range();
- let meta = attr.meta()?;
- let meta_tt = meta.token_tree()?;
- let mut inner_attrs = Vec::with_capacity(1);
- let mut found_comma = false;
- let mut iter = meta_tt.token_trees_and_tokens().skip(1).peekable();
- while let Some(tt) = iter.next() {
- if let NodeOrToken::Token(token) = &tt {
- if token.kind() == T![')'] {
- break;
- }
- if token.kind() == T![,] {
- found_comma = true;
- continue;
- }
- }
- if !found_comma {
- continue;
- }
- let Some(attr_name) = tt.into_token().and_then(|token| {
- if token.kind() == T![ident] { Some(make::ext::ident_path(token.text())) } else { None }
- }) else {
- continue;
- };
- let next_tt = iter.next()?;
- let meta = match next_tt {
- NodeOrToken::Node(tt) => make::meta_token_tree(attr_name, tt),
- NodeOrToken::Token(token) if token.kind() == T![,] || token.kind() == T![')'] => {
- make::meta_path(attr_name)
- }
- NodeOrToken::Token(token) => {
- let equals = algo::skip_trivia_token(token, syntax::Direction::Next)?;
- if equals.kind() != T![=] {
- return None;
- }
- let expr_token =
- algo::skip_trivia_token(equals.next_token()?, syntax::Direction::Next)
- .and_then(|it| {
- if it.kind().is_literal() {
- Some(make::expr_literal(it.text()))
- } else {
- None
- }
- })?;
- make::meta_expr(attr_name, ast::Expr::Literal(expr_token))
- }
- };
- if attr.excl_token().is_some() {
- inner_attrs.push(make::attr_inner(meta));
- } else {
- inner_attrs.push(make::attr_outer(meta));
- }
- }
- if inner_attrs.is_empty() {
+
+fn unwrap_cfg_attr(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+ meta: ast::CfgAttrMeta,
+) -> Option<()> {
+ let top_attr = ast::Meta::from(meta.clone()).parent_attr()?;
+ let range = top_attr.syntax().text_range();
+ let inner_metas: Vec<ast::Meta> = meta.metas().collect();
+ if inner_metas.is_empty() {
return None;
}
- let handle_source_change = |f: &mut SourceChangeBuilder| {
- let inner_attrs =
- inner_attrs.iter().map(|it| it.to_string()).join(&format!("\n{}", attr.indent_level()));
- f.replace(range, inner_attrs);
- };
+ let is_inner = top_attr.excl_token().is_some();
+ let indent = top_attr.indent_level();
acc.add(
AssistId::refactor("wrap_unwrap_cfg_attr"),
"Extract Inner Attributes from `cfg_attr`",
range,
- handle_source_change,
+ |builder: &mut SourceChangeBuilder| {
+ let editor = builder.make_editor(top_attr.syntax());
+ let make = editor.make();
+ let mut elements = vec![];
+ for (i, meta) in inner_metas.into_iter().enumerate() {
+ if i > 0 {
+ elements.push(make.whitespace(&format!("\n{indent}")).into());
+ }
+ let attr = if is_inner { make.attr_inner(meta) } else { make.attr_outer(meta) };
+ elements.push(attr.syntax().clone().into());
+ }
+ editor.replace_with_many(top_attr.syntax(), elements);
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
+ },
);
Some(())
}
+
#[cfg(test)]
mod tests {
use crate::tests::check_assist;
@@ -388,7 +318,7 @@ mod tests {
}
"#,
r#"
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -422,7 +352,7 @@ mod tests {
"#,
r#"
pub struct Test {
- #[cfg_attr($0, foo)]
+ #[cfg_attr(${0:cfg}, foo)]
test: u32,
}
"#,
@@ -456,7 +386,7 @@ mod tests {
r#"
pub struct Test {
#[other_attr]
- #[cfg_attr($0, foo, bar)]
+ #[cfg_attr(${0:cfg}, foo, bar)]
#[other_attr]
test: u32,
}
@@ -491,7 +421,7 @@ mod tests {
"#,
r#"
pub struct Test {
- #[cfg_attr($0, foo = "bar")]
+ #[cfg_attr(${0:cfg}, foo = "bar")]
test: u32,
}
"#,
@@ -520,7 +450,7 @@ mod tests {
#![no_std$0]
"#,
r#"
- #![cfg_attr($0, no_std)]
+ #![cfg_attr(${0:cfg}, no_std)]
"#,
);
check_assist(
@@ -545,7 +475,7 @@ mod tests {
"#,
r#"
#[derive( Clone, Copy)]
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -561,7 +491,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(Debug))]
+ #[cfg_attr(${0:cfg}, derive(Debug))]
pub struct Test {
test: u32,
}
@@ -580,7 +510,7 @@ mod tests {
"#,
r#"
#[derive( Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
@@ -596,7 +526,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
@@ -615,7 +545,7 @@ mod tests {
"#,
r#"
#[derive(std::fmt::Debug, Clone)]
- #[cfg_attr($0, derive(Copy))]
+ #[cfg_attr(${0:cfg}, derive(Copy))]
pub struct Test {
test: u32,
}
@@ -631,7 +561,7 @@ mod tests {
"#,
r#"
#[derive(Clone, Copy)]
- #[cfg_attr($0, derive(std::fmt::Debug))]
+ #[cfg_attr(${0:cfg}, derive(std::fmt::Debug))]
pub struct Test {
test: u32,
}
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs
index a52bd74d14..135e750ca0 100644
--- a/crates/ide-assists/src/tests.rs
+++ b/crates/ide-assists/src/tests.rs
@@ -208,6 +208,15 @@ pub(crate) fn check_assist_target(
}
#[track_caller]
+pub(crate) fn check_assist_with_label(
+ assist: Handler,
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ label: &str,
+) {
+ check(assist, ra_fixture, ExpectedResult::Label(label), None);
+}
+
+#[track_caller]
pub(crate) fn check_assist_not_applicable(
assist: Handler,
#[rust_analyzer::rust_fixture] ra_fixture: &str,
@@ -307,6 +316,7 @@ enum ExpectedResult<'a> {
Unresolved,
After(&'a str),
Target(&'a str),
+ Label(&'a str),
}
#[track_caller]
@@ -335,7 +345,7 @@ fn check_with_config(
let ctx = AssistContext::new(sema, &config, frange);
let resolve = match expected {
- ExpectedResult::Unresolved => AssistResolveStrategy::None,
+ ExpectedResult::Unresolved | ExpectedResult::Label(_) => AssistResolveStrategy::None,
_ => AssistResolveStrategy::All,
};
let mut acc = Assists::new(&ctx, resolve);
@@ -404,6 +414,9 @@ fn check_with_config(
let range = assist.target;
assert_eq_text!(&text_without_caret[range], target);
}
+ (Some(assist), ExpectedResult::Label(label)) => {
+ assert_eq!(assist.label.to_string(), label);
+ }
(Some(assist), ExpectedResult::Unresolved) => assert!(
assist.source_change.is_none(),
"unresolved assist should not contain source changes"
@@ -411,7 +424,10 @@ fn check_with_config(
(Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
(
None,
- ExpectedResult::After(_) | ExpectedResult::Target(_) | ExpectedResult::Unresolved,
+ ExpectedResult::After(_)
+ | ExpectedResult::Target(_)
+ | ExpectedResult::Label(_)
+ | ExpectedResult::Unresolved,
) => {
panic!("code action is not applicable")
}
@@ -479,6 +495,7 @@ pub fn test_some_range(a: int) -> bool {
expect![[r#"
Extract into...
Replace if let with match
+ Convert to guarded return
"#]]
.assert_eq(&expected);
}
@@ -511,6 +528,7 @@ pub fn test_some_range(a: int) -> bool {
expect![[r#"
Extract into...
Replace if let with match
+ Convert to guarded return
"#]]
.assert_eq(&expected);
}
diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs
index 66d5cf834f..048f3d7ce8 100644
--- a/crates/ide-assists/src/tests/generated.rs
+++ b/crates/ide-assists/src/tests/generated.rs
@@ -463,7 +463,7 @@ fn doctest_convert_closure_to_fn() {
check_doc_test(
"convert_closure_to_fn",
r#####"
-//- minicore: copy
+//- minicore: copy, fn
struct String;
impl String {
fn new() -> Self {}
@@ -3852,7 +3852,7 @@ struct S {
}
"#####,
r#####"
-#[cfg_attr($0, derive(Debug))]
+#[cfg_attr(${0:cfg}, derive(Debug))]
struct S {
field: i32
}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 10057f8681..bf1062d207 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -15,7 +15,6 @@ use ide_db::{
syntax_helpers::{node_ext::preorder_expr, prettify_macro_expansion},
};
use itertools::Itertools;
-use stdx::format_to;
use syntax::{
AstNode, AstToken, Direction, NodeOrToken, SourceFile,
SyntaxKind::*,
@@ -203,11 +202,9 @@ pub fn filter_assoc_items(
/// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it,
/// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got
/// inserted.
-///
-/// Legacy: prefer [`add_trait_assoc_items_to_impl_with_factory`] when a [`SyntaxFactory`] is
-/// available.
#[must_use]
pub fn add_trait_assoc_items_to_impl(
+ make: &SyntaxFactory,
sema: &Semantics<'_, RootDatabase>,
config: &AssistConfig,
original_items: &[InFile<ast::AssocItem>],
@@ -250,95 +247,23 @@ pub fn add_trait_assoc_items_to_impl(
})
.filter_map(|item| match item {
ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let fn_ = fn_.clone_subtree();
- let new_body = make::block_expr(None, Some(expr_fill_default(config)));
- let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone());
- fn_.replace_or_insert_body(&mut fn_editor, new_body.clone_for_update());
- let new_fn_ = fn_editor.finish().new_root().clone();
- ast::AssocItem::cast(new_fn_)
- }
- ast::AssocItem::TypeAlias(type_alias) => {
- let type_alias = type_alias.clone_subtree();
- if let Some(type_bound_list) = type_alias.type_bound_list() {
- let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone());
- type_bound_list.remove(&mut type_alias_editor);
- let type_alias = type_alias_editor.finish().new_root().clone();
- ast::AssocItem::cast(type_alias)
- } else {
- Some(ast::AssocItem::TypeAlias(type_alias))
- }
- }
- item => Some(item),
- })
- .map(|item| AstNodeEdit::indent(&item, new_indent_level))
- .collect()
-}
-
-/// [`SyntaxFactory`]-based variant of [`add_trait_assoc_items_to_impl`].
-#[must_use]
-pub fn add_trait_assoc_items_to_impl_with_factory(
- make: &SyntaxFactory,
- sema: &Semantics<'_, RootDatabase>,
- config: &AssistConfig,
- original_items: &[InFile<ast::AssocItem>],
- trait_: hir::Trait,
- impl_: &ast::Impl,
- target_scope: &hir::SemanticsScope<'_>,
-) -> Vec<ast::AssocItem> {
- let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
- original_items
- .iter()
- .map(|InFile { file_id, value: original_item }| {
- let mut cloned_item = {
- if let Some(macro_file) = file_id.macro_file() {
- let span_map = sema.db.expansion_span_map(macro_file);
- let item_prettified = prettify_macro_expansion(
- sema.db,
- original_item.syntax().clone(),
- &span_map,
- target_scope.krate().into(),
- );
- if let Some(formatted) = ast::AssocItem::cast(item_prettified) {
- return formatted;
- } else {
- stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");
- }
- }
- original_item
- }
- .reset_indent();
-
- if let Some(source_scope) = sema.scope(original_item.syntax()) {
- let transform =
- 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
- })
- .filter_map(|item| match item {
- ast::AssocItem::Fn(fn_) if fn_.body().is_none() => {
- let fn_ = fn_.clone_subtree();
+ let (fn_editor, fn_) = SyntaxEditor::with_ast_node(&fn_);
let fill_expr: ast::Expr = match config.expr_fill_default {
ExprFillDefaultMode::Todo | ExprFillDefaultMode::Default => make.expr_todo(),
ExprFillDefaultMode::Underscore => make.expr_underscore().into(),
};
let new_body = make.block_expr(None::<ast::Stmt>, Some(fill_expr));
- let mut fn_editor = SyntaxEditor::new(fn_.syntax().clone());
- fn_.replace_or_insert_body(&mut fn_editor, new_body);
+ fn_.replace_or_insert_body(&fn_editor, new_body);
let new_fn_ = fn_editor.finish().new_root().clone();
ast::AssocItem::cast(new_fn_)
}
ast::AssocItem::TypeAlias(type_alias) => {
- let type_alias = type_alias.clone_subtree();
+ let (type_alias_editor, type_alias) = SyntaxEditor::with_ast_node(&type_alias);
if let Some(type_bound_list) = type_alias.type_bound_list() {
- let mut type_alias_editor = SyntaxEditor::new(type_alias.syntax().clone());
- type_bound_list.remove(&mut type_alias_editor);
- let type_alias = type_alias_editor.finish().new_root().clone();
- ast::AssocItem::cast(type_alias)
- } else {
- Some(ast::AssocItem::TypeAlias(type_alias))
- }
+ type_bound_list.remove(&type_alias_editor);
+ };
+ let type_alias = type_alias_editor.finish().new_root().clone();
+ ast::AssocItem::cast(type_alias)
}
item => Some(item),
})
@@ -404,10 +329,8 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Ex
Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into())
}
ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
- ast::Expr::ParenExpr(parexpr) => {
- parexpr.expr().map(|e| e.clone_subtree().clone_for_update())
- }
- _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()),
+ ast::Expr::ParenExpr(parexpr) => parexpr.expr(),
+ _ => pe.expr(),
},
ast::Expr::Literal(lit) => match lit.kind() {
ast::LiteralKind::Bool(b) => match b {
@@ -422,10 +345,10 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Ex
pub(crate) fn insert_attributes(
before: impl Element,
- edit: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
attrs: impl IntoIterator<Item = ast::Attr>,
- make: &SyntaxFactory,
) {
+ let make = editor.make();
let mut attrs = attrs.into_iter().peekable();
if attrs.peek().is_none() {
return;
@@ -433,12 +356,10 @@ pub(crate) fn insert_attributes(
let elem = before.syntax_element();
let indent = IndentLevel::from_element(&elem);
let whitespace = format!("\n{indent}");
- edit.insert_all(
- syntax::syntax_editor::Position::before(elem),
- attrs
- .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()])
- .collect(),
- );
+ let elements: Vec<syntax::SyntaxElement> = attrs
+ .flat_map(|attr| [attr.syntax().clone().into(), make.whitespace(&whitespace).into()])
+ .collect();
+ editor.insert_all(syntax::syntax_editor::Position::before(elem), elements);
}
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
@@ -608,104 +529,6 @@ fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool {
false
}
-/// Find the end of the `impl` block for the given `ast::Impl`.
-//
-// FIXME: this partially overlaps with `find_struct_impl`
-pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Option<TextSize> {
- buf.push('\n');
- let end = impl_def
- .assoc_item_list()
- .and_then(|it| it.r_curly_token())?
- .prev_sibling_or_token()?
- .text_range()
- .end();
- Some(end)
-}
-
-/// Generates the surrounding `impl Type { <code> }` including type and lifetime
-/// parameters.
-// FIXME: migrate remaining uses to `generate_impl`
-pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
- generate_impl_text_inner(adt, None, true, code)
-}
-
-fn generate_impl_text_inner(
- adt: &ast::Adt,
- trait_text: Option<&str>,
- trait_is_transitive: bool,
- code: &str,
-) -> String {
- // 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_text {
- // 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_text(trait_));
- }
- };
- // `{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))
- });
-
- // FIXME: use syntax::make & mutable AST apis instead
- // `trait_text` and `code` can't be opaque blobs of text
- let mut buf = String::with_capacity(code.len());
-
- // Copy any cfg attrs from the original adt
- buf.push_str("\n\n");
- let cfg_attrs = adt
- .attrs()
- .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
- cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
-
- // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
- buf.push_str("impl");
- if let Some(generic_params) = &generic_params {
- format_to!(buf, "{generic_params}");
- }
- buf.push(' ');
- if let Some(trait_text) = trait_text {
- buf.push_str(trait_text);
- buf.push_str(" for ");
- }
- buf.push_str(&adt.name().unwrap().text());
- if let Some(generic_params) = generic_params {
- format_to!(buf, "{}", generic_params.to_generic_args());
- }
-
- match adt.where_clause() {
- Some(where_clause) => {
- format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
- }
- None => {
- format_to!(buf, " {{\n{code}\n}}");
- }
- }
-
- buf
-}
-
/// Generates the corresponding `impl Type {}` including type and lifetime
/// parameters.
pub(crate) fn generate_impl_with_item(
@@ -758,6 +581,16 @@ pub(crate) fn generate_trait_impl_intransitive_with_item(
generate_impl_inner_with_factory(make, false, adt, Some(trait_), false, Some(body))
}
+pub(crate) fn generate_trait_impl_with_item(
+ make: &SyntaxFactory,
+ is_unsafe: bool,
+ adt: &ast::Adt,
+ trait_: ast::Type,
+ body: ast::AssocItemList,
+) -> ast::Impl {
+ generate_impl_inner_with_factory(make, is_unsafe, adt, Some(trait_), true, Some(body))
+}
+
fn generate_impl_inner(
is_unsafe: bool,
adt: &ast::Adt,
@@ -806,8 +639,7 @@ fn generate_impl_inner(
let ty = make::ty_path(make::ext::ident_path(&adt.name().unwrap().text()));
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make::impl_trait(
cfg_attrs,
@@ -877,8 +709,7 @@ fn generate_impl_inner_with_factory(
let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into();
- let cfg_attrs =
- adt.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+ let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
match trait_ {
Some(trait_) => make.impl_trait(
cfg_attrs,
@@ -989,28 +820,6 @@ fn generic_param_associated_bounds_with_factory(
trait_where_clause.peek().is_some().then(|| make.where_clause(trait_where_clause))
}
-pub(crate) fn add_method_to_adt(
- builder: &mut SourceChangeBuilder,
- adt: &ast::Adt,
- impl_def: Option<ast::Impl>,
- method: &str,
-) {
- let mut buf = String::with_capacity(method.len() + 2);
- if impl_def.is_some() {
- buf.push('\n');
- }
- buf.push_str(method);
-
- let start_offset = impl_def
- .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
- .unwrap_or_else(|| {
- buf = generate_impl_text(adt, &buf);
- adt.syntax().text_range().end()
- });
-
- builder.insert(start_offset, buf);
-}
-
#[derive(Debug)]
pub(crate) struct ReferenceConversion<'db> {
conversion: ReferenceConversionType,
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
index 87e90e8519..c0ddcb950c 100644
--- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs
+++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -1,7 +1,10 @@
//! This module contains functions to generate default trait impl function bodies where possible.
use hir::TraitRef;
-use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make};
+use syntax::ast::{
+ self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit,
+ syntax_factory::SyntaxFactory,
+};
/// Generate custom trait bodies without default implementation where possible.
///
@@ -11,6 +14,7 @@ use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNod
/// `None` means that generating a custom trait body failed, and the body will remain
/// as `todo!` instead.
pub(crate) fn gen_trait_fn_body(
+ make: &SyntaxFactory,
func: &ast::Fn,
trait_path: &ast::Path,
adt: &ast::Adt,
@@ -20,32 +24,32 @@ pub(crate) fn gen_trait_fn_body(
match trait_path.segment()?.name_ref()?.text().as_str() {
"Clone" => {
stdx::always!(func.name().is_some_and(|name| name.text() == "clone"));
- gen_clone_impl(adt)
+ gen_clone_impl(make, adt)
}
- "Debug" => gen_debug_impl(adt),
- "Default" => gen_default_impl(adt),
+ "Debug" => gen_debug_impl(make, adt),
+ "Default" => gen_default_impl(make, adt),
"Hash" => {
stdx::always!(func.name().is_some_and(|name| name.text() == "hash"));
- gen_hash_impl(adt)
+ gen_hash_impl(make, adt)
}
"PartialEq" => {
stdx::always!(func.name().is_some_and(|name| name.text() == "eq"));
- gen_partial_eq(adt, trait_ref)
+ gen_partial_eq(make, adt, trait_ref)
}
"PartialOrd" => {
stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp"));
- gen_partial_ord(adt, trait_ref)
+ gen_partial_ord(make, adt, trait_ref)
}
_ => None,
}
}
/// Generate a `Clone` impl based on the fields and members of the target type.
-fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
- fn gen_clone_call(target: ast::Expr) -> ast::Expr {
- let method = make::name_ref("clone");
- make::expr_method_call(target, method, make::arg_list(None)).into()
- }
+fn gen_clone_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> {
+ let gen_clone_call = |target: ast::Expr| -> ast::Expr {
+ let method = make.name_ref("clone");
+ make.expr_method_call(target, method, make.arg_list([])).into()
+ };
let expr = match adt {
// `Clone` cannot be derived for unions, so no default impl can be provided.
ast::Adt::Union(_) => return None,
@@ -54,7 +58,7 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
let mut arms = vec![];
for variant in list.variants() {
let name = variant.name()?;
- let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
+ let variant_name = make.path_from_idents(["Self", &format!("{name}")])?;
match variant.field_list() {
// => match self { Self::Name { x } => Self::Name { x: x.clone() } }
@@ -63,19 +67,20 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
let mut fields = vec![];
for field in list.fields() {
let field_name = field.name()?;
- let pat = make::ident_pat(false, false, field_name.clone());
- pats.push(pat.into());
+ let pat = make.ident_pat(false, false, field_name.clone());
+ pats.push(make.record_pat_field_shorthand(pat.into()));
- let path = make::ext::ident_path(&field_name.to_string());
- let method_call = gen_clone_call(make::expr_path(path));
- let name_ref = make::name_ref(&field_name.to_string());
- let field = make::record_expr_field(name_ref, Some(method_call));
+ let path = make.ident_path(&field_name.to_string());
+ let method_call = gen_clone_call(make.expr_path(path));
+ let name_ref = make.name_ref(&field_name.to_string());
+ let field = make.record_expr_field(name_ref, Some(method_call));
fields.push(field);
}
- let pat = make::record_pat(variant_name.clone(), pats.into_iter());
- let fields = make::record_expr_field_list(fields);
- let record_expr = make::record_expr(variant_name, fields).into();
- arms.push(make::match_arm(pat.into(), None, record_expr));
+ let pat_field_list = make.record_pat_field_list(pats, None);
+ let pat = make.record_pat_with_fields(variant_name.clone(), pat_field_list);
+ let fields = make.record_expr_field_list(fields);
+ let record_expr = make.record_expr(variant_name, fields).into();
+ arms.push(make.match_arm(pat.into(), None, record_expr));
}
// => match self { Self::Name(arg1) => Self::Name(arg1.clone()) }
@@ -84,31 +89,30 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
let mut fields = vec![];
for (i, _) in list.fields().enumerate() {
let field_name = format!("arg{i}");
- let pat = make::ident_pat(false, false, make::name(&field_name));
+ let pat = make.ident_pat(false, false, make.name(&field_name));
pats.push(pat.into());
- let f_path = make::expr_path(make::ext::ident_path(&field_name));
+ let f_path = make.expr_path(make.ident_path(&field_name));
fields.push(gen_clone_call(f_path));
}
- let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
- let struct_name = make::expr_path(variant_name);
- let tuple_expr =
- make::expr_call(struct_name, make::arg_list(fields)).into();
- arms.push(make::match_arm(pat.into(), None, tuple_expr));
+ let pat = make.tuple_struct_pat(variant_name.clone(), pats);
+ let struct_name = make.expr_path(variant_name);
+ let tuple_expr = make.expr_call(struct_name, make.arg_list(fields)).into();
+ arms.push(make.match_arm(pat.into(), None, tuple_expr));
}
// => match self { Self::Name => Self::Name }
None => {
- let pattern = make::path_pat(variant_name.clone());
- let variant_expr = make::expr_path(variant_name);
- arms.push(make::match_arm(pattern, None, variant_expr));
+ let pattern = make.path_pat(variant_name.clone());
+ let variant_expr = make.expr_path(variant_name);
+ arms.push(make.match_arm(pattern, None, variant_expr));
}
}
}
- let match_target = make::expr_path(make::ext::ident_path("self"));
- let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- make::expr_match(match_target, list).into()
+ let match_target = make.expr_path(make.ident_path("self"));
+ let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ make.expr_match(match_target, list).into()
}
ast::Adt::Struct(strukt) => {
match strukt.field_list() {
@@ -116,43 +120,43 @@ fn gen_clone_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
Some(ast::FieldList::RecordFieldList(field_list)) => {
let mut fields = vec![];
for field in field_list.fields() {
- let base = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(base, &field.name()?.to_string());
+ let base = make.expr_path(make.ident_path("self"));
+ let target = make.expr_field(base, &field.name()?.to_string()).into();
let method_call = gen_clone_call(target);
- let name_ref = make::name_ref(&field.name()?.to_string());
- let field = make::record_expr_field(name_ref, Some(method_call));
+ let name_ref = make.name_ref(&field.name()?.to_string());
+ let field = make.record_expr_field(name_ref, Some(method_call));
fields.push(field);
}
- let struct_name = make::ext::ident_path("Self");
- let fields = make::record_expr_field_list(fields);
- make::record_expr(struct_name, fields).into()
+ let struct_name = make.ident_path("Self");
+ let fields = make.record_expr_field_list(fields);
+ make.record_expr(struct_name, fields).into()
}
// => Self(self.0.clone(), self.1.clone())
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut fields = vec![];
for (i, _) in field_list.fields().enumerate() {
- let f_path = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(f_path, &format!("{i}"));
+ let f_path = make.expr_path(make.ident_path("self"));
+ let target = make.expr_field(f_path, &format!("{i}")).into();
fields.push(gen_clone_call(target));
}
- let struct_name = make::expr_path(make::ext::ident_path("Self"));
- make::expr_call(struct_name, make::arg_list(fields)).into()
+ let struct_name = make.expr_path(make.ident_path("Self"));
+ make.expr_call(struct_name, make.arg_list(fields)).into()
}
// => Self { }
None => {
- let struct_name = make::ext::ident_path("Self");
- let fields = make::record_expr_field_list(None);
- make::record_expr(struct_name, fields).into()
+ let struct_name = make.ident_path("Self");
+ let fields = make.record_expr_field_list([]);
+ make.record_expr(struct_name, fields).into()
}
}
}
};
- let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ let body = make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
Some(body)
}
/// Generate a `Debug` impl based on the fields and members of the target type.
-fn gen_debug_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
+fn gen_debug_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> {
let annotated_name = adt.name()?;
match adt {
// `Debug` cannot be derived for unions, so no default impl can be provided.
@@ -164,156 +168,154 @@ fn gen_debug_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
let mut arms = vec![];
for variant in list.variants() {
let name = variant.name()?;
- let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?;
- let target = make::expr_path(make::ext::ident_path("f"));
+ let variant_name = make.path_from_idents(["Self", &format!("{name}")])?;
+ let target = make.expr_path(make.ident_path("f"));
match variant.field_list() {
Some(ast::FieldList::RecordFieldList(list)) => {
// => f.debug_struct(name)
- let target = make::expr_path(make::ext::ident_path("f"));
- let method = make::name_ref("debug_struct");
+ let target = make.expr_path(make.ident_path("f"));
+ let method = make.name_ref("debug_struct");
let struct_name = format!("\"{name}\"");
- let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
- let mut expr = make::expr_method_call(target, method, args).into();
+ let args = make.arg_list([make.expr_literal(&struct_name).into()]);
+ let mut expr = make.expr_method_call(target, method, args).into();
let mut pats = vec![];
for field in list.fields() {
let field_name = field.name()?;
// create a field pattern for use in `MyStruct { fields.. }`
- let pat = make::ident_pat(false, false, field_name.clone());
- pats.push(pat.into());
+ let pat = make.ident_pat(false, false, field_name.clone());
+ pats.push(make.record_pat_field_shorthand(pat.into()));
// => <expr>.field("field_name", field)
- let method_name = make::name_ref("field");
- let name = make::expr_literal(&(format!("\"{field_name}\""))).into();
+ let method_name = make.name_ref("field");
+ let name = make.expr_literal(&(format!("\"{field_name}\""))).into();
let path = &format!("{field_name}");
- let path = make::expr_path(make::ext::ident_path(path));
- let args = make::arg_list(vec![name, path]);
- expr = make::expr_method_call(expr, method_name, args).into();
+ let path = make.expr_path(make.ident_path(path));
+ let args = make.arg_list([name, path]);
+ expr = make.expr_method_call(expr, method_name, args).into();
}
// => <expr>.finish()
- let method = make::name_ref("finish");
- let expr =
- make::expr_method_call(expr, method, make::arg_list(None)).into();
+ let method = make.name_ref("finish");
+ let expr = make.expr_method_call(expr, method, make.arg_list([])).into();
// => MyStruct { fields.. } => f.debug_struct("MyStruct")...finish(),
- let pat = make::record_pat(variant_name.clone(), pats.into_iter());
- arms.push(make::match_arm(pat.into(), None, expr));
+ let pat_field_list = make.record_pat_field_list(pats, None);
+ let pat = make.record_pat_with_fields(variant_name.clone(), pat_field_list);
+ arms.push(make.match_arm(pat.into(), None, expr));
}
Some(ast::FieldList::TupleFieldList(list)) => {
// => f.debug_tuple(name)
- let target = make::expr_path(make::ext::ident_path("f"));
- let method = make::name_ref("debug_tuple");
+ let target = make.expr_path(make.ident_path("f"));
+ let method = make.name_ref("debug_tuple");
let struct_name = format!("\"{name}\"");
- let args = make::arg_list(Some(make::expr_literal(&struct_name).into()));
- let mut expr = make::expr_method_call(target, method, args).into();
+ let args = make.arg_list([make.expr_literal(&struct_name).into()]);
+ let mut expr = make.expr_method_call(target, method, args).into();
let mut pats = vec![];
for (i, _) in list.fields().enumerate() {
let name = format!("arg{i}");
// create a field pattern for use in `MyStruct(fields..)`
- let field_name = make::name(&name);
- let pat = make::ident_pat(false, false, field_name.clone());
+ let field_name = make.name(&name);
+ let pat = make.ident_pat(false, false, field_name.clone());
pats.push(pat.into());
// => <expr>.field(field)
- let method_name = make::name_ref("field");
+ let method_name = make.name_ref("field");
let field_path = &name.to_string();
- let field_path = make::expr_path(make::ext::ident_path(field_path));
- let args = make::arg_list(vec![field_path]);
- expr = make::expr_method_call(expr, method_name, args).into();
+ let field_path = make.expr_path(make.ident_path(field_path));
+ let args = make.arg_list([field_path]);
+ expr = make.expr_method_call(expr, method_name, args).into();
}
// => <expr>.finish()
- let method = make::name_ref("finish");
- let expr =
- make::expr_method_call(expr, method, make::arg_list(None)).into();
+ let method = make.name_ref("finish");
+ let expr = make.expr_method_call(expr, method, make.arg_list([])).into();
// => MyStruct (fields..) => f.debug_tuple("MyStruct")...finish(),
- let pat = make::tuple_struct_pat(variant_name.clone(), pats.into_iter());
- arms.push(make::match_arm(pat.into(), None, expr));
+ let pat = make.tuple_struct_pat(variant_name.clone(), pats);
+ arms.push(make.match_arm(pat.into(), None, expr));
}
None => {
- let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into();
- let args = make::ext::token_tree_from_node(
- make::arg_list([target, fmt_string]).syntax(),
- );
- let macro_name = make::ext::ident_path("write");
- let macro_call = make::expr_macro(macro_name, args);
-
- let variant_name = make::path_pat(variant_name);
- arms.push(make::match_arm(variant_name, None, macro_call.into()));
+ let fmt_string = make.expr_literal(&(format!("\"{name}\""))).into();
+ let args =
+ make.token_tree_from_node(make.arg_list([target, fmt_string]).syntax());
+ let macro_name = make.ident_path("write");
+ let macro_call = make.expr_macro(macro_name, args);
+
+ let variant_name = make.path_pat(variant_name);
+ arms.push(make.match_arm(variant_name, None, macro_call.into()));
}
}
}
- let match_target = make::expr_path(make::ext::ident_path("self"));
- let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- let match_expr = make::expr_match(match_target, list);
+ let match_target = make.expr_path(make.ident_path("self"));
+ let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ let match_expr = make.expr_match(match_target, list);
- let body = make::block_expr(None, Some(match_expr.into()));
+ let body = make.block_expr(None::<ast::Stmt>, Some(match_expr.into()));
let body = body.indent(ast::edit::IndentLevel(1));
Some(body)
}
ast::Adt::Struct(strukt) => {
let name = format!("\"{annotated_name}\"");
- let args = make::arg_list(Some(make::expr_literal(&name).into()));
- let target = make::expr_path(make::ext::ident_path("f"));
+ let args = make.arg_list([make.expr_literal(&name).into()]);
+ let target = make.expr_path(make.ident_path("f"));
let expr = match strukt.field_list() {
// => f.debug_struct("Name").finish()
- None => make::expr_method_call(target, make::name_ref("debug_struct"), args).into(),
+ None => make.expr_method_call(target, make.name_ref("debug_struct"), args).into(),
// => f.debug_struct("Name").field("foo", &self.foo).finish()
Some(ast::FieldList::RecordFieldList(field_list)) => {
- let method = make::name_ref("debug_struct");
- let mut expr = make::expr_method_call(target, method, args).into();
+ let method = make.name_ref("debug_struct");
+ let mut expr = make.expr_method_call(target, method, args).into();
for field in field_list.fields() {
let name = field.name()?;
- let f_name = make::expr_literal(&(format!("\"{name}\""))).into();
- let f_path = make::expr_path(make::ext::ident_path("self"));
- let f_path = make::expr_ref(f_path, false);
- let f_path = make::expr_field(f_path, &format!("{name}"));
- let args = make::arg_list([f_name, f_path]);
- expr = make::expr_method_call(expr, make::name_ref("field"), args).into();
+ let f_name = make.expr_literal(&(format!("\"{name}\""))).into();
+ let f_path = make.expr_path(make.ident_path("self"));
+ let f_path = make.expr_field(f_path, &format!("{name}")).into();
+ let f_path = make.expr_ref(f_path, false);
+ let args = make.arg_list([f_name, f_path]);
+ expr = make.expr_method_call(expr, make.name_ref("field"), args).into();
}
expr
}
- // => f.debug_tuple("Name").field(self.0).finish()
+ // => f.debug_tuple("Name").field(&self.0).finish()
Some(ast::FieldList::TupleFieldList(field_list)) => {
- let method = make::name_ref("debug_tuple");
- let mut expr = make::expr_method_call(target, method, args).into();
+ let method = make.name_ref("debug_tuple");
+ let mut expr = make.expr_method_call(target, method, args).into();
for (i, _) in field_list.fields().enumerate() {
- let f_path = make::expr_path(make::ext::ident_path("self"));
- let f_path = make::expr_ref(f_path, false);
- let f_path = make::expr_field(f_path, &format!("{i}"));
- let method = make::name_ref("field");
- expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path)))
- .into();
+ let f_path = make.expr_path(make.ident_path("self"));
+ let f_path = make.expr_field(f_path, &format!("{i}")).into();
+ let f_path = make.expr_ref(f_path, false);
+ let method = make.name_ref("field");
+ expr = make.expr_method_call(expr, method, make.arg_list([f_path])).into();
}
expr
}
};
- let method = make::name_ref("finish");
- let expr = make::expr_method_call(expr, method, make::arg_list(None)).into();
- let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ let method = make.name_ref("finish");
+ let expr = make.expr_method_call(expr, method, make.arg_list([])).into();
+ let body =
+ make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1));
Some(body)
}
}
}
-/// Generate a `Debug` impl based on the fields and members of the target type.
-fn gen_default_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
- fn gen_default_call() -> Option<ast::Expr> {
- let fn_name = make::ext::path_from_idents(["Default", "default"])?;
- Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)).into())
- }
+/// Generate a `Default` impl based on the fields and members of the target type.
+fn gen_default_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> {
+ let gen_default_call = || -> Option<ast::Expr> {
+ let fn_name = make.path_from_idents(["Default", "default"])?;
+ Some(make.expr_call(make.expr_path(fn_name), make.arg_list([])).into())
+ };
match adt {
// `Debug` cannot be derived for unions, so no default impl can be provided.
ast::Adt::Union(_) => None,
@@ -325,42 +327,43 @@ fn gen_default_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
let mut fields = vec![];
for field in field_list.fields() {
let method_call = gen_default_call()?;
- let name_ref = make::name_ref(&field.name()?.to_string());
- let field = make::record_expr_field(name_ref, Some(method_call));
+ let name_ref = make.name_ref(&field.name()?.to_string());
+ let field = make.record_expr_field(name_ref, Some(method_call));
fields.push(field);
}
- let struct_name = make::ext::ident_path("Self");
- let fields = make::record_expr_field_list(fields);
- make::record_expr(struct_name, fields).into()
+ let struct_name = make.ident_path("Self");
+ let fields = make.record_expr_field_list(fields);
+ make.record_expr(struct_name, fields).into()
}
Some(ast::FieldList::TupleFieldList(field_list)) => {
- let struct_name = make::expr_path(make::ext::ident_path("Self"));
+ let struct_name = make.expr_path(make.ident_path("Self"));
let fields = field_list
.fields()
.map(|_| gen_default_call())
.collect::<Option<Vec<ast::Expr>>>()?;
- make::expr_call(struct_name, make::arg_list(fields)).into()
+ make.expr_call(struct_name, make.arg_list(fields)).into()
}
None => {
- let struct_name = make::ext::ident_path("Self");
- let fields = make::record_expr_field_list(None);
- make::record_expr(struct_name, fields).into()
+ let struct_name = make.ident_path("Self");
+ let fields = make.record_expr_field_list([]);
+ make.record_expr(struct_name, fields).into()
}
};
- let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1));
+ let body =
+ make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1));
Some(body)
}
}
}
/// Generate a `Hash` impl based on the fields and members of the target type.
-fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
- fn gen_hash_call(target: ast::Expr) -> ast::Stmt {
- let method = make::name_ref("hash");
- let arg = make::expr_path(make::ext::ident_path("state"));
- let expr = make::expr_method_call(target, method, make::arg_list(Some(arg))).into();
- make::expr_stmt(expr).into()
- }
+fn gen_hash_impl(make: &SyntaxFactory, adt: &ast::Adt) -> Option<ast::BlockExpr> {
+ let gen_hash_call = |target: ast::Expr| -> ast::Stmt {
+ let method = make.name_ref("hash");
+ let arg = make.expr_path(make.ident_path("state"));
+ let expr = make.expr_method_call(target, method, make.arg_list([arg])).into();
+ make.expr_stmt(expr).into()
+ };
let body = match adt {
// `Hash` cannot be derived for unions, so no default impl can be provided.
@@ -368,35 +371,35 @@ fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
// => std::mem::discriminant(self).hash(state);
ast::Adt::Enum(_) => {
- let fn_name = make_discriminant()?;
+ let fn_name = make_discriminant(make)?;
- let arg = make::expr_path(make::ext::ident_path("self"));
- let fn_call = make::expr_call(fn_name, make::arg_list(Some(arg))).into();
+ let arg = make.expr_path(make.ident_path("self"));
+ let fn_call: ast::Expr = make.expr_call(fn_name, make.arg_list([arg])).into();
let stmt = gen_hash_call(fn_call);
- make::block_expr(Some(stmt), None).indent(ast::edit::IndentLevel(1))
+ make.block_expr([stmt], None).indent(ast::edit::IndentLevel(1))
}
ast::Adt::Struct(strukt) => match strukt.field_list() {
// => self.<field>.hash(state);
Some(ast::FieldList::RecordFieldList(field_list)) => {
let mut stmts = vec![];
for field in field_list.fields() {
- let base = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(base, &field.name()?.to_string());
+ let base = make.expr_path(make.ident_path("self"));
+ let target = make.expr_field(base, &field.name()?.to_string()).into();
stmts.push(gen_hash_call(target));
}
- make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
+ make.block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
}
// => self.<field_index>.hash(state);
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut stmts = vec![];
for (i, _) in field_list.fields().enumerate() {
- let base = make::expr_path(make::ext::ident_path("self"));
- let target = make::expr_field(base, &format!("{i}"));
+ let base = make.expr_path(make.ident_path("self"));
+ let target = make.expr_field(base, &format!("{i}")).into();
stmts.push(gen_hash_call(target));
}
- make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
+ make.block_expr(stmts, None).indent(ast::edit::IndentLevel(1))
}
// No fields in the body means there's nothing to hash.
@@ -408,32 +411,37 @@ fn gen_hash_impl(adt: &ast::Adt) -> Option<ast::BlockExpr> {
}
/// Generate a `PartialEq` impl based on the fields and members of the target type.
-fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> {
- fn gen_eq_chain(expr: Option<ast::Expr>, cmp: ast::Expr) -> Option<ast::Expr> {
+fn gen_partial_eq(
+ make: &SyntaxFactory,
+ adt: &ast::Adt,
+ trait_ref: Option<TraitRef<'_>>,
+) -> Option<ast::BlockExpr> {
+ let gen_eq_chain = |expr: Option<ast::Expr>, cmp: ast::Expr| -> Option<ast::Expr> {
match expr {
- Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
+ Some(expr) => Some(make.expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)),
None => Some(cmp),
}
- }
+ };
- fn gen_record_pat_field(field_name: &str, pat_name: &str) -> ast::RecordPatField {
- let pat = make::ext::simple_ident_pat(make::name(pat_name));
- let name_ref = make::name_ref(field_name);
- make::record_pat_field(name_ref, pat.into())
- }
+ let gen_record_pat_field = |field_name: &str, pat_name: &str| -> ast::RecordPatField {
+ let pat = make.ident_pat(false, false, make.name(pat_name));
+ let name_ref = make.name_ref(field_name);
+ make.record_pat_field(name_ref, pat.into())
+ };
- fn gen_record_pat(record_name: ast::Path, fields: Vec<ast::RecordPatField>) -> ast::RecordPat {
- let list = make::record_pat_field_list(fields, None);
- make::record_pat_with_fields(record_name, list)
- }
+ let gen_record_pat =
+ |record_name: ast::Path, fields: Vec<ast::RecordPatField>| -> ast::RecordPat {
+ let list = make.record_pat_field_list(fields, None);
+ make.record_pat_with_fields(record_name, list)
+ };
- fn gen_variant_path(variant: &ast::Variant) -> Option<ast::Path> {
- make::ext::path_from_idents(["Self", &variant.name()?.to_string()])
- }
+ let gen_variant_path = |variant: &ast::Variant| -> Option<ast::Path> {
+ make.path_from_idents(["Self", &variant.name()?.to_string()])
+ };
- fn gen_tuple_field(field_name: &str) -> ast::Pat {
- ast::Pat::IdentPat(make::ident_pat(false, false, make::name(field_name)))
- }
+ let gen_tuple_field = |field_name: &str| -> ast::Pat {
+ ast::Pat::IdentPat(make.ident_pat(false, false, make.name(field_name)))
+ };
// Check that self type and rhs type match. We don't know how to implement the method
// automatically otherwise.
@@ -451,14 +459,14 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
ast::Adt::Enum(enum_) => {
// => std::mem::discriminant(self) == std::mem::discriminant(other)
- let lhs_name = make::expr_path(make::ext::ident_path("self"));
- let lhs = make::expr_call(make_discriminant()?, make::arg_list(Some(lhs_name.clone())))
- .into();
- let rhs_name = make::expr_path(make::ext::ident_path("other"));
- let rhs = make::expr_call(make_discriminant()?, make::arg_list(Some(rhs_name.clone())))
- .into();
+ let lhs_name = make.expr_path(make.ident_path("self"));
+ let lhs =
+ make.expr_call(make_discriminant(make)?, make.arg_list([lhs_name.clone()])).into();
+ let rhs_name = make.expr_path(make.ident_path("other"));
+ let rhs =
+ make.expr_call(make_discriminant(make)?, make.arg_list([rhs_name.clone()])).into();
let eq_check =
- make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+ make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
let mut n_cases = 0;
let mut arms = vec![];
@@ -480,9 +488,9 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
let r_name = &format!("r_{field_name}");
r_fields.push(gen_record_pat_field(&field_name, r_name));
- let lhs = make::expr_path(make::ext::ident_path(l_name));
- let rhs = make::expr_path(make::ext::ident_path(r_name));
- let cmp = make::expr_bin_op(
+ let lhs = make.expr_path(make.ident_path(l_name));
+ let rhs = make.expr_path(make.ident_path(r_name));
+ let cmp = make.expr_bin_op(
lhs,
BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
rhs,
@@ -492,10 +500,10 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
let left = gen_record_pat(gen_variant_path(&variant)?, l_fields);
let right = gen_record_pat(gen_variant_path(&variant)?, r_fields);
- let tuple = make::tuple_pat(vec![left.into(), right.into()]);
+ let tuple = make.tuple_pat(vec![left.into(), right.into()]);
if let Some(expr) = expr {
- arms.push(make::match_arm(tuple.into(), None, expr));
+ arms.push(make.match_arm(tuple.into(), None, expr));
}
}
@@ -513,9 +521,9 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
let r_name = format!("r{field_name}");
r_fields.push(gen_tuple_field(&r_name));
- let lhs = make::expr_path(make::ext::ident_path(&l_name));
- let rhs = make::expr_path(make::ext::ident_path(&r_name));
- let cmp = make::expr_bin_op(
+ let lhs = make.expr_path(make.ident_path(&l_name));
+ let rhs = make.expr_path(make.ident_path(&r_name));
+ let cmp = make.expr_bin_op(
lhs,
BinaryOp::CmpOp(CmpOp::Eq { negated: false }),
rhs,
@@ -523,12 +531,12 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
expr = gen_eq_chain(expr, cmp);
}
- let left = make::tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
- let right = make::tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
- let tuple = make::tuple_pat(vec![left.into(), right.into()]);
+ let left = make.tuple_struct_pat(gen_variant_path(&variant)?, l_fields);
+ let right = make.tuple_struct_pat(gen_variant_path(&variant)?, r_fields);
+ let tuple = make.tuple_pat(vec![left.into(), right.into()]);
if let Some(expr) = expr {
- arms.push(make::match_arm(tuple.into(), None, expr));
+ arms.push(make.match_arm(tuple.into(), None, expr));
}
}
None => continue,
@@ -542,57 +550,57 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
// The fallback arm will be `_ => false,` if we've already gone through every case where the variants of self and other match,
// and `_ => std::mem::discriminant(self) == std::mem::discriminant(other),` otherwise.
if n_cases > 1 {
- let lhs = make::wildcard_pat().into();
+ let lhs = make.wildcard_pat().into();
let rhs = if arms_len == n_cases {
- make::expr_literal("false").into()
+ make.expr_literal("false").into()
} else {
eq_check
};
- arms.push(make::match_arm(lhs, None, rhs));
+ arms.push(make.match_arm(lhs, None, rhs));
}
- let match_target = make::expr_tuple([lhs_name, rhs_name]).into();
- let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- make::expr_match(match_target, list).into()
+ let match_target = make.expr_tuple([lhs_name, rhs_name]).into();
+ let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ make.expr_match(match_target, list).into()
}
};
- make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ make.block_expr(None::<ast::Stmt>, Some(expr)).indent(ast::edit::IndentLevel(1))
}
ast::Adt::Struct(strukt) => match strukt.field_list() {
Some(ast::FieldList::RecordFieldList(field_list)) => {
let mut expr = None;
for field in field_list.fields() {
- let lhs = make::expr_path(make::ext::ident_path("self"));
- let lhs = make::expr_field(lhs, &field.name()?.to_string());
- let rhs = make::expr_path(make::ext::ident_path("other"));
- let rhs = make::expr_field(rhs, &field.name()?.to_string());
+ let lhs = make.expr_path(make.ident_path("self"));
+ let lhs = make.expr_field(lhs, &field.name()?.to_string()).into();
+ let rhs = make.expr_path(make.ident_path("other"));
+ let rhs = make.expr_field(rhs, &field.name()?.to_string()).into();
let cmp =
- make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+ make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
expr = gen_eq_chain(expr, cmp);
}
- make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
+ make.block_expr(None, expr).indent(ast::edit::IndentLevel(1))
}
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut expr = None;
for (i, _) in field_list.fields().enumerate() {
let idx = format!("{i}");
- let lhs = make::expr_path(make::ext::ident_path("self"));
- let lhs = make::expr_field(lhs, &idx);
- let rhs = make::expr_path(make::ext::ident_path("other"));
- let rhs = make::expr_field(rhs, &idx);
+ let lhs = make.expr_path(make.ident_path("self"));
+ let lhs = make.expr_field(lhs, &idx).into();
+ let rhs = make.expr_path(make.ident_path("other"));
+ let rhs = make.expr_field(rhs, &idx).into();
let cmp =
- make::expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
+ make.expr_bin_op(lhs, BinaryOp::CmpOp(CmpOp::Eq { negated: false }), rhs);
expr = gen_eq_chain(expr, cmp);
}
- make::block_expr(None, expr).indent(ast::edit::IndentLevel(1))
+ make.block_expr(None::<ast::Stmt>, expr).indent(ast::edit::IndentLevel(1))
}
// No fields in the body means there's nothing to compare.
None => {
- let expr = make::expr_literal("true").into();
- make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ let expr = make.expr_literal("true").into();
+ make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
}
},
};
@@ -600,29 +608,33 @@ fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast
Some(body)
}
-fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<ast::BlockExpr> {
- fn gen_partial_eq_match(match_target: ast::Expr) -> Option<ast::Stmt> {
+fn gen_partial_ord(
+ make: &SyntaxFactory,
+ adt: &ast::Adt,
+ trait_ref: Option<TraitRef<'_>>,
+) -> Option<ast::BlockExpr> {
+ let gen_partial_eq_match = |match_target: ast::Expr| -> Option<ast::Stmt> {
let mut arms = vec![];
let variant_name =
- make::path_pat(make::ext::path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
- let lhs = make::tuple_struct_pat(make::ext::path_from_idents(["Some"])?, [variant_name]);
- arms.push(make::match_arm(lhs.into(), None, make::expr_empty_block().into()));
+ make.path_pat(make.path_from_idents(["core", "cmp", "Ordering", "Equal"])?);
+ let lhs = make.tuple_struct_pat(make.path_from_idents(["Some"])?, [variant_name]);
+ arms.push(make.match_arm(lhs.into(), None, make.expr_empty_block().into()));
- arms.push(make::match_arm(
- make::ident_pat(false, false, make::name("ord")).into(),
+ arms.push(make.match_arm(
+ make.ident_pat(false, false, make.name("ord")).into(),
None,
- make::expr_return(Some(make::expr_path(make::ext::ident_path("ord")))),
+ make.expr_return(Some(make.expr_path(make.ident_path("ord")))).into(),
));
- let list = make::match_arm_list(arms).indent(ast::edit::IndentLevel(1));
- Some(make::expr_stmt(make::expr_match(match_target, list).into()).into())
- }
+ let list = make.match_arm_list(arms).indent(ast::edit::IndentLevel(1));
+ Some(make.expr_stmt(make.expr_match(match_target, list).into()).into())
+ };
- fn gen_partial_cmp_call(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
- let rhs = make::expr_ref(rhs, false);
- let method = make::name_ref("partial_cmp");
- make::expr_method_call(lhs, method, make::arg_list(Some(rhs))).into()
- }
+ let gen_partial_cmp_call = |lhs: ast::Expr, rhs: ast::Expr| -> ast::Expr {
+ let rhs = make.expr_ref(rhs, false);
+ let method = make.name_ref("partial_cmp");
+ make.expr_method_call(lhs, method, make.arg_list([rhs])).into()
+ };
// Check that self type and rhs type match. We don't know how to implement the method
// automatically otherwise.
@@ -643,10 +655,10 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as
Some(ast::FieldList::RecordFieldList(field_list)) => {
let mut exprs = vec![];
for field in field_list.fields() {
- let lhs = make::expr_path(make::ext::ident_path("self"));
- let lhs = make::expr_field(lhs, &field.name()?.to_string());
- let rhs = make::expr_path(make::ext::ident_path("other"));
- let rhs = make::expr_field(rhs, &field.name()?.to_string());
+ let lhs = make.expr_path(make.ident_path("self"));
+ let lhs = make.expr_field(lhs, &field.name()?.to_string()).into();
+ let rhs = make.expr_path(make.ident_path("other"));
+ let rhs = make.expr_field(rhs, &field.name()?.to_string()).into();
let ord = gen_partial_cmp_call(lhs, rhs);
exprs.push(ord);
}
@@ -656,17 +668,17 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as
.into_iter()
.map(gen_partial_eq_match)
.collect::<Option<Vec<ast::Stmt>>>()?;
- make::block_expr(stmts, tail).indent(ast::edit::IndentLevel(1))
+ make.block_expr(stmts, tail).indent(ast::edit::IndentLevel(1))
}
Some(ast::FieldList::TupleFieldList(field_list)) => {
let mut exprs = vec![];
for (i, _) in field_list.fields().enumerate() {
let idx = format!("{i}");
- let lhs = make::expr_path(make::ext::ident_path("self"));
- let lhs = make::expr_field(lhs, &idx);
- let rhs = make::expr_path(make::ext::ident_path("other"));
- let rhs = make::expr_field(rhs, &idx);
+ let lhs = make.expr_path(make.ident_path("self"));
+ let lhs = make.expr_field(lhs, &idx).into();
+ let rhs = make.expr_path(make.ident_path("other"));
+ let rhs = make.expr_field(rhs, &idx).into();
let ord = gen_partial_cmp_call(lhs, rhs);
exprs.push(ord);
}
@@ -675,13 +687,13 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as
.into_iter()
.map(gen_partial_eq_match)
.collect::<Option<Vec<ast::Stmt>>>()?;
- make::block_expr(stmts, tail).indent(ast::edit::IndentLevel(1))
+ make.block_expr(stmts, tail).indent(ast::edit::IndentLevel(1))
}
// No fields in the body means there's nothing to compare.
None => {
- let expr = make::expr_literal("true").into();
- make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
+ let expr = make.expr_literal("true").into();
+ make.block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1))
}
},
};
@@ -689,6 +701,6 @@ fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option<TraitRef<'_>>) -> Option<as
Some(body)
}
-fn make_discriminant() -> Option<ast::Expr> {
- Some(make::expr_path(make::ext::path_from_idents(["core", "mem", "discriminant"])?))
+fn make_discriminant(make: &SyntaxFactory) -> Option<ast::Expr> {
+ Some(make.expr_path(make.path_from_idents(["core", "mem", "discriminant"])?))
}
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index 1fb1fd4e57..2ed582598b 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -34,7 +34,7 @@ use crate::{
CompletionContext, CompletionItem, CompletionItemKind,
context::{
DotAccess, ItemListKind, NameContext, NameKind, NameRefContext, NameRefKind,
- PathCompletionCtx, PathKind, PatternContext, TypeLocation, Visible,
+ PathCompletionCtx, PathKind, PatternContext, TypeAscriptionTarget, TypeLocation, Visible,
},
item::Builder,
render::{
@@ -45,7 +45,7 @@ use crate::{
macro_::render_macro,
pattern::{render_struct_pat, render_variant_pat},
render_expr, render_field, render_path_resolution, render_pattern_resolution,
- render_tuple_field,
+ render_tuple_field, render_type_keyword_snippet,
type_alias::{render_type_alias, render_type_alias_with_eq},
union_literal::render_union_literal,
},
@@ -104,6 +104,21 @@ impl Completions {
}
}
+ pub(crate) fn add_nameref_keywords_with_type_like(
+ &mut self,
+ ctx: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx<'_>,
+ ) {
+ let mut add_keyword = |kw| {
+ render_type_keyword_snippet(ctx, path_ctx, kw, kw).add_to(self, ctx.db);
+ };
+ ["self::", "crate::"].into_iter().for_each(&mut add_keyword);
+
+ if ctx.depth_from_crate_root > 0 {
+ add_keyword("super::");
+ }
+ }
+
pub(crate) fn add_nameref_keywords(&mut self, ctx: &CompletionContext<'_>) {
["self", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
@@ -112,11 +127,19 @@ impl Completions {
}
}
- pub(crate) fn add_type_keywords(&mut self, ctx: &CompletionContext<'_>) {
- self.add_keyword_snippet(ctx, "fn", "fn($1)");
- self.add_keyword_snippet(ctx, "dyn", "dyn $0");
- self.add_keyword_snippet(ctx, "impl", "impl $0");
- self.add_keyword_snippet(ctx, "for", "for<$1>");
+ pub(crate) fn add_type_keywords(
+ &mut self,
+ ctx: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx<'_>,
+ ) {
+ let mut add_keyword = |kw, snippet| {
+ render_type_keyword_snippet(ctx, path_ctx, kw, snippet).add_to(self, ctx.db);
+ };
+
+ add_keyword("fn", "fn($1)");
+ add_keyword("dyn", "dyn $0");
+ add_keyword("impl", "impl $0");
+ add_keyword("for", "for<$1>");
}
pub(crate) fn add_super_keyword(
@@ -211,17 +234,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_path_resolution(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- path_ctx,
- local_name,
- resolution,
- )
- .build(ctx.db),
- );
+ render_path_resolution(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ resolution,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_pattern_resolution(
@@ -236,15 +255,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_pattern_resolution(
- RenderContext::new(ctx).private_editable(is_private_editable),
- pattern_ctx,
- local_name,
- resolution,
- )
- .build(ctx.db),
- );
+ render_pattern_resolution(
+ RenderContext::new(ctx).private_editable(is_private_editable),
+ pattern_ctx,
+ local_name,
+ resolution,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_enum_variants(
@@ -253,9 +270,6 @@ impl Completions {
path_ctx: &PathCompletionCtx<'_>,
e: hir::Enum,
) {
- if !ctx.check_stability_and_hidden(e) {
- return;
- }
e.variants(ctx.db)
.into_iter()
.for_each(|variant| self.add_enum_variant(ctx, path_ctx, variant, None));
@@ -290,15 +304,13 @@ impl Completions {
Visible::Editable => true,
Visible::No => return,
};
- self.add(
- render_macro(
- RenderContext::new(ctx).private_editable(is_private_editable),
- path_ctx,
- local_name,
- mac,
- )
- .build(ctx.db),
- );
+ render_macro(
+ RenderContext::new(ctx).private_editable(is_private_editable),
+ path_ctx,
+ local_name,
+ mac,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_function(
@@ -314,17 +326,13 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_fn(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- path_ctx,
- local_name,
- func,
- )
- .build(ctx.db),
- );
+ render_fn(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ path_ctx,
+ local_name,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_method(
@@ -341,18 +349,14 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_method(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases),
- dot_access,
- receiver,
- local_name,
- func,
- )
- .build(ctx.db),
- );
+ render_method(
+ RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
+ dot_access,
+ receiver,
+ local_name,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_method_with_import(
@@ -368,19 +372,17 @@ impl Completions {
Visible::No => return,
};
let doc_aliases = ctx.doc_aliases(&func);
- self.add(
- render_method(
- RenderContext::new(ctx)
- .private_editable(is_private_editable)
- .doc_aliases(doc_aliases)
- .import_to_add(Some(import)),
- dot_access,
- None,
- None,
- func,
- )
- .build(ctx.db),
- );
+ render_method(
+ RenderContext::new(ctx)
+ .private_editable(is_private_editable)
+ .doc_aliases(doc_aliases)
+ .import_to_add(Some(import)),
+ dot_access,
+ None,
+ None,
+ func,
+ )
+ .add_to(self, ctx.db);
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
@@ -733,7 +735,7 @@ pub(super) fn complete_name_ref(
match &path_ctx.kind {
PathKind::Expr { expr_ctx } => {
expr::complete_expr_path(acc, ctx, path_ctx, expr_ctx);
- expr::complete_expr(acc, ctx);
+ expr::complete_expr(acc, ctx, path_ctx);
dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx);
item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx);
@@ -747,6 +749,12 @@ pub(super) fn complete_name_ref(
field::complete_field_list_tuple_variant(acc, ctx, path_ctx);
}
TypeLocation::TypeAscription(ascription) => {
+ if let TypeAscriptionTarget::RetType { item: Some(item), .. } =
+ ascription
+ && path_ctx.required_thin_arrow().is_some()
+ {
+ keyword::complete_for_and_where(acc, ctx, &item.clone().into());
+ }
r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
}
TypeLocation::GenericArg { .. }
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs
index 20776f6c49..da1e664f96 100644
--- a/crates/ide-completion/src/completions/attribute.rs
+++ b/crates/ide-completion/src/completions/attribute.rs
@@ -30,6 +30,7 @@ mod lint;
mod macro_use;
mod repr;
+pub(crate) use self::cfg::complete_cfg;
pub(crate) use self::derive::complete_derive_path;
/// Complete inputs to known builtin attributes as well as derive attributes
@@ -37,7 +38,7 @@ pub(crate) fn complete_known_attribute_input(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
&colon_prefix: &bool,
- fake_attribute_under_caret: &ast::Attr,
+ fake_attribute_under_caret: &ast::TokenTreeMeta,
extern_crate: Option<&ast::ExternCrate>,
) -> Option<()> {
let attribute = fake_attribute_under_caret;
@@ -70,7 +71,6 @@ pub(crate) fn complete_known_attribute_input(
lint::complete_lint(acc, ctx, colon_prefix, &existing_lints, &lints);
}
- ["cfg"] | ["cfg_attr"] => cfg::complete_cfg(acc, ctx),
["macro_use"] => macro_use::complete_macro_use(
acc,
ctx,
diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs
index 92cbf411c1..885d1a3075 100644
--- a/crates/ide-completion/src/completions/env_vars.rs
+++ b/crates/ide-completion/src/completions/env_vars.rs
@@ -51,11 +51,10 @@ pub(crate) fn complete_cargo_env_vars(
original: &ast::String,
expanded: &ast::String,
) -> Option<()> {
- let is_in_env_expansion = ctx
- .sema
- .hir_file_for(&expanded.syntax().parent()?)
- .macro_file()
- .is_some_and(|it| it.is_env_or_option_env(ctx.sema.db));
+ let descends = ctx.sema.descend_into_macros_exact_with_file(original.syntax().clone());
+ let macro_file = descends.first()?.file_id.macro_file();
+
+ let is_in_env_expansion = macro_file.is_some_and(|it| it.is_env_or_option_env(ctx.sema.db));
if !is_in_env_expansion {
let call = macro_call_for_string_token(expanded)?;
let makro = ctx.sema.resolve_macro_call(&call)?;
@@ -117,6 +116,47 @@ fn main() {
}
#[test]
+ fn complete_in_expanded_env_macro() {
+ check_edit(
+ "CARGO_BIN_NAME",
+ r#"
+//- minicore: env
+macro_rules! bar {
+ ($($arg:tt)*) => { $($arg)* }
+}
+
+fn main() {
+ let foo = bar!(env!("CA$0"));
+}
+ "#,
+ r#"
+macro_rules! bar {
+ ($($arg:tt)*) => { $($arg)* }
+}
+
+fn main() {
+ let foo = bar!(env!("CARGO_BIN_NAME"));
+}
+ "#,
+ );
+
+ check_edit(
+ "CARGO_BIN_NAME",
+ r#"
+//- minicore: env, fmt
+fn main() {
+ let foo = format_args!("{}", env!("CA$0"));
+}
+ "#,
+ r#"
+fn main() {
+ let foo = format_args!("{}", env!("CARGO_BIN_NAME"));
+}
+ "#,
+ );
+ }
+
+ #[test]
fn doesnt_complete_in_random_strings() {
let fixture = r#"
fn main() {
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs
index 8c532e0f4d..c15c67173e 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -317,7 +317,12 @@ pub(crate) fn complete_expr_path(
}
// synthetic names currently leak out as we lack synthetic hygiene, so filter them
// out here
- ScopeDef::Local(_) => {
+ ScopeDef::Local(_) =>
+ {
+ #[expect(
+ clippy::collapsible_match,
+ reason = "this changes meaning, causing the next arm to be selected"
+ )]
if !name.as_str().starts_with('<') {
acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases)
}
@@ -451,7 +456,11 @@ pub(crate) fn complete_expr_path(
}
}
-pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+pub(crate) fn complete_expr(
+ acc: &mut Completions,
+ ctx: &CompletionContext<'_>,
+ PathCompletionCtx { qualified, .. }: &PathCompletionCtx<'_>,
+) {
let _p = tracing::info_span!("complete_expr").entered();
if !ctx.config.enable_term_search {
@@ -462,6 +471,10 @@ pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>)
return;
}
+ if !matches!(qualified, Qualified::No) {
+ return;
+ }
+
if let Some(ty) = &ctx.expected_type {
// Ignore unit types as they are not very interesting
if ty.is_unit() || ty.is_unknown() {
diff --git a/crates/ide-completion/src/completions/flyimport.rs b/crates/ide-completion/src/completions/flyimport.rs
index 20d01485a4..2cf87baf33 100644
--- a/crates/ide-completion/src/completions/flyimport.rs
+++ b/crates/ide-completion/src/completions/flyimport.rs
@@ -133,9 +133,15 @@ pub(crate) fn import_on_the_fly_path(
let potential_import_name = import_name(ctx);
let qualifier = match qualified {
Qualified::With { path, .. } => Some(path.clone()),
- _ => None,
+ Qualified::TypeAnchor { .. } => return None,
+ Qualified::No | Qualified::Absolute => None,
};
- let import_assets = import_assets_for_path(ctx, &potential_import_name, qualifier.clone())?;
+ let import_assets = import_assets_for_path(
+ ctx,
+ Some(&path_ctx.path),
+ &potential_import_name,
+ qualifier.clone(),
+ )?;
import_on_the_fly(
acc,
@@ -160,7 +166,7 @@ pub(crate) fn import_on_the_fly_pat(
}
let potential_import_name = import_name(ctx);
- let import_assets = import_assets_for_path(ctx, &potential_import_name, None)?;
+ let import_assets = import_assets_for_path(ctx, None, &potential_import_name, None)?;
import_on_the_fly_pat_(
acc,
@@ -402,6 +408,7 @@ fn import_name(ctx: &CompletionContext<'_>) -> String {
fn import_assets_for_path<'db>(
ctx: &CompletionContext<'db>,
+ path: Option<&ast::Path>,
potential_import_name: &str,
qualifier: Option<ast::Path>,
) -> Option<ImportAssets<'db>> {
@@ -411,6 +418,7 @@ fn import_assets_for_path<'db>(
let fuzzy_name_length = potential_import_name.len();
let mut assets_for_path = ImportAssets::for_fuzzy_path(
ctx.module,
+ path,
qualifier,
potential_import_name.to_owned(),
&ctx.sema,
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 5b91e7c456..82baf885dd 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -310,7 +310,7 @@ pub(crate) fn complete_postfix(
if let ast::Expr::Literal(literal) = dot_receiver.clone()
&& let Some(literal_text) = ast::String::cast(literal.token())
{
- add_format_like_completions(acc, ctx, &dot_receiver_including_refs, cap, &literal_text);
+ add_format_like_completions(acc, ctx, dot_receiver, cap, &literal_text, semi);
}
postfix_snippet("return", "return expr", &format!("return {receiver_text}{semi}"))
@@ -402,7 +402,7 @@ fn receiver_accessor(receiver: &ast::Expr) -> ast::Expr {
.unwrap_or_else(|| receiver.clone())
}
-/// Given an `initial_element`, tries to expand it to include deref(s), and then references.
+/// Given an `initial_element`, tries to expand it to include deref(s), not(s), and then references.
/// Returns the expanded expressions, and the added prefix as a string
///
/// For example, if called with the `42` in `&&mut *42`, would return `(&&mut *42, "&&mut *")`.
@@ -410,22 +410,23 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
let mut resulting_element = initial_element.clone();
let mut prefix = String::new();
- let mut found_ref_or_deref = false;
-
- while let Some(parent_deref_element) =
- resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
- && parent_deref_element.op_kind() == Some(ast::UnaryOp::Deref)
+ while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
+ && parent.op_kind() == Some(ast::UnaryOp::Deref)
{
- found_ref_or_deref = true;
- resulting_element = ast::Expr::from(parent_deref_element);
-
+ resulting_element = ast::Expr::from(parent);
prefix.insert(0, '*');
}
+ while let Some(parent) = resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast)
+ && parent.op_kind() == Some(ast::UnaryOp::Not)
+ {
+ resulting_element = ast::Expr::from(parent);
+ prefix.insert(0, '!');
+ }
+
while let Some(parent_ref_element) =
resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
{
- found_ref_or_deref = true;
let last_child_or_token = parent_ref_element.syntax().last_child_or_token();
prefix.insert_str(
0,
@@ -440,13 +441,6 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, String) {
resulting_element = ast::Expr::from(parent_ref_element);
}
- if !found_ref_or_deref {
- // If we do not find any ref/deref expressions, restore
- // all the progress of tree climbing
- prefix.clear();
- resulting_element = initial_element.clone();
- }
-
(resulting_element, prefix)
}
@@ -1133,6 +1127,27 @@ fn main() {
}
#[test]
+ fn postfix_completion_for_nots() {
+ check_edit(
+ "if",
+ r#"
+fn main() {
+ let is_foo = true;
+ !is_foo.$0
+}
+"#,
+ r#"
+fn main() {
+ let is_foo = true;
+ if !is_foo {
+ $0
+}
+}
+"#,
+ )
+ }
+
+ #[test]
fn postfix_completion_for_unsafe() {
postfix_completion_for_block("unsafe");
}
@@ -1287,34 +1302,42 @@ fn main() {
check_edit(
"panic",
r#"fn main() { "Panic with {a}".$0 }"#,
- r#"fn main() { panic!("Panic with {a}") }"#,
+ r#"fn main() { panic!("Panic with {a}"); }"#,
);
check_edit(
"println",
r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".$0 }"#,
- r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#,
+ r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }); }"#,
);
check_edit(
"loge",
r#"fn main() { "{2+2}".$0 }"#,
- r#"fn main() { log::error!("{}", 2+2) }"#,
+ r#"fn main() { log::error!("{}", 2+2); }"#,
);
check_edit(
"logt",
r#"fn main() { "{2+2}".$0 }"#,
- r#"fn main() { log::trace!("{}", 2+2) }"#,
+ r#"fn main() { log::trace!("{}", 2+2); }"#,
);
check_edit(
"logd",
r#"fn main() { "{2+2}".$0 }"#,
- r#"fn main() { log::debug!("{}", 2+2) }"#,
+ r#"fn main() { log::debug!("{}", 2+2); }"#,
+ );
+ check_edit(
+ "logi",
+ r#"fn main() { "{2+2}".$0 }"#,
+ r#"fn main() { log::info!("{}", 2+2); }"#,
+ );
+ check_edit(
+ "logw",
+ r#"fn main() { "{2+2}".$0 }"#,
+ r#"fn main() { log::warn!("{}", 2+2); }"#,
);
- check_edit("logi", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::info!("{}", 2+2) }"#);
- check_edit("logw", r#"fn main() { "{2+2}".$0 }"#, r#"fn main() { log::warn!("{}", 2+2) }"#);
check_edit(
"loge",
r#"fn main() { "{2+2}".$0 }"#,
- r#"fn main() { log::error!("{}", 2+2) }"#,
+ r#"fn main() { log::error!("{}", 2+2); }"#,
);
}
diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs
index 7faa113959..85a8899fd1 100644
--- a/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -40,6 +40,7 @@ static KINDS: &[(&str, &str)] = &[
("logw", "log::warn!"),
("loge", "log::error!"),
];
+static SNIPPET_RETURNS_NON_UNIT: &[&str] = &["format"];
pub(crate) fn add_format_like_completions(
acc: &mut Completions,
@@ -47,6 +48,7 @@ pub(crate) fn add_format_like_completions(
dot_receiver: &ast::Expr,
cap: SnippetCap,
receiver_text: &ast::String,
+ semi: &str,
) {
let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
Some(it) => it,
@@ -64,10 +66,11 @@ pub(crate) fn add_format_like_completions(
let exprs = with_placeholders(exprs);
for (label, macro_name) in KINDS {
+ let semi = if SNIPPET_RETURNS_NON_UNIT.contains(label) { "" } else { semi };
let snippet = if exprs.is_empty() {
- format!(r#"{macro_name}({out})"#)
+ format!(r#"{macro_name}({out}){semi}"#)
} else {
- format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "))
+ format!(r#"{}({}, {}){semi}"#, macro_name, out, exprs.join(", "))
};
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 b44c90757f..5a8881edc7 100644
--- a/crates/ide-completion/src/completions/ra_fixture.rs
+++ b/crates/ide-completion/src/completions/ra_fixture.rs
@@ -22,7 +22,7 @@ pub(crate) fn complete_ra_fixture(
&ctx.sema,
original.clone(),
expanded,
- ctx.config.minicore,
+ &ctx.config.ra_fixture,
&mut |_| {},
)?;
let (virtual_file_id, virtual_offset) = analysis.map_offset_down(ctx.position.offset)?;
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 8ff9c3258e..20bbf0dd8b 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -162,29 +162,27 @@ pub(crate) fn complete_type_path(
}
TypeLocation::GenericArg {
args: Some(arg_list), of_trait: Some(trait_), ..
- } => {
- if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() {
- let arg_idx = arg_list
- .generic_args()
- .filter(|arg| {
- arg.syntax().text_range().end()
- < ctx.original_token.text_range().start()
- })
- .count();
-
- let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true);
- if arg_idx >= n_required_params {
- trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| {
- if let hir::AssocItem::TypeAlias(alias) = it {
- cov_mark::hit!(complete_assoc_type_in_generics_list);
- acc.add_type_alias_with_eq(ctx, alias);
- }
- });
-
- let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
- if arg_idx >= n_params {
- return; // only show assoc types
+ } if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() => {
+ let arg_idx = arg_list
+ .generic_args()
+ .filter(|arg| {
+ arg.syntax().text_range().end()
+ < ctx.original_token.text_range().start()
+ })
+ .count();
+
+ let n_required_params = trait_.type_or_const_param_count(ctx.sema.db, true);
+ if arg_idx >= n_required_params {
+ trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| {
+ if let hir::AssocItem::TypeAlias(alias) = it {
+ cov_mark::hit!(complete_assoc_type_in_generics_list);
+ acc.add_type_alias_with_eq(ctx, alias);
}
+ });
+
+ let n_params = trait_.type_or_const_param_count(ctx.sema.db, false);
+ if arg_idx >= n_params {
+ return; // only show assoc types
}
}
}
@@ -206,8 +204,8 @@ pub(crate) fn complete_type_path(
_ => {}
};
- acc.add_nameref_keywords_with_colon(ctx);
- acc.add_type_keywords(ctx);
+ acc.add_nameref_keywords_with_type_like(ctx, path_ctx);
+ acc.add_type_keywords(ctx, path_ctx);
ctx.process_all_names(&mut |name, def, doc_aliases| {
if scope_def_applicable(def) {
acc.add_path_resolution(ctx, path_ctx, name, def, doc_aliases);
@@ -230,14 +228,14 @@ pub(crate) fn complete_ascribed_type(
TypeAscriptionTarget::Let(pat) | TypeAscriptionTarget::FnParam(pat) => {
ctx.sema.type_of_pat(pat.as_ref()?)
}
- TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType(exp) => {
+ TypeAscriptionTarget::Const(exp) | TypeAscriptionTarget::RetType { body: exp, .. } => {
ctx.sema.type_of_expr(exp.as_ref()?)
}
}?
.adjusted();
if !ty.is_unknown() {
let ty_string = ty.display_source_code(ctx.db, ctx.module.into(), true).ok()?;
- acc.add(render_type_inference(ty_string, ctx));
+ acc.add(render_type_inference(ty_string, ctx, path_ctx));
}
None
}
diff --git a/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs
index 5623257a27..80c1572972 100644
--- a/crates/ide-completion/src/config.rs
+++ b/crates/ide-completion/src/config.rs
@@ -6,8 +6,9 @@
use hir::FindPathConfig;
use ide_db::{
- MiniCore, SnippetCap,
+ SnippetCap,
imports::{import_assets::ImportPathConfig, insert_use::InsertUseConfig},
+ ra_fixture::RaFixtureConfig,
};
use crate::{CompletionFieldsToResolve, snippet::Snippet};
@@ -35,7 +36,7 @@ pub struct CompletionConfig<'a> {
pub fields_to_resolve: CompletionFieldsToResolve,
pub exclude_flyimport: Vec<(String, AutoImportExclusionType)>,
pub exclude_traits: &'a [String],
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index 4fd0348156..b9520e9132 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -4,12 +4,12 @@ mod analysis;
#[cfg(test)]
mod tests;
-use std::iter;
+use std::{iter, sync::LazyLock};
-use base_db::RootQueryDb as _;
+use base_db::toolchain_channel;
use hir::{
DisplayTarget, HasAttrs, InFile, Local, ModuleDef, ModuleSource, Name, PathResolution,
- ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo,
+ ScopeDef, Semantics, SemanticsScope, Symbol, Type, TypeInfo, sym,
};
use ide_db::{
FilePosition, FxHashMap, FxHashSet, RootDatabase, famous_defs::FamousDefs,
@@ -102,6 +102,28 @@ impl PathCompletionCtx<'_> {
}
)
}
+
+ pub(crate) fn required_thin_arrow(&self) -> Option<(&'static str, TextSize)> {
+ let PathKind::Type {
+ location:
+ TypeLocation::TypeAscription(TypeAscriptionTarget::RetType {
+ item: Some(ref fn_item),
+ ..
+ }),
+ } = self.kind
+ else {
+ return None;
+ };
+ if fn_item.ret_type().is_some_and(|it| it.thin_arrow_token().is_some()) {
+ return None;
+ }
+ let ret_type = fn_item.ret_type().and_then(|it| it.ty());
+ match (ret_type, fn_item.param_list()) {
+ (Some(ty), _) => Some(("-> ", ty.syntax().text_range().start())),
+ (None, Some(param)) => Some((" ->", param.syntax().text_range().end())),
+ (None, None) => None,
+ }
+ }
}
/// The kind of path we are completing right now.
@@ -231,7 +253,7 @@ impl TypeLocation {
pub(crate) enum TypeAscriptionTarget {
Let(Option<ast::Pat>),
FnParam(Option<ast::Pat>),
- RetType(Option<ast::Expr>),
+ RetType { body: Option<ast::Expr>, item: Option<ast::Fn> },
Const(Option<ast::Expr>),
}
@@ -386,9 +408,11 @@ pub(crate) enum CompletionAnalysis<'db> {
/// Set if we are currently completing in an unexpanded attribute, this usually implies a builtin attribute like `allow($0)`
UnexpandedAttrTT {
colon_prefix: bool,
- fake_attribute_under_caret: Option<ast::Attr>,
+ fake_attribute_under_caret: Option<ast::TokenTreeMeta>,
extern_crate: Option<ast::ExternCrate>,
},
+ /// Set if we are inside the predicate of a `#[cfg]` or `#[cfg_attr]`.
+ CfgPredicate,
MacroSegment,
}
@@ -577,7 +601,18 @@ impl CompletionContext<'_> {
let Some(attrs) = attrs else {
return true;
};
- !attrs.is_unstable() || self.is_nightly
+ if !attrs.is_unstable() {
+ return true;
+ }
+ if !self.is_nightly {
+ return false;
+ }
+ // Unstable on nightly, but we still don't want to suggest internal features, unless the feature flag is enabled.
+ let Some(unstable_feature) = attrs.unstable_feature(self.db) else {
+ return true;
+ };
+ !INTERNAL_FEATURES.contains(&unstable_feature)
+ || self.krate.is_unstable_feature_enabled(self.db, &unstable_feature)
}
pub(crate) fn check_stability_and_hidden<I>(&self, item: I) -> bool
@@ -715,7 +750,7 @@ impl<'db> CompletionContext<'db> {
// actual completion.
let file_with_fake_ident = {
let (_, edition) = editioned_file_id.unpack(db);
- let parse = db.parse(editioned_file_id);
+ let parse = editioned_file_id.parse(db);
parse.reparse(TextRange::empty(offset), COMPLETION_MARKER, edition).tree()
};
@@ -768,7 +803,7 @@ impl<'db> CompletionContext<'db> {
let containing_function = scope.containing_function();
let edition = krate.edition(db);
- let toolchain = db.toolchain_channel(krate.into());
+ let toolchain = toolchain_channel(db, krate.into());
// `toolchain == None` means we're in some detached files. Since we have no information on
// the toolchain being used, let's just allow unstable items to be listed.
let is_nightly = matches!(toolchain, Some(base_db::ReleaseChannel::Nightly) | None);
@@ -900,3 +935,40 @@ const OP_TRAIT_LANG: &[hir::LangItem] = &[
hir::LangItem::Shr,
hir::LangItem::Sub,
];
+
+// FIXME: Find a way to keep this up to date somehow?
+const INTERNAL_FEATURES_LIST: &[Symbol] = &[
+ sym::abi_unadjusted,
+ sym::allocator_internals,
+ sym::allow_internal_unsafe,
+ sym::allow_internal_unstable,
+ sym::cfg_emscripten_wasm_eh,
+ sym::cfg_target_has_reliable_f16_f128,
+ sym::compiler_builtins,
+ sym::custom_mir,
+ sym::eii_internals,
+ sym::field_representing_type_raw,
+ sym::intrinsics,
+ sym::lang_items,
+ sym::link_cfg,
+ sym::more_maybe_bounds,
+ sym::negative_bounds,
+ sym::pattern_complexity_limit,
+ sym::prelude_import,
+ sym::profiler_runtime,
+ sym::rustc_attrs,
+ sym::staged_api,
+ sym::test_unstable_lint,
+ sym::builtin_syntax,
+ sym::link_llvm_intrinsics,
+ sym::needs_panic_runtime,
+ sym::panic_runtime,
+ sym::pattern_types,
+ sym::rustdoc_internals,
+ sym::contracts_internals,
+ sym::freeze_impls,
+ sym::unsized_fn_params,
+];
+
+static INTERNAL_FEATURES: LazyLock<FxHashSet<Symbol>> =
+ LazyLock::new(|| INTERNAL_FEATURES_LIST.iter().cloned().collect());
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index a3494b964f..58c0f683a3 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -284,9 +284,12 @@ fn expand(
};
// Expand pseudo-derive expansion aka `derive(Debug$0)`
- if let Some((orig_attr, spec_attr)) = attrs {
+ if let Some((orig_attr, spec_attr)) = attrs
+ && let Some(orig_meta) = orig_attr.meta()
+ {
+ // FIXME: Support speculative expansion with `cfg_attr`.
if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_tokens))) = (
- sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
+ sema.expand_derive_as_pseudo_attr_macro(&orig_meta),
sema.speculative_expand_derive_as_pseudo_attr_macro(
&orig_attr,
&spec_attr,
@@ -463,7 +466,9 @@ fn analyze<'db>(
}
// Overwrite the path kind for derives
- if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
+ if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx
+ && let Some(origin_meta) = origin_attr.meta()
+ {
if let Some(ast::NameLike::NameRef(name_ref)) =
find_node_at_offset(&file_with_fake_ident, offset)
{
@@ -473,7 +478,7 @@ fn analyze<'db>(
if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
path_ctx.kind = PathKind::Derive {
existing_derives: sema
- .resolve_derive_macro(&origin_attr)
+ .resolve_derive_macro(&origin_meta)
.into_iter()
.flatten()
.flatten()
@@ -498,7 +503,7 @@ fn analyze<'db>(
let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?;
let p = token.parent()?;
if p.kind() == SyntaxKind::TOKEN_TREE
- && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+ && p.ancestors().any(|it| it.kind() == SyntaxKind::TOKEN_TREE_META)
{
let colon_prefix = previous_non_trivia_token(self_token.clone())
.is_some_and(|it| T![:] == it.kind());
@@ -506,7 +511,7 @@ fn analyze<'db>(
CompletionAnalysis::UnexpandedAttrTT {
fake_attribute_under_caret: fake_ident_token
.parent_ancestors()
- .find_map(ast::Attr::cast),
+ .find_map(ast::TokenTreeMeta::cast),
colon_prefix,
extern_crate: p.ancestors().find_map(ast::ExternCrate::cast),
}
@@ -525,6 +530,13 @@ fn analyze<'db>(
} else {
return None;
}
+ } else if find_node_at_offset::<ast::CfgPredicate>(
+ &speculative_file,
+ speculative_offset,
+ )
+ .is_some()
+ {
+ CompletionAnalysis::CfgPredicate
} else {
return None;
}
@@ -827,6 +839,19 @@ fn expected_type_and_name<'db>(
.map(|c| (Some(c.return_type()), None))
.unwrap_or((None, None))
},
+ ast::Variant(it) => {
+ let is_simple_field = |field: ast::TupleField| {
+ let Some(ty) = field.ty() else { return true };
+ matches!(ty, ast::Type::PathType(_)) && ty.generic_arg_list().is_none()
+ };
+ let is_simple_variant = matches!(
+ it.field_list(),
+ Some(ast::FieldList::TupleFieldList(list))
+ if list.syntax().children_with_tokens().all(|it| it.kind() != T![,])
+ && list.fields().next().is_none_or(is_simple_field)
+ );
+ (None, it.name().filter(|_| is_simple_variant).map(NameOrNameRef::Name))
+ },
ast::Stmt(_) => (None, None),
ast::Item(_) => (None, None),
_ => {
@@ -1158,18 +1183,16 @@ fn classify_name_ref<'db>(
let arg_name = arg_name.text();
for item in trait_.items_with_supertraits(sema.db) {
match item {
- hir::AssocItem::TypeAlias(assoc_ty) => {
- if assoc_ty.name(sema.db).as_str() == arg_name {
+ hir::AssocItem::TypeAlias(assoc_ty)
+ if assoc_ty.name(sema.db).as_str() == arg_name => {
override_location = Some(TypeLocation::AssocTypeEq);
return None;
- }
- },
- hir::AssocItem::Const(const_) => {
- if const_.name(sema.db)?.as_str() == arg_name {
+ },
+ hir::AssocItem::Const(const_)
+ if const_.name(sema.db)?.as_str() == arg_name => {
override_location = Some(TypeLocation::AssocConstEq);
return None;
- }
- },
+ },
_ => (),
}
}
@@ -1265,15 +1288,14 @@ fn classify_name_ref<'db>(
let original = ast::Static::cast(name.syntax().parent()?)?;
TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
},
- ast::RetType(it) => {
- it.thin_arrow_token()?;
+ ast::RetType(_) => {
let parent = match ast::Fn::cast(parent.parent()?) {
Some(it) => it.param_list(),
None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
};
let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?;
- TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! {
+ let body = match_ast! {
match parent {
ast::ClosureExpr(it) => {
it.body()
@@ -1283,7 +1305,9 @@ fn classify_name_ref<'db>(
},
_ => return None,
}
- }))
+ };
+ let item = ast::Fn::cast(parent);
+ TypeLocation::TypeAscription(TypeAscriptionTarget::RetType { body, item })
},
ast::Param(it) => {
it.colon_token()?;
@@ -1566,7 +1590,7 @@ fn classify_name_ref<'db>(
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
// completing inside a qualifier
ast::Path(parent) => {
@@ -1595,7 +1619,7 @@ fn classify_name_ref<'db>(
kind_macro_call(it)?
},
ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::VisibilityInner(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
ast::UseTree(_) => PathKind::Use,
ast::RecordExpr(it) => make_path_kind_expr(it.into()),
_ => return None,
diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs
index 1a9139d855..6abf4f632a 100644
--- a/crates/ide-completion/src/item.rs
+++ b/crates/ide-completion/src/item.rs
@@ -84,7 +84,15 @@ pub struct CompletionItem {
pub ref_match: Option<(CompletionItemRefMode, TextSize)>,
/// The import data to add to completion's edits.
- pub import_to_add: SmallVec<[String; 1]>,
+ pub import_to_add: SmallVec<[CompletionItemImport; 1]>,
+}
+
+#[derive(Clone, UpmapFromRaFixture)]
+pub struct CompletionItemImport {
+ /// The path to import.
+ pub path: String,
+ /// Whether to import `as _`.
+ pub as_underscore: bool,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
@@ -184,6 +192,8 @@ pub struct CompletionRelevance {
pub function: Option<CompletionRelevanceFn>,
/// true when there is an `await.method()` or `iter().method()` completion.
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,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CompletionRelevanceTraitInfo {
@@ -275,6 +285,7 @@ impl CompletionRelevance {
trait_,
function,
is_skipping_completion,
+ has_local_inherent_impl,
} = self;
// only applicable for completions within use items
@@ -347,6 +358,10 @@ impl CompletionRelevance {
score += fn_score;
};
+ if has_local_inherent_impl {
+ score -= 5;
+ }
+
score
}
@@ -450,6 +465,7 @@ impl CompletionItem {
ref_match: None,
imports_to_add: Default::default(),
doc_aliases: vec![],
+ adds_text: None,
edition,
}
}
@@ -480,12 +496,13 @@ impl CompletionItem {
/// A helper to make `CompletionItem`s.
#[must_use]
-#[derive(Clone)]
+#[derive(Debug, Clone)]
pub(crate) struct Builder {
source_range: TextRange,
imports_to_add: SmallVec<[LocatedImport; 1]>,
trait_name: Option<SmolStr>,
doc_aliases: Vec<SmolStr>,
+ adds_text: Option<SmolStr>,
label: SmolStr,
insert_text: Option<String>,
is_snippet: bool,
@@ -526,9 +543,16 @@ impl Builder {
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
let mut detail_left = None;
+ let mut to_detail_left = |args: fmt::Arguments<'_>| {
+ let detail_left = detail_left.get_or_insert_with(String::new);
+ if !detail_left.is_empty() {
+ detail_left.push(' ');
+ }
+ format_to!(detail_left, "{args}")
+ };
if !self.doc_aliases.is_empty() {
let doc_aliases = self.doc_aliases.iter().join(", ");
- detail_left = Some(format!("(alias {doc_aliases})"));
+ to_detail_left(format_args!("(alias {doc_aliases})"));
let lookup_doc_aliases = self
.doc_aliases
.iter()
@@ -548,22 +572,17 @@ impl Builder {
lookup = format_smolstr!("{lookup}{lookup_doc_aliases}");
}
}
+ if let Some(adds_text) = self.adds_text {
+ to_detail_left(format_args!("(adds {})", adds_text.trim()));
+ }
if let [import_edit] = &*self.imports_to_add {
// snippets can have multiple imports, but normal completions only have up to one
- let detail_left = detail_left.get_or_insert_with(String::new);
- format_to!(
- detail_left,
- "{}(use {})",
- if detail_left.is_empty() { "" } else { " " },
+ to_detail_left(format_args!(
+ "(use {})",
import_edit.import_path.display(db, self.edition)
- );
+ ));
} else if let Some(trait_name) = self.trait_name {
- let detail_left = detail_left.get_or_insert_with(String::new);
- format_to!(
- detail_left,
- "{}(as {trait_name})",
- if detail_left.is_empty() { "" } else { " " },
- );
+ to_detail_left(format_args!("(as {trait_name})"));
}
let text_edit = match self.text_edit {
@@ -574,7 +593,18 @@ impl Builder {
let import_to_add = self
.imports_to_add
.into_iter()
- .map(|import| import.import_path.display(db, self.edition).to_string())
+ .map(|import| {
+ let path = import.import_path.display(db, self.edition).to_string();
+ let as_underscore =
+ if let hir::ItemInNs::Types(hir::ModuleDef::Trait(trait_to_import)) =
+ import.item_to_import
+ {
+ trait_to_import.prefer_underscore_import(db)
+ } else {
+ false
+ };
+ CompletionItemImport { path, as_underscore }
+ })
.collect();
CompletionItem {
@@ -613,6 +643,10 @@ impl Builder {
self.doc_aliases = doc_aliases;
self
}
+ pub(crate) fn adds_text(&mut self, adds_text: SmolStr) -> &mut Builder {
+ self.adds_text = Some(adds_text);
+ self
+ }
pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
self.insert_text = Some(insert_text.into());
self
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 69ca2af772..3df511a5ad 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -36,8 +36,8 @@ use crate::{
pub use crate::{
config::{AutoImportExclusionType, CallableSnippets, CompletionConfig},
item::{
- CompletionItem, CompletionItemKind, CompletionItemRefMode, CompletionRelevance,
- CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
+ CompletionItem, CompletionItemImport, CompletionItemKind, CompletionItemRefMode,
+ CompletionRelevance, CompletionRelevancePostfixMatch, CompletionRelevanceReturnType,
CompletionRelevanceTypeMatch,
},
snippet::{Snippet, SnippetScope},
@@ -263,6 +263,7 @@ pub fn completions(
extern_crate.as_ref(),
);
}
+ CompletionAnalysis::CfgPredicate => completions::attribute::complete_cfg(acc, ctx),
CompletionAnalysis::MacroSegment => {
completions::macro_def::complete_macro_segment(acc, ctx);
}
@@ -279,7 +280,7 @@ pub fn resolve_completion_edits(
db: &RootDatabase,
config: &CompletionConfig<'_>,
FilePosition { file_id, offset }: FilePosition,
- imports: impl IntoIterator<Item = String>,
+ imports: impl IntoIterator<Item = CompletionItemImport>,
) -> Option<Vec<TextEdit>> {
let _p = tracing::info_span!("resolve_completion_edits").entered();
let sema = hir::Semantics::new(db);
@@ -298,12 +299,18 @@ pub fn resolve_completion_edits(
let new_ast = scope.clone_for_update();
let mut import_insert = TextEdit::builder();
- imports.into_iter().for_each(|full_import_path| {
- insert_use::insert_use(
- &new_ast,
- make::path_from_text_with_edition(&full_import_path, current_edition),
- &config.insert_use,
- );
+ imports.into_iter().for_each(|import| {
+ 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,
+ full_path,
+ &config.insert_use,
+ current_edition,
+ );
+ } else {
+ insert_use::insert_use(&new_ast, full_path, &config.insert_use);
+ }
});
diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert);
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index d77e793295..a636c0603b 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -10,7 +10,7 @@ pub(crate) mod type_alias;
pub(crate) mod union_literal;
pub(crate) mod variant;
-use hir::{AsAssocItem, HasAttrs, HirDisplay, ModuleDef, ScopeDef, Type};
+use hir::{AsAssocItem, HasAttrs, HirDisplay, Impl, ModuleDef, ScopeDef, Type};
use ide_db::text_edit::TextEdit;
use ide_db::{
RootDatabase, SnippetCap, SymbolKind,
@@ -23,7 +23,9 @@ use syntax::{AstNode, SmolStr, SyntaxKind, TextRange, ToSmolStr, ast, format_smo
use crate::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionItemRefMode,
CompletionRelevance,
- context::{DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext},
+ context::{
+ DotAccess, DotAccessKind, PathCompletionCtx, PathKind, PatternContext, TypeLocation,
+ },
item::{Builder, CompletionRelevanceTypeMatch},
render::{
function::render_fn,
@@ -90,27 +92,32 @@ impl<'a> RenderContext<'a> {
&& self.completion.token.parent().is_some_and(|it| it.kind() == SyntaxKind::MACRO_CALL)
}
- fn is_deprecated(&self, def: impl HasAttrs) -> bool {
- def.attrs(self.db()).is_deprecated()
- }
-
- fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
+ /// Whether `def` is deprecated.
+ ///
+ /// This can happen for two reasons:
+ /// - the def is marked with `#[deprecated]`
+ /// - the def is an assoc item whose trait is deprecated
+ ///
+ /// In order to be able to check for the latter, we'd ideally want to `try_as_dyn<_, dyn AsAssocItem>(def)`
+ /// (see [`try_as_dyn`][]), but that function is currently unstable. Therefore, we employ a hack instead:
+ /// if `def` can be an assoc item, it should be passed to this method as follows:
+ /// ```ignore
+ /// self.is_deprecated(def, Some(def))
+ /// ```
+ /// otherwise, it should be passed as:
+ /// ```ignore
+ /// self.is_deprecated(def, None)
+ /// ```
+ ///
+ /// [`try_as_dyn`]: https://doc.rust-lang.org/std/any/fn.try_as_dyn.html
+ fn is_deprecated(&self, def: impl HasAttrs, def_as_assoc_item: Option<hir::AssocItem>) -> bool {
let db = self.db();
- let assoc = match as_assoc_item.as_assoc_item(db) {
- Some(assoc) => assoc,
- None => return false,
- };
-
- let is_assoc_deprecated = match assoc {
- hir::AssocItem::Function(it) => self.is_deprecated(it),
- hir::AssocItem::Const(it) => self.is_deprecated(it),
- hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
- };
- is_assoc_deprecated
- || assoc
- .container_or_implemented_trait(db)
- .map(|trait_| self.is_deprecated(trait_))
- .unwrap_or(false)
+ def.attrs(db).is_deprecated()
+ || def_as_assoc_item
+ .and_then(|assoc| assoc.container_or_implemented_trait(db))
+ .is_some_and(|trait_| {
+ self.is_deprecated(trait_, None /* traits can't be assoc items */)
+ })
}
// FIXME: remove this
@@ -127,7 +134,7 @@ pub(crate) fn render_field(
ty: &hir::Type<'_>,
) -> CompletionItem {
let db = ctx.db();
- let is_deprecated = ctx.is_deprecated(field);
+ let is_deprecated = ctx.is_deprecated(field, None /* fields can't be assoc items */);
let name = field.name(db);
let (name, escaped_name) =
(name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr());
@@ -220,13 +227,15 @@ pub(crate) fn render_tuple_field(
pub(crate) fn render_type_inference(
ty_string: String,
ctx: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx<'_>,
) -> CompletionItem {
let mut builder = CompletionItem::new(
CompletionItemKind::InferredType,
ctx.source_range(),
- ty_string,
+ &ty_string,
ctx.edition,
);
+ adds_ret_type_arrow(ctx, path_ctx, &mut builder, ty_string);
builder.set_relevance(CompletionRelevance {
type_match: Some(CompletionRelevanceTypeMatch::Exact),
exact_name_match: true,
@@ -420,16 +429,16 @@ fn render_resolution_path(
}
let completion = ctx.completion;
+ let module = completion.module;
let cap = ctx.snippet_cap();
let db = completion.db;
let config = completion.config;
let requires_import = import_to_add.is_some();
- let name = local_name.display_no_db(ctx.completion.edition).to_smolstr();
+ let name = local_name.display(db, completion.edition).to_smolstr();
let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution);
- if local_name.needs_escape(completion.edition) {
- item.insert_text(local_name.display_no_db(completion.edition).to_smolstr());
- }
+ let mut insert_text = name.clone();
+
// Add `<>` for generic types
let type_path_no_ty_args = matches!(
path_ctx,
@@ -446,12 +455,14 @@ fn render_resolution_path(
if has_non_default_type_params {
cov_mark::hit!(inserts_angle_brackets_for_generics);
+ insert_text = format_smolstr!("{insert_text}<$0>");
item.lookup_by(name.clone())
.label(SmolStr::from_iter([&name, "<…>"]))
.trigger_call_info()
- .insert_snippet(cap, format!("{}<$0>", local_name.display(db, completion.edition)));
+ .insert_snippet(cap, ""); // set is snippet
}
}
+ adds_ret_type_arrow(completion, path_ctx, &mut item, insert_text.into());
let mut set_item_relevance = |ty: Type<'_>| {
if !ty.is_unknown() {
@@ -463,6 +474,7 @@ fn render_resolution_path(
exact_name_match: compute_exact_name_match(completion, &name),
is_local: matches!(resolution, ScopeDef::Local(_)),
requires_import,
+ has_local_inherent_impl: compute_has_local_inherent_impl(db, path_ctx, &ty, module),
..CompletionRelevance::default()
});
@@ -569,14 +581,61 @@ fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentati
}
fn scope_def_is_deprecated(ctx: &RenderContext<'_>, resolution: ScopeDef) -> bool {
+ let db = ctx.db();
match resolution {
- ScopeDef::ModuleDef(it) => ctx.is_deprecated_assoc_item(it),
- ScopeDef::GenericParam(it) => ctx.is_deprecated(it),
- ScopeDef::AdtSelfType(it) => ctx.is_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 */)
+ }
+ ScopeDef::AdtSelfType(it) => {
+ ctx.is_deprecated(it, None /* `Self` can't be an assoc item */)
+ }
_ => false,
}
}
+pub(crate) fn render_type_keyword_snippet(
+ ctx: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx<'_>,
+ label: &str,
+ snippet: &str,
+) -> Builder {
+ let source_range = ctx.source_range();
+ let mut item =
+ CompletionItem::new(CompletionItemKind::Keyword, source_range, label, ctx.edition);
+
+ let insert_text = if !snippet.contains('$') {
+ item.insert_text(snippet);
+ snippet
+ } else if let Some(cap) = ctx.config.snippet_cap {
+ item.insert_snippet(cap, snippet);
+ snippet
+ } else {
+ label
+ };
+
+ adds_ret_type_arrow(ctx, path_ctx, &mut item, insert_text.to_owned());
+ item
+}
+
+fn adds_ret_type_arrow(
+ ctx: &CompletionContext<'_>,
+ path_ctx: &PathCompletionCtx<'_>,
+ item: &mut Builder,
+ insert_text: String,
+) {
+ if let Some((arrow, at)) = path_ctx.required_thin_arrow() {
+ let mut edit = TextEdit::builder();
+
+ edit.insert(at, arrow.to_owned());
+ edit.replace(ctx.source_range(), insert_text);
+
+ item.text_edit(edit.finish()).adds_text(SmolStr::new_static(arrow));
+ } else {
+ item.insert_text(insert_text);
+ }
+}
+
// FIXME: This checks types without possible coercions which some completions might want to do
fn match_types(
ctx: &CompletionContext<'_>,
@@ -615,6 +674,18 @@ fn compute_type_match(
match_types(ctx, expected_type, completion_ty)
}
+fn compute_has_local_inherent_impl(
+ db: &RootDatabase,
+ path_ctx: &PathCompletionCtx<'_>,
+ completion_ty: &hir::Type<'_>,
+ curr_module: hir::Module,
+) -> bool {
+ matches!(path_ctx.kind, PathKind::Type { location: TypeLocation::ImplTarget })
+ && Impl::all_for_type(db, completion_ty.clone())
+ .iter()
+ .any(|imp| imp.trait_(db).is_none() && imp.module(db) == curr_module)
+}
+
fn compute_exact_name_match(ctx: &CompletionContext<'_>, completion_name: &str) -> bool {
ctx.expected_name.as_ref().is_some_and(|name| name.text() == completion_name)
}
@@ -672,7 +743,7 @@ fn path_ref_match(
// FIXME: This might create inconsistent completions where we show a ref match in macro inputs
// as long as nothing was typed yet
if let Some(ref_mode) = compute_ref_match(completion, ty) {
- item.ref_match(ref_mode, completion.position.offset);
+ item.ref_match(ref_mode, completion.source_range().start());
}
}
}
@@ -787,6 +858,7 @@ mod tests {
),
(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"),
]
.into_iter()
.filter_map(|(cond, desc)| if cond { Some(desc) } else { None })
@@ -1169,6 +1241,7 @@ fn main() { Foo::Fo$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1219,6 +1292,7 @@ fn main() { Foo::Fo$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1362,6 +1436,7 @@ fn main() { Foo::Fo$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1445,6 +1520,7 @@ fn main() { let _: m::Spam = S$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1481,6 +1557,7 @@ fn main() { let _: m::Spam = S$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1494,6 +1571,32 @@ fn main() { let _: m::Spam = S$0 }
check(
r#"
#[deprecated]
+mod something_deprecated {}
+
+fn main() { som$0 }
+"#,
+ SymbolKind::Module,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "something_deprecated",
+ detail_left: None,
+ detail_right: None,
+ source_range: 55..58,
+ delete: 55..58,
+ insert: "something_deprecated",
+ kind: SymbolKind(
+ Module,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
fn something_deprecated() {}
fn main() { som$0 }
@@ -1538,8 +1641,293 @@ fn main() { som$0 }
check(
r#"
+#[deprecated]
+struct A;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Struct,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "A",
+ ),
+ source_range: 37..38,
+ delete: 37..38,
+ insert: "A",
+ kind: SymbolKind(
+ Struct,
+ ),
+ detail: "A",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+enum A {}
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Enum,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "A",
+ ),
+ source_range: 37..38,
+ delete: 37..38,
+ insert: "A",
+ kind: SymbolKind(
+ Enum,
+ ),
+ detail: "A",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+enum A {
+ Okay,
+ #[deprecated]
+ Old,
+}
+
+fn main() { A::$0 }
+"#,
+ SymbolKind::Variant,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "Okay",
+ detail_left: None,
+ detail_right: Some(
+ "Okay",
+ ),
+ source_range: 64..64,
+ delete: 64..64,
+ insert: "Okay$0",
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: "Okay",
+ 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: Some(
+ CompletionRelevanceFn {
+ has_params: false,
+ has_self_param: false,
+ return_type: DirectConstructor,
+ },
+ ),
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ },
+ trigger_call_info: true,
+ },
+ CompletionItem {
+ label: "Old",
+ detail_left: None,
+ detail_right: Some(
+ "Old",
+ ),
+ source_range: 64..64,
+ delete: 64..64,
+ insert: "Old$0",
+ kind: SymbolKind(
+ Variant,
+ ),
+ detail: "Old",
+ 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: Some(
+ CompletionRelevanceFn {
+ has_params: false,
+ has_self_param: false,
+ return_type: DirectConstructor,
+ },
+ ),
+ is_skipping_completion: false,
+ has_local_inherent_impl: false,
+ },
+ trigger_call_info: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+const A: i32 = 0;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Const,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "i32",
+ ),
+ source_range: 45..46,
+ delete: 45..46,
+ insert: "A",
+ kind: SymbolKind(
+ Const,
+ ),
+ detail: "i32",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+static A: i32 = 0;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::Static,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: Some(
+ "i32",
+ ),
+ source_range: 46..47,
+ delete: 46..47,
+ insert: "A",
+ kind: SymbolKind(
+ Static,
+ ),
+ detail: "i32",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+trait A {}
+
+impl A$0
+"#,
+ SymbolKind::Trait,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: None,
+ source_range: 31..32,
+ delete: 31..32,
+ insert: "A",
+ kind: SymbolKind(
+ Trait,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+type A = i32;
+
+fn main() { A$0 }
+"#,
+ SymbolKind::TypeAlias,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "A",
+ detail_left: None,
+ detail_right: None,
+ source_range: 41..42,
+ delete: 41..42,
+ insert: "A",
+ kind: SymbolKind(
+ TypeAlias,
+ ),
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
+#[deprecated]
+macro_rules! a { _ => {}}
+
+fn main() { a$0 }
+"#,
+ SymbolKind::Macro,
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "a!(…)",
+ detail_left: None,
+ detail_right: Some(
+ "macro_rules! a",
+ ),
+ source_range: 53..54,
+ delete: 53..54,
+ insert: "a!($0)",
+ kind: SymbolKind(
+ Macro,
+ ),
+ lookup: "a!",
+ detail: "macro_rules! a",
+ deprecated: true,
+ },
+ ]
+ "#]],
+ );
+
+ check(
+ r#"
struct A { #[deprecated] the_field: u32 }
-fn foo() { A { the$0 } }
+
+fn main() { A { the$0 } }
"#,
SymbolKind::Field,
expect![[r#"
@@ -1550,8 +1938,8 @@ fn foo() { A { the$0 } }
detail_right: Some(
"u32",
),
- source_range: 57..60,
- delete: 57..60,
+ source_range: 59..62,
+ delete: 59..62,
insert: "the_field",
kind: SymbolKind(
Field,
@@ -1571,6 +1959,7 @@ fn foo() { A { the$0 } }
postfix_match: None,
function: None,
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
]
@@ -1630,6 +2019,7 @@ impl S {
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
CompletionItem {
@@ -1721,6 +2111,7 @@ use self::E::*;
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
trigger_call_info: true,
},
@@ -1791,6 +2182,7 @@ fn foo(s: S) { s.$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
]
@@ -2003,6 +2395,7 @@ fn f() -> i32 {
postfix_match: None,
function: None,
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
]
@@ -2071,6 +2464,53 @@ fn go(world: &WorldSnapshot) { go(w$0) }
}
#[test]
+ fn complete_ref_match_after_keyword_prefix() {
+ // About https://github.com/rust-lang/rust-analyzer/issues/15139
+ check_kinds(
+ r#"
+fn foo(data: &i32) {}
+fn main() {
+ let indent = 2i32;
+ foo(in$0)
+}
+"#,
+ &[CompletionItemKind::SymbolKind(SymbolKind::Local)],
+ expect![[r#"
+ [
+ CompletionItem {
+ label: "indent",
+ detail_left: None,
+ detail_right: Some(
+ "i32",
+ ),
+ source_range: 65..67,
+ delete: 65..67,
+ insert: "indent",
+ kind: SymbolKind(
+ Local,
+ ),
+ detail: "i32",
+ relevance: CompletionRelevance {
+ exact_name_match: false,
+ type_match: None,
+ is_local: true,
+ 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,
+ },
+ ref_match: "&@65",
+ },
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
fn too_many_arguments() {
cov_mark::check!(too_many_arguments);
check_relevance(
@@ -2149,6 +2589,48 @@ fn f() {
}
#[test]
+ fn score_has_local_inherent_impl() {
+ check_relevance(
+ r#"
+trait Foob {}
+struct Fooa {}
+impl Fooa {}
+
+impl Foo$0
+"#,
+ expect![[r#"
+ tt Foob []
+ st Fooa Fooa [has_local_inherent_impl]
+ "#]],
+ );
+
+ // inherent impl in different modules, not trigger `has_local_inherent_impl`
+ check_relevance(
+ r#"
+trait Foob {}
+struct Fooa {}
+
+mod a {
+ use super::*;
+ impl Fooa {}
+}
+
+mod b {
+ use super::*;
+ impl Foo$0
+}
+
+"#,
+ expect![[r#"
+ st Fooa Fooa []
+ tt Foob []
+ md a []
+ md b []
+ "#]],
+ );
+ }
+
+ #[test]
fn test_avoid_redundant_suggestion() {
check_relevance(
r#"
@@ -2166,7 +2648,6 @@ fn bb()-> &'static aa {
}
"#,
expect![[r#"
- ex bb() [type]
fn from_bytes(…) fn(&[u8]) -> &aa [type_could_unify]
"#]],
);
@@ -2817,6 +3298,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 }
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
ref_match: "&@107",
},
@@ -2904,6 +3386,7 @@ fn foo() {
postfix_match: None,
function: None,
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
]
@@ -2962,6 +3445,7 @@ fn main() {
},
),
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
ref_match: "&@92",
},
@@ -3045,6 +3529,57 @@ fn main() {
}
#[test]
+ fn enum_variant_name_exact_match_is_high_priority() {
+ check_relevance(
+ r#"
+struct Other;
+struct String;
+enum Foo {
+ String($0)
+}
+ "#,
+ expect![[r#"
+ st String String [name]
+ en Foo Foo []
+ st Other Other []
+ sp Self Foo []
+ "#]],
+ );
+
+ check_relevance(
+ r#"
+struct Other;
+struct String;
+enum Foo {
+ String(String, $0)
+}
+ "#,
+ expect![[r#"
+ en Foo Foo []
+ st Other Other []
+ sp Self Foo []
+ st String String []
+ "#]],
+ );
+
+ check_relevance(
+ r#"
+struct Other;
+struct Vec<T>(T);
+enum Foo {
+ Vec(Vec<$0>)
+}
+ "#,
+ expect![[r#"
+ en Foo Foo []
+ st Other Other []
+ sp Self Foo []
+ st Vec<…> Vec<{unknown}> []
+ "#]],
+ );
+ }
+
+ #[test]
fn postfix_inexact_match_is_low_priority() {
cov_mark::check!(postfix_inexact_match_is_low_priority);
check_relevance_for_kinds(
@@ -3381,6 +3916,7 @@ fn main() {
postfix_match: None,
function: None,
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
CompletionItem {
@@ -3415,6 +3951,7 @@ fn main() {
postfix_match: None,
function: None,
is_skipping_completion: false,
+ has_local_inherent_impl: false,
},
},
]
diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs
index 707a8aed4f..134a77a899 100644
--- a/crates/ide-completion/src/render/const_.rs
+++ b/crates/ide-completion/src/render/const_.rs
@@ -21,7 +21,7 @@ fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option<CompletionItem>
let mut item =
CompletionItem::new(SymbolKind::Const, ctx.source_range(), name, ctx.completion.edition);
item.set_documentation(ctx.docs(const_))
- .set_deprecated(ctx.is_deprecated(const_) || ctx.is_deprecated_assoc_item(const_))
+ .set_deprecated(ctx.is_deprecated(const_, const_.as_assoc_item(db)))
.detail(detail)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs
index dfa30841e7..18151cffcd 100644
--- a/crates/ide-completion/src/render/function.rs
+++ b/crates/ide-completion/src/render/function.rs
@@ -147,7 +147,7 @@ fn render(
detail(ctx.completion, func)
};
item.set_documentation(ctx.docs(func))
- .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func))
+ .set_deprecated(ctx.is_deprecated(func, func.as_assoc_item(db)))
.detail(detail)
.lookup_by(name.as_str().to_smolstr());
diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs
index 6e49af980a..b7de3da468 100644
--- a/crates/ide-completion/src/render/literal.rs
+++ b/crates/ide-completion/src/render/literal.rs
@@ -189,8 +189,12 @@ impl Variant {
fn is_deprecated(self, ctx: &RenderContext<'_>) -> bool {
match self {
- Variant::Struct(it) => ctx.is_deprecated(it),
- Variant::EnumVariant(it) => ctx.is_deprecated(it),
+ 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 */)
+ }
}
}
diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs
index 8cdeb8abbf..ff4cf9a75b 100644
--- a/crates/ide-completion/src/render/macro_.rs
+++ b/crates/ide-completion/src/render/macro_.rs
@@ -64,7 +64,7 @@ fn render(
label(&ctx, needs_bang, bra, ket, &name.to_smolstr()),
completion.edition,
);
- item.set_deprecated(ctx.is_deprecated(macro_))
+ item.set_deprecated(ctx.is_deprecated(macro_, None /* macros can't be assoc items */))
.detail(macro_.display(completion.db, completion.display_target).to_string())
.set_documentation(docs)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs
index fb35d7b9b6..022e97e4f7 100644
--- a/crates/ide-completion/src/render/pattern.rs
+++ b/crates/ide-completion/src/render/pattern.rs
@@ -126,7 +126,9 @@ fn build_completion(
ctx.completion.edition,
);
item.set_documentation(ctx.docs(def))
- .set_deprecated(ctx.is_deprecated(def))
+ .set_deprecated(
+ ctx.is_deprecated(def, None /* the two current `def` arguments to this function, `Struct` and `EnumVariant`, both can't be assoc items */),
+ )
.detail(&pat)
.lookup_by(lookup)
.set_relevance(relevance);
diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs
index 3fc0f369e5..2b79ca2deb 100644
--- a/crates/ide-completion/src/render/type_alias.rs
+++ b/crates/ide-completion/src/render/type_alias.rs
@@ -47,7 +47,7 @@ fn render(
ctx.completion.edition,
);
item.set_documentation(ctx.docs(type_alias))
- .set_deprecated(ctx.is_deprecated(type_alias) || ctx.is_deprecated_assoc_item(type_alias))
+ .set_deprecated(ctx.is_deprecated(type_alias, type_alias.as_assoc_item(db)))
.detail(detail)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs
index 23f0d4e06f..7164c94fde 100644
--- a/crates/ide-completion/src/render/union_literal.rs
+++ b/crates/ide-completion/src/render/union_literal.rs
@@ -95,7 +95,7 @@ pub(crate) fn render_union_literal(
);
item.set_documentation(ctx.docs(un))
- .set_deprecated(ctx.is_deprecated(un))
+ .set_deprecated(ctx.is_deprecated(un, None /* unions can't be assoc items */))
.detail(detail)
.set_relevance(ctx.completion_relevance());
diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs
index cb1adfcfb6..02e299b2a9 100644
--- a/crates/ide-completion/src/tests.rs
+++ b/crates/ide-completion/src/tests.rs
@@ -29,8 +29,9 @@ use expect_test::Expect;
use hir::db::HirDatabase;
use hir::{PrefixKind, setup_tracing};
use ide_db::{
- FilePosition, MiniCore, RootDatabase, SnippetCap,
+ FilePosition, RootDatabase, SnippetCap,
imports::insert_use::{ImportGranularity, InsertUseConfig},
+ ra_fixture::RaFixtureConfig,
};
use itertools::Itertools;
use stdx::{format_to, trim_indent};
@@ -90,7 +91,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig {
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String {
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index 8e50ef10ec..294434297e 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1030,8 +1030,6 @@ fn main() {
"#,
expect![[r#"
fn test() fn() -> Zulu
- ex Zulu
- ex Zulu::test()
"#]],
);
}
@@ -2275,7 +2273,7 @@ fn main() {
$0
}
//- /std.rs crate:std
-#[unstable]
+#[unstable(feature = "some_non_internal_feature")]
pub struct UnstableButWeAreOnNightlyAnyway;
"#,
expect![[r#"
@@ -2320,6 +2318,112 @@ pub struct UnstableButWeAreOnNightlyAnyway;
}
#[test]
+fn expr_unstable_item_internal_feature() {
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable(feature = "intrinsics")]
+pub mod intrinsics {}
+ "#,
+ expect![[r#"
+ fn main() fn()
+ md std
+ 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
+ "#]],
+ );
+ check(
+ r#"
+//- toolchain:nightly
+//- /main.rs crate:main deps:std
+#![feature(intrinsics)]
+use std::*;
+fn main() {
+ $0
+}
+//- /std.rs crate:std
+#[unstable(feature = "intrinsics")]
+pub mod intrinsics {}
+ "#,
+ expect![[r#"
+ fn main() fn()
+ md intrinsics
+ md std
+ 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
+ "#]],
+ );
+}
+
+#[test]
fn inside_format_args_completions_work() {
check(
r#"
diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index d7db896679..60ae077d01 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1243,6 +1243,39 @@ impl Bar for Foo {
}
#[test]
+fn no_flyimports_type_anchor() {
+ check(
+ r#"
+mod m {
+ pub fn foo() {}
+}
+struct Bar;
+trait Foo {}
+impl Foo for Bar {}
+fn main() {
+ <Bar as Foo>::foo$0
+}
+ "#,
+ expect![[r#""#]],
+ );
+
+ check(
+ r#"
+mod m {
+ pub fn foo() {}
+}
+struct Bar;
+trait Foo {}
+impl Foo for Bar {}
+fn main() {
+ <Bar>::foo$0
+}
+ "#,
+ expect![[r#""#]],
+ );
+}
+
+#[test]
fn no_inherent_candidates_proposed() {
check(
r#"
@@ -2024,3 +2057,38 @@ fn main() {
"#,
);
}
+
+#[test]
+fn prefer_underscore_import() {
+ check_edit(
+ "bar",
+ r#"
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.bar$0
+}
+ "#,
+ r#"
+use foo::Ext as _;
+
+mod foo {
+ #[rust_analyzer::prefer_underscore_import]
+ pub trait Ext {
+ fn bar(&self) {}
+ }
+ impl<T> Ext for T {}
+}
+
+fn baz() {
+ 1.bar();$0
+}
+ "#,
+ );
+}
diff --git a/crates/ide-completion/src/tests/item.rs b/crates/ide-completion/src/tests/item.rs
index 61a9da8c27..45024ad216 100644
--- a/crates/ide-completion/src/tests/item.rs
+++ b/crates/ide-completion/src/tests/item.rs
@@ -116,8 +116,23 @@ fn completes_where() {
check_with_base_items(
r"fn func() $0",
expect![[r#"
- kw where
- "#]],
+ en Enum (adds ->) Enum
+ ma makro!(…) macro_rules! makro
+ md module (adds ->)
+ st Record (adds ->) Record
+ st Tuple (adds ->) Tuple
+ st Unit (adds ->) Unit
+ tt Trait (adds ->)
+ un Union (adds ->) Union
+ 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_with_base_items(
r"enum Enum $0",
@@ -244,6 +259,19 @@ impl Copy for S where $0
}
#[test]
+fn fn_item_where_kw() {
+ check_edit(
+ "where",
+ r#"
+fn foo() $0
+"#,
+ r#"
+fn foo() where $0
+"#,
+ );
+}
+
+#[test]
fn test_is_not_considered_macro() {
check_with_base_items(
r#"
@@ -352,3 +380,23 @@ foo!(f$0);
"#]],
);
}
+
+#[test]
+fn completes_variant_through_hidden_enum_alias() {
+ check(
+ r#"
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub enum Foo { Variant }
+pub type Bar = Foo;
+
+//- /main.rs crate:main deps:dep
+fn main() {
+ let x = dep::Bar::V$0;
+}
+"#,
+ expect![[r#"
+ ev Variant Variant
+ "#]],
+ );
+}
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs
index b82b23541c..55059a4035 100644
--- a/crates/ide-completion/src/tests/special.rs
+++ b/crates/ide-completion/src/tests/special.rs
@@ -896,9 +896,6 @@ fn bar() -> Bar {
"#,
expect![[r#"
fn foo() (as Foo) fn() -> Self
- ex Bar
- ex Bar::foo()
- ex bar()
"#]],
);
}
@@ -926,9 +923,6 @@ fn bar() -> Bar {
expect![[r#"
fn bar() fn()
fn foo() (as Foo) fn() -> Self
- ex Bar
- ex Bar::foo()
- ex bar()
"#]],
);
}
@@ -955,9 +949,6 @@ fn bar() -> Bar {
"#,
expect![[r#"
fn foo() (as Foo) fn() -> Self
- ex Bar
- ex Bar::foo()
- ex bar()
"#]],
);
}
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 7c6b7370aa..7d4a7fe6b8 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -1,7 +1,7 @@
//! Completion tests for type position.
use expect_test::expect;
-use crate::tests::{check, check_with_base_items};
+use crate::tests::{check, check_edit, check_with_base_items};
#[test]
fn record_field_ty() {
@@ -94,6 +94,230 @@ fn x<'lt, T, const C: usize>() -> $0
}
#[test]
+fn fn_return_type_missing_thin_arrow() {
+ check_with_base_items(
+ r#"
+fn x() u$0
+"#,
+ expect![[r#"
+ en Enum (adds ->) Enum
+ ma makro!(…) macro_rules! makro
+ md module (adds ->)
+ st Record (adds ->) Record
+ st Tuple (adds ->) Tuple
+ st Unit (adds ->) Unit
+ tt Trait (adds ->)
+ un Union (adds ->) Union
+ 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_with_base_items(
+ r#"
+fn x() $0
+"#,
+ expect![[r#"
+ en Enum (adds ->) Enum
+ ma makro!(…) macro_rules! makro
+ md module (adds ->)
+ st Record (adds ->) Record
+ st Tuple (adds ->) Tuple
+ st Unit (adds ->) Unit
+ tt Trait (adds ->)
+ un Union (adds ->) Union
+ bt u32 (adds ->) u32
+ kw crate:: (adds ->)
+ kw dyn (adds ->)
+ kw fn (adds ->)
+ kw for (adds ->)
+ kw impl (adds ->)
+ kw self:: (adds ->)
+ kw where
+ "#]],
+ );
+}
+
+#[test]
+fn fn_return_type_missing_thin_arrow_path_completion() {
+ check_edit(
+ "u32",
+ r#"
+fn foo() u$0
+"#,
+ r#"
+fn foo() -> u32
+"#,
+ );
+
+ check_edit(
+ "u32",
+ r#"
+fn foo() $0
+"#,
+ r#"
+fn foo() -> u32
+"#,
+ );
+
+ check_edit(
+ "Num",
+ r#"
+type Num = u32;
+fn foo() $0
+"#,
+ r#"
+type Num = u32;
+fn foo() -> Num
+"#,
+ );
+
+ check_edit(
+ "impl",
+ r#"
+fn foo() $0
+"#,
+ r#"
+fn foo() -> impl $0
+"#,
+ );
+
+ check_edit(
+ "foo",
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() $0
+"#,
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() -> foo
+"#,
+ );
+
+ check_edit(
+ "crate::",
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() $0
+"#,
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() -> crate::
+"#,
+ );
+
+ check_edit(
+ "Num",
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() foo::$0
+"#,
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() -> foo::Num
+"#,
+ );
+
+ // no spaces, test edit order
+ check_edit(
+ "foo",
+ r#"
+mod foo { pub type Num = u32; }
+fn foo()$0
+"#,
+ r#"
+mod foo { pub type Num = u32; }
+fn foo() ->foo
+"#,
+ );
+}
+
+#[test]
+fn fn_return_type_missing_thin_arrow_path_completion_with_generic_args() {
+ check_edit(
+ "Foo",
+ r#"
+struct Foo<T>(T);
+fn foo() F$0
+"#,
+ r#"
+struct Foo<T>(T);
+fn foo() -> Foo<$0>
+"#,
+ );
+
+ check_edit(
+ "Foo",
+ r#"
+struct Foo<T>(T);
+fn foo() $0
+"#,
+ r#"
+struct Foo<T>(T);
+fn foo() -> Foo<$0>
+"#,
+ );
+
+ check_edit(
+ "Foo",
+ r#"
+type Foo<T> = T;
+fn foo() $0
+"#,
+ r#"
+type Foo<T> = T;
+fn foo() -> Foo<$0>
+"#,
+ );
+}
+
+#[test]
+fn fn_return_type_missing_thin_arrow_infer_ref_type() {
+ check_with_base_items(
+ r#"
+fn x() u$0 {&2u32}
+"#,
+ expect![[r#"
+ en Enum (adds ->) Enum
+ ma makro!(…) macro_rules! makro
+ md module (adds ->)
+ st Record (adds ->) Record
+ st Tuple (adds ->) Tuple
+ st Unit (adds ->) Unit
+ tt Trait (adds ->)
+ un Union (adds ->) Union
+ bt u32 (adds ->) u32
+ it &u32 (adds ->)
+ kw crate:: (adds ->)
+ kw dyn (adds ->)
+ kw fn (adds ->)
+ kw for (adds ->)
+ kw impl (adds ->)
+ kw self:: (adds ->)
+ kw where
+ "#]],
+ );
+
+ check_edit(
+ "&u32",
+ r#"
+struct Foo<T>(T);
+fn x() u$0 {&2u32}
+"#,
+ r#"
+struct Foo<T>(T);
+fn x() -> &u32 {&2u32}
+"#,
+ );
+}
+
+#[test]
fn fn_return_type_after_reference() {
check_with_base_items(
r#"
diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml
index fca06b69d1..2c0919a183 100644
--- a/crates/ide-db/Cargo.toml
+++ b/crates/ide-db/Cargo.toml
@@ -25,7 +25,6 @@ arrayvec.workspace = true
memchr = "2.7.5"
salsa.workspace = true
salsa-macros.workspace = true
-query-group.workspace = true
triomphe.workspace = true
nohash-hasher.workspace = true
bitflags.workspace = true
diff --git a/crates/ide-db/src/generated/lints.rs b/crates/ide-db/src/generated/lints.rs
index 9e6d586008..52a5a95450 100644
--- a/crates/ide-db/src/generated/lints.rs
+++ b/crates/ide-db/src/generated/lints.rs
@@ -1,4 +1,4 @@
-//! Generated by `cargo codegen lint-definitions`, do not edit by hand.
+//! Generated by `cargo xtask codegen lint-definitions`, do not edit by hand.
use span::Edition;
@@ -20,8 +20,8 @@ pub struct LintGroup {
pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
- label: "abi_unsupported_vector_types",
- description: r##"this function call or definition uses a vector type which is not enabled"##,
+ label: "aarch64_softfloat_neon",
+ description: r##"detects code that could be affected by ABI issues on aarch64 softfloat targets"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -41,9 +41,23 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "ambiguous_derive_helpers",
+ description: r##"detects derive helper attributes that are ambiguous with built-in attributes"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "ambiguous_glob_imported_traits",
+ description: r##"detects uses of ambiguously glob imported traits"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ambiguous_glob_imports",
description: r##"detects certain glob imports that require reporting an ambiguity error"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -55,6 +69,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "ambiguous_import_visibilities",
+ description: r##"detects certain glob imports that require reporting an ambiguity error"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ambiguous_negative_literals",
description: r##"ambiguous negative literals operations"##,
default_severity: Severity::Allow,
@@ -62,6 +83,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "ambiguous_panic_imports",
+ description: r##"detects ambiguous core and std panic imports"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ambiguous_wide_pointer_comparisons",
description: r##"detects ambiguous wide pointer comparisons"##,
default_severity: Severity::Warning,
@@ -146,13 +174,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "cenum_impl_drop_cast",
- description: r##"a C-like enum implementing Drop is cast"##,
- default_severity: Severity::Error,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "clashing_extern_declarations",
description: r##"detects when an extern fn has been declared with the same name but different types"##,
default_severity: Severity::Warning,
@@ -195,6 +216,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "const_item_interior_mutations",
+ description: r##"checks for calls which mutates a interior mutable const-item"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "const_item_mutation",
description: r##"detects attempts to mutate a `const` item"##,
default_severity: Severity::Warning,
@@ -202,6 +230,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "dangerous_implicit_autorefs",
+ description: r##"implicit reference to a dereference of a raw pointer"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "dangling_pointers_from_locals",
+ description: r##"detects returning a pointer from a local variable"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "dangling_pointers_from_temporaries",
description: r##"detects getting a pointer from a temporary"##,
default_severity: Severity::Warning,
@@ -216,9 +258,16 @@ pub const DEFAULT_LINTS: &[Lint] = &[
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,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "dependency_on_unit_never_type_fallback",
description: r##"never type fallback affecting unsafe function calls"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -237,6 +286,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "deprecated_llvm_intrinsic",
+ description: r##"detects uses of deprecated LLVM intrinsics"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "deprecated_safe_2024",
description: r##"detects unsafe functions being used as safe functions"##,
default_severity: Severity::Allow,
@@ -252,14 +308,21 @@ pub const DEFAULT_LINTS: &[Lint] = &[
},
Lint {
label: "deref_into_dyn_supertrait",
- description: r##"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future"##,
- default_severity: Severity::Warning,
+ description: r##"`Deref` implementation with a supertrait trait object for output is shadowed by trait upcasting"##,
+ default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
label: "deref_nullptr",
description: r##"detects when an null pointer is dereferenced"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "double_negations",
+ description: r##"detects expressions of the form `--x`"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -286,6 +349,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "duplicate_features",
+ description: r##"duplicate features found in crate-level `#[feature]` directives"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "duplicate_macro_attributes",
description: r##"duplicated attribute"##,
default_severity: Severity::Warning,
@@ -321,13 +391,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "elided_named_lifetimes",
- description: r##"detects when an elided lifetime gets resolved to be `'static` or some named parameter"##,
- default_severity: Severity::Warning,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "ellipsis_inclusive_range_patterns",
description: r##"`...` range patterns are deprecated"##,
default_severity: Severity::Warning,
@@ -370,6 +433,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "float_literal_f32_fallback",
+ description: r##"detects unsuffixed floating point literals whose type fallback to `f32`"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "for_loops_over_fallibles",
description: r##"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"##,
default_severity: Severity::Warning,
@@ -398,6 +468,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "function_casts_as_integer",
+ description: r##"casting a function into an integer"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "function_item_references",
description: r##"suggest casting to a function pointer when attempting to take references to function items"##,
default_severity: Severity::Warning,
@@ -442,7 +519,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "impl_trait_redundant_captures",
description: r##"redundant precise-capturing `use<...>` syntax on an `impl Trait`"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
@@ -461,6 +538,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "improper_gpu_kernel_arg",
+ description: r##"GPU kernel entry points have a limited ABI"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "incomplete_features",
description: r##"incomplete features that may function improperly in some or all cases"##,
default_severity: Severity::Warning,
@@ -482,8 +566,29 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "inline_always_mismatching_target_features",
+ description: r##"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "inline_no_sanitize",
- description: r##"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"##,
+ description: r##"detects incompatible use of `#[inline(always)]` and `#[sanitize(... = "off")]`"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "integer_to_ptr_transmutes",
+ description: r##"detects integer to pointer transmutes"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "internal_eq_trait_method_impls",
+ description: r##"manual implementation of the internal `Eq::assert_receiver_is_total_eq` method"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -505,7 +610,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "invalid_doc_attributes",
description: r##"detects invalid `#[doc(...)]` attributes"##,
- default_severity: Severity::Error,
+ default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
},
@@ -526,7 +631,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "invalid_macro_export_arguments",
description: r##""invalid_parameter" isn't a valid argument for `#[macro_export]`"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -538,6 +643,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "invalid_null_arguments",
+ description: r##"invalid null pointer in arguments"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "invalid_reference_casting",
description: r##"casts of `&T` to `&mut T` without interior mutability"##,
default_severity: Severity::Error,
@@ -596,7 +708,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "legacy_derive_helpers",
description: r##"detects derive helper attributes that are used before they are introduced"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -615,6 +727,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "linker_info",
+ description: r##"linker warnings known to be informational-only and not indicative of a problem"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "linker_messages",
+ description: r##"warnings emitted at runtime by the target-specific linker program"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "long_running_const_eval",
description: r##"detects long const eval operations"##,
default_severity: Severity::Error,
@@ -643,6 +769,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "malformed_diagnostic_attributes",
+ description: r##"detects malformed diagnostic attributes"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "malformed_diagnostic_format_literals",
+ description: r##"detects diagnostic attribute with malformed diagnostic format literals"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "map_unit_fn",
description: r##"`Iterator::map` call that discard the iterator's values"##,
default_severity: Severity::Warning,
@@ -657,9 +797,23 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "mismatched_lifetime_syntaxes",
+ description: r##"detects when a lifetime uses different syntax between arguments and return values"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "misplaced_diagnostic_attributes",
+ description: r##"detects diagnostic attributes that are placed on the wrong item"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "missing_abi",
description: r##"No declared ABI for extern declaration"##,
- default_severity: Severity::Allow,
+ default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
},
@@ -685,9 +839,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "missing_fragment_specifier",
- description: r##"detects missing fragment specifiers in unused `macro_rules!` patterns"##,
- default_severity: Severity::Error,
+ label: "missing_gpu_kernel_export_name",
+ description: r##"mangled gpu-kernel function"##,
+ default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
},
@@ -743,9 +897,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "never_type_fallback_flowing_into_unsafe",
description: r##"never type fallback affecting unsafe function calls"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
- deny_since: Some(Edition::Edition2024),
+ deny_since: None,
},
Lint {
label: "no_mangle_const_items",
@@ -839,16 +993,9 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "order_dependent_trait_objects",
- description: r##"trait-object types were treated as different depending on marker-trait order"##,
- default_severity: Severity::Error,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "out_of_scope_macro_calls",
description: r##"detects out of scope calls to `macro_rules` in key-value attributes"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -902,13 +1049,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "ptr_cast_add_auto_to_object",
- description: r##"detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`"##,
- default_severity: Severity::Warning,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "ptr_to_integer_transmute_in_consts",
description: r##"detects pointer to integer transmutes in const functions and associated constants"##,
default_severity: Severity::Warning,
@@ -965,8 +1105,29 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "repr_transparent_external_private_fields",
+ label: "repr_c_enums_larger_than_int",
+ description: r##"repr(C) enums with discriminant values that do not fit into a C int"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "repr_transparent_non_zst_fields",
description: r##"transparent type contains an external ZST that is marked #[non_exhaustive] or contains private fields"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "resolving_to_items_shadowing_supertrait_items",
+ description: r##"detects when a supertrait item is shadowed by a subtrait item"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "rtsan_nonblocking_async",
+ description: r##"detects incompatible uses of `#[sanitize(realtime = "nonblocking")]` on async functions"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -1030,21 +1191,21 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "semicolon_in_expressions_from_macros",
description: r##"trailing semicolon in macro body used as expression"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
Lint {
- label: "single_use_lifetimes",
- description: r##"detects lifetime parameters that are only used once"##,
+ label: "shadowing_supertrait_items",
+ description: r##"detects when a supertrait item is shadowed by a subtrait item"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "soft_unstable",
- description: r##"a feature gate that doesn't break dependent crates"##,
- default_severity: Severity::Error,
+ label: "single_use_lifetimes",
+ description: r##"detects lifetime parameters that are only used once"##,
+ default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
@@ -1064,7 +1225,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
},
Lint {
label: "static_mut_refs",
- description: r##"shared references or mutable references of mutable static is discouraged"##,
+ description: r##"creating a shared reference to mutable static"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: Some(Edition::Edition2024),
@@ -1168,13 +1329,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "undefined_naked_function_abi",
- description: r##"undefined naked function ABI"##,
- default_severity: Severity::Warning,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "undropped_manually_drops",
description: r##"calls to `std::mem::drop` with `std::mem::ManuallyDrop` instead of it's inner value"##,
default_severity: Severity::Error,
@@ -1205,7 +1359,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
Lint {
label: "uninhabited_static",
description: r##"uninhabited static"##,
- default_severity: Severity::Warning,
+ default_severity: Severity::Error,
warn_since: None,
deny_since: None,
},
@@ -1224,15 +1378,15 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "unknown_lints",
- description: r##"unrecognized lint attribute"##,
+ label: "unknown_diagnostic_attributes",
+ description: r##"detects unknown diagnostic attributes"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
},
Lint {
- label: "unknown_or_malformed_diagnostic_attributes",
- description: r##"unrecognized or malformed diagnostic attribute"##,
+ label: "unknown_lints",
+ description: r##"unrecognized lint attribute"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -1252,6 +1406,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "unnecessary_transmutes",
+ description: r##"detects transmutes that can also be achieved by other operations"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "unpredictable_function_pointer_comparisons",
+ description: r##"detects unpredictable function pointer comparisons"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "unqualified_local_imports",
description: r##"`use` of a local item without leading `self::`, `super::`, or `crate::`"##,
default_severity: Severity::Allow,
@@ -1259,6 +1427,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "unreachable_cfg_select_predicates",
+ description: r##"detects unreachable configuration predicates in the cfg_select macro"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "unreachable_code",
description: r##"detects unreachable code paths"##,
default_severity: Severity::Warning,
@@ -1322,8 +1497,8 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "unsupported_fn_ptr_calling_conventions",
- description: r##"use of unsupported calling convention for function pointer"##,
+ label: "unsupported_calling_conventions",
+ description: r##"use of unsupported calling convention"##,
default_severity: Severity::Warning,
warn_since: None,
deny_since: None,
@@ -1490,6 +1665,13 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "unused_visibilities",
+ description: r##"detect visibility qualifiers on `const _` items"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "useless_deprecated",
description: r##"detects deprecation attributes with no effect"##,
default_severity: Severity::Error,
@@ -1504,6 +1686,20 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
+ label: "uses_power_alignment",
+ description: r##"Structs do not follow the power alignment rule under repr(C)"##,
+ default_severity: Severity::Warning,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "varargs_without_pattern",
+ description: r##"detects usage of `...` arguments without a pattern in non-foreign items"##,
+ default_severity: Severity::Error,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "variant_size_differences",
description: r##"detects enums with widely varying variant sizes"##,
default_severity: Severity::Allow,
@@ -1518,13 +1714,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "wasm_c_abi",
- description: r##"detects dependencies that are incompatible with the Wasm C ABI"##,
- default_severity: Severity::Error,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "while_true",
description: r##"suggest using `loop { }` instead of `while true { }`"##,
default_severity: Severity::Warning,
@@ -1540,7 +1729,7 @@ pub const DEFAULT_LINTS: &[Lint] = &[
},
Lint {
label: "future_incompatible",
- description: r##"lint group for: deref-into-dyn-supertrait, abi-unsupported-vector-types, ambiguous-associated-items, ambiguous-glob-imports, cenum-impl-drop-cast, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, order-dependent-trait-objects, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, ptr-cast-add-auto-to-object, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, soft-unstable, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-fn-ptr-calling-conventions, wasm-c-abi"##,
+ description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, float-literal-f32-fallback, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
@@ -1602,15 +1791,15 @@ pub const DEFAULT_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "unused",
- description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##,
+ label: "unknown_or_malformed_diagnostic_attributes",
+ description: r##"lint group for: malformed-diagnostic-attributes, malformed-diagnostic-format-literals, misplaced-diagnostic-attributes, unknown-diagnostic-attributes"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "warnings",
- description: r##"lint group for: all lints that are set to issue warnings"##,
+ label: "unused",
+ description: r##"lint group for: unused-imports, unused-variables, unused-visibilities, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
@@ -1631,44 +1820,46 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[
LintGroup {
lint: Lint {
label: "future_incompatible",
- description: r##"lint group for: deref-into-dyn-supertrait, abi-unsupported-vector-types, ambiguous-associated-items, ambiguous-glob-imports, cenum-impl-drop-cast, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, forbidden-lint-groups, ill-formed-attribute-input, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, missing-fragment-specifier, order-dependent-trait-objects, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, ptr-cast-add-auto-to-object, pub-use-of-private-extern-crate, repr-transparent-external-private-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, soft-unstable, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-fn-ptr-calling-conventions, wasm-c-abi"##,
+ description: r##"lint group for: internal-eq-trait-method-impls, aarch64-softfloat-neon, ambiguous-associated-items, ambiguous-derive-helpers, ambiguous-glob-imported-traits, ambiguous-glob-imports, ambiguous-import-visibilities, ambiguous-panic-imports, coherence-leak-check, conflicting-repr-hints, const-evaluatable-unchecked, elided-lifetimes-in-associated-constant, float-literal-f32-fallback, forbidden-lint-groups, ill-formed-attribute-input, invalid-macro-export-arguments, invalid-type-param-default, late-bound-lifetime-arguments, legacy-derive-helpers, macro-expanded-macro-exports-accessed-by-absolute-paths, out-of-scope-macro-calls, patterns-in-fns-without-body, proc-macro-derive-resolution-fallback, pub-use-of-private-extern-crate, repr-c-enums-larger-than-int, repr-transparent-non-zst-fields, self-constructor-from-outer-item, semicolon-in-expressions-from-macros, uncovered-param-in-projection, uninhabited-static, unstable-name-collisions, unstable-syntax-pre-expansion, unsupported-calling-conventions, varargs-without-pattern"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
children: &[
- "deref_into_dyn_supertrait",
- "abi_unsupported_vector_types",
+ "internal_eq_trait_method_impls",
+ "aarch64_softfloat_neon",
"ambiguous_associated_items",
+ "ambiguous_derive_helpers",
+ "ambiguous_glob_imported_traits",
"ambiguous_glob_imports",
- "cenum_impl_drop_cast",
+ "ambiguous_import_visibilities",
+ "ambiguous_panic_imports",
"coherence_leak_check",
"conflicting_repr_hints",
"const_evaluatable_unchecked",
"elided_lifetimes_in_associated_constant",
+ "float_literal_f32_fallback",
"forbidden_lint_groups",
"ill_formed_attribute_input",
+ "invalid_macro_export_arguments",
"invalid_type_param_default",
"late_bound_lifetime_arguments",
"legacy_derive_helpers",
"macro_expanded_macro_exports_accessed_by_absolute_paths",
- "missing_fragment_specifier",
- "order_dependent_trait_objects",
"out_of_scope_macro_calls",
"patterns_in_fns_without_body",
"proc_macro_derive_resolution_fallback",
- "ptr_cast_add_auto_to_object",
"pub_use_of_private_extern_crate",
- "repr_transparent_external_private_fields",
+ "repr_c_enums_larger_than_int",
+ "repr_transparent_non_zst_fields",
"self_constructor_from_outer_item",
"semicolon_in_expressions_from_macros",
- "soft_unstable",
"uncovered_param_in_projection",
"uninhabited_static",
"unstable_name_collisions",
"unstable_syntax_pre_expansion",
- "unsupported_fn_ptr_calling_conventions",
- "wasm_c_abi",
+ "unsupported_calling_conventions",
+ "varargs_without_pattern",
],
},
LintGroup {
@@ -1790,8 +1981,23 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[
},
LintGroup {
lint: Lint {
+ label: "unknown_or_malformed_diagnostic_attributes",
+ description: r##"lint group for: malformed-diagnostic-attributes, malformed-diagnostic-format-literals, misplaced-diagnostic-attributes, unknown-diagnostic-attributes"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ children: &[
+ "malformed_diagnostic_attributes",
+ "malformed_diagnostic_format_literals",
+ "misplaced_diagnostic_attributes",
+ "unknown_diagnostic_attributes",
+ ],
+ },
+ LintGroup {
+ lint: Lint {
label: "unused",
- description: r##"lint group for: unused-imports, unused-variables, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##,
+ description: r##"lint group for: unused-imports, unused-variables, unused-visibilities, unused-assignments, dead-code, unused-mut, unreachable-code, unreachable-patterns, unused-must-use, unused-unsafe, path-statements, unused-attributes, unused-macros, unused-macro-rules, unused-allocation, unused-doc-comments, unused-extern-crates, unused-features, unused-labels, unused-parens, unused-braces, redundant-semicolons, map-unit-fn"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
@@ -1799,6 +2005,7 @@ pub const DEFAULT_LINT_GROUPS: &[LintGroup] = &[
children: &[
"unused_imports",
"unused_variables",
+ "unused_visibilities",
"unused_assignments",
"dead_code",
"unused_mut",
@@ -1902,15 +2109,8 @@ pub const RUSTDOC_LINTS: &[Lint] = &[
deny_since: None,
},
Lint {
- label: "rustdoc::unportable_markdown",
- description: r##"detects markdown that is interpreted differently in different parser"##,
- default_severity: Severity::Warning,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "rustdoc::all",
- description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links, rustdoc::unportable-markdown"##,
+ description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
@@ -1920,7 +2120,7 @@ pub const RUSTDOC_LINTS: &[Lint] = &[
pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &[LintGroup {
lint: Lint {
label: "rustdoc::all",
- description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links, rustdoc::unportable-markdown"##,
+ description: r##"lint group for: rustdoc::broken-intra-doc-links, rustdoc::private-intra-doc-links, rustdoc::private-doc-tests, rustdoc::invalid-codeblock-attributes, rustdoc::invalid-rust-codeblocks, rustdoc::invalid-html-tags, rustdoc::bare-urls, rustdoc::missing-crate-level-docs, rustdoc::unescaped-backticks, rustdoc::redundant-explicit-links"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
@@ -1936,7 +2136,6 @@ pub const RUSTDOC_LINT_GROUPS: &[LintGroup] = &[LintGroup {
"rustdoc::missing_crate_level_docs",
"rustdoc::unescaped_backticks",
"rustdoc::redundant_explicit_links",
- "rustdoc::unportable_markdown",
],
}];
@@ -1945,9 +2144,11 @@ pub const FEATURES: &[Lint] = &[
label: "aarch64_unstable_target_feature",
description: r##"# `aarch64_unstable_target_feature`
-The tracking issue for this feature is: [#44839]
+The remaining unstable target features on aarch64.
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#150244]
+
+[#150244]: https://github.com/rust-lang/rust/issues/150244
------------------------
"##,
@@ -1959,9 +2160,11 @@ The tracking issue for this feature is: [#44839]
label: "aarch64_ver_target_feature",
description: r##"# `aarch64_ver_target_feature`
-The tracking issue for this feature is: [#44839]
+Instruction set "version" target features on aarch64.
+
+The tracking issue for this feature is: [#150245]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150245]: https://github.com/rust-lang/rust/issues/150245
------------------------
"##,
@@ -1973,6 +2176,8 @@ The tracking issue for this feature is: [#44839]
label: "abi_avr_interrupt",
description: r##"# `abi_avr_interrupt`
+Allows `extern "avr-interrupt" fn()` and `extern "avr-non-blocking-interrupt" fn()`.
+
The tracking issue for this feature is: [#69664]
[#69664]: https://github.com/rust-lang/rust/issues/69664
@@ -1984,8 +2189,8 @@ The tracking issue for this feature is: [#69664]
deny_since: None,
},
Lint {
- label: "abi_c_cmse_nonsecure_call",
- description: r##"# `abi_c_cmse_nonsecure_call`
+ label: "abi_cmse_nonsecure_call",
+ description: r##"# `abi_cmse_nonsecure_call`
The tracking issue for this feature is: [#81391]
@@ -2001,10 +2206,9 @@ LLVM, the Rust compiler and the linker are providing
[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
TrustZone-M feature.
-One of the things provided, with this unstable feature, is the
-`C-cmse-nonsecure-call` function ABI. This ABI is used on function pointers to
-non-secure code to mark a non-secure function call (see [section
-5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
+One of the things provided with this unstable feature is the "cmse-nonsecure-call" function ABI.
+This ABI is used on function pointers to non-secure code to mark a non-secure function call
+(see [section 5.5](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
With this ABI, the compiler will do the following to perform the call:
* save registers needed after the call to Secure memory
@@ -2015,19 +2219,16 @@ With this ABI, the compiler will do the following to perform the call:
To avoid using the non-secure stack, the compiler will constrain the number and
type of parameters/return value.
-The `extern "C-cmse-nonsecure-call"` ABI is otherwise equivalent to the
-`extern "C"` ABI.
-
<!-- NOTE(ignore) this example is specific to thumbv8m targets -->
``` rust,ignore
#![no_std]
-#![feature(abi_c_cmse_nonsecure_call)]
+#![feature(abi_cmse_nonsecure_call)]
#[no_mangle]
pub fn call_nonsecure_function(addr: usize) -> u32 {
let non_secure_function =
- unsafe { core::mem::transmute::<usize, extern "C-cmse-nonsecure-call" fn() -> u32>(addr) };
+ unsafe { core::mem::transmute::<usize, extern "cmse-nonsecure-call" fn() -> u32>(addr) };
non_secure_function()
}
```
@@ -2079,6 +2280,38 @@ call_nonsecure_function:
deny_since: None,
},
Lint {
+ label: "abi_custom",
+ description: r##"# `abi_custom`
+
+Allows `extern "custom" fn()`.
+
+The tracking issue for this feature is: [#140829]
+
+[#140829]: https://github.com/rust-lang/rust/issues/140829
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "abi_gpu_kernel",
+ description: r##"# `abi_gpu_kernel`
+
+Allows `extern "gpu-kernel" fn()`.
+
+The tracking issue for this feature is: [#135467]
+
+[#135467]: https://github.com/rust-lang/rust/issues/135467
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "abi_msp430_interrupt",
description: r##"# `abi_msp430_interrupt`
@@ -2198,6 +2431,8 @@ $ cat $(find -name '*.s')
label: "abi_riscv_interrupt",
description: r##"# `abi_riscv_interrupt`
+Allows `extern "riscv-interrupt-m" fn()` and `extern "riscv-interrupt-s" fn()`.
+
The tracking issue for this feature is: [#111889]
[#111889]: https://github.com/rust-lang/rust/issues/111889
@@ -2212,6 +2447,8 @@ The tracking issue for this feature is: [#111889]
label: "abi_unadjusted",
description: r##"# `abi_unadjusted`
+Allows using the `unadjusted` ABI; perma-unstable.
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -2250,6 +2487,8 @@ fn main() {
label: "abi_x86_interrupt",
description: r##"# `abi_x86_interrupt`
+Allows `extern "x86-interrupt" fn()`.
+
The tracking issue for this feature is: [#40180]
[#40180]: https://github.com/rust-lang/rust/issues/40180
@@ -2261,9 +2500,27 @@ The tracking issue for this feature is: [#40180]
deny_since: None,
},
Lint {
+ label: "abort_immediate",
+ description: r##"# `abort_immediate`
+
+
+
+The tracking issue for this feature is: [#154601]
+
+[#154601]: https://github.com/rust-lang/rust/issues/154601
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "abort_unwind",
description: r##"# `abort_unwind`
+
+
The tracking issue for this feature is: [#130338]
[#130338]: https://github.com/rust-lang/rust/issues/130338
@@ -2278,6 +2535,8 @@ The tracking issue for this feature is: [#130338]
label: "acceptfilter",
description: r##"# `acceptfilter`
+
+
The tracking issue for this feature is: [#121891]
[#121891]: https://github.com/rust-lang/rust/issues/121891
@@ -2292,6 +2551,8 @@ The tracking issue for this feature is: [#121891]
label: "addr_parse_ascii",
description: r##"# `addr_parse_ascii`
+
+
The tracking issue for this feature is: [#101035]
[#101035]: https://github.com/rust-lang/rust/issues/101035
@@ -2345,9 +2606,27 @@ fn is_foo_a_and_bar_true<const F: Foo, const B: Bar>() -> bool {
deny_since: None,
},
Lint {
+ label: "align_to_uninit_mut",
+ description: r##"# `align_to_uninit_mut`
+
+
+
+The tracking issue for this feature is: [#139062]
+
+[#139062]: https://github.com/rust-lang/rust/issues/139062
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "alloc_error_handler",
description: r##"# `alloc_error_handler`
+Allows defining an `#[alloc_error_handler]`.
+
The tracking issue for this feature is: [#51540]
[#51540]: https://github.com/rust-lang/rust/issues/51540
@@ -2362,6 +2641,8 @@ The tracking issue for this feature is: [#51540]
label: "alloc_error_hook",
description: r##"# `alloc_error_hook`
+
+
The tracking issue for this feature is: [#51245]
[#51245]: https://github.com/rust-lang/rust/issues/51245
@@ -2376,6 +2657,8 @@ The tracking issue for this feature is: [#51245]
label: "alloc_internals",
description: r##"# `alloc_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -2385,12 +2668,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "alloc_layout_extra",
- description: r##"# `alloc_layout_extra`
+ label: "alloc_slice_into_array",
+ description: r##"# `alloc_slice_into_array`
+
+
-The tracking issue for this feature is: [#55724]
+The tracking issue for this feature is: [#148082]
-[#55724]: https://github.com/rust-lang/rust/issues/55724
+[#148082]: https://github.com/rust-lang/rust/issues/148082
------------------------
"##,
@@ -2435,9 +2720,25 @@ compiler.
deny_since: None,
},
Lint {
+ label: "alloctests",
+ description: r##"# `alloctests`
+
+
+
+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: "allow_internal_unsafe",
description: r##"# `allow_internal_unsafe`
+Allows using `#[allow_internal_unsafe]`. This is an attribute on `macro_rules!` and can't use the attribute handling below (it has to be checked before expansion possibly makes macros disappear).
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -2450,6 +2751,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "allow_internal_unstable",
description: r##"# `allow_internal_unstable`
+Allows using `#[allow_internal_unstable]`. This is an attribute on `macro_rules!` and can't use the attribute handling below (it has to be checked before expansion possibly makes macros disappear).
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -2462,6 +2765,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "anonymous_lifetime_in_impl_trait",
description: r##"# `anonymous_lifetime_in_impl_trait`
+Allows using anonymous lifetimes in argument-position impl-trait.
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -2471,12 +2776,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "anonymous_pipe",
- description: r##"# `anonymous_pipe`
+ label: "apx_target_feature",
+ description: r##"# `apx_target_feature`
-The tracking issue for this feature is: [#127154]
+The `apxf` target feature on x86
-[#127154]: https://github.com/rust-lang/rust/issues/127154
+The tracking issue for this feature is: [#139284]
+
+[#139284]: https://github.com/rust-lang/rust/issues/139284
------------------------
"##,
@@ -2493,6 +2800,153 @@ The tracking issue for this feature is: [#44874]
[#44874]: https://github.com/rust-lang/rust/issues/44874
------------------------
+
+Allows any type implementing `core::ops::Receiver<Target=T>` to be used as the type
+of `self` in a method belonging to `T`.
+
+For example,
+
+```rust
+#![feature(arbitrary_self_types)]
+
+struct A;
+
+impl A {
+ fn f(self: SmartPtr<Self>) -> i32 { 1 } // note self type
+}
+
+struct SmartPtr<T>(T);
+
+impl<T> core::ops::Receiver for SmartPtr<T> {
+ type Target = T;
+}
+
+fn main() {
+ let smart_ptr = SmartPtr(A);
+ assert_eq!(smart_ptr.f(), 1);
+}
+```
+
+The `Receiver` trait has a blanket implementation for all `T: Deref`, so in fact
+things like this work too:
+
+```rust
+#![feature(arbitrary_self_types)]
+
+use std::rc::Rc;
+
+struct A;
+
+impl A {
+ fn f(self: Rc<Self>) -> i32 { 1 } // Rc implements Deref
+}
+
+fn main() {
+ let smart_ptr = Rc::new(A);
+ assert_eq!(smart_ptr.f(), 1);
+}
+```
+
+Interestingly, that works even without the `arbitrary_self_types` feature
+- but that's because certain types are _effectively_ hard coded, including
+`Rc`. ("Hard coding" isn't quite true; they use a lang-item called
+`LegacyReceiver` to denote their special-ness in this way). With the
+`arbitrary_self_types` feature, their special-ness goes away, and custom
+smart pointers can achieve the same.
+
+## Changes to method lookup
+
+Method lookup previously used to work by stepping through the `Deref`
+chain then using the resulting list of steps in two different ways:
+
+* To identify types that might contribute methods via their `impl`
+ blocks (inherent methods) or via traits
+* To identify the types that the method receiver (`a` in the above
+ examples) can be converted to.
+
+With this feature, these lists are created by instead stepping through
+the `Receiver` chain. However, a note is kept about whether the type
+can be reached also via the `Deref` chain.
+
+The full chain (via `Receiver` hops) is used for the first purpose
+(identifying relevant `impl` blocks and traits); whereas the shorter
+list (reachable via `Deref`) is used for the second purpose. That's
+because, to convert the method target (`a` in `a.b()`) to the self
+type, Rust may need to be able to use `Deref::deref`. Type conversions,
+then, can only proceed as far as the end of the `Deref` chain whereas
+the longer `Receiver` chain can be used to explore more places where
+useful methods might reside.
+
+## Types suitable for use as smart pointers
+
+This feature allows the creation of customised smart pointers - for example
+your own equivalent to `Rc` or `Box` with whatever capabilities you like.
+Those smart pointers can either implement `Deref` (if it's safe to
+create a reference to the referent) or `Receiver` (if it isn't).
+
+Either way, smart pointer types should mostly _avoid having methods_.
+Calling methods on a smart pointer leads to ambiguity about whether you're
+aiming for a method on the pointer, or on the referent.
+
+Best practice is therefore to put smart pointer functionality into
+associated functions instead - that's what's done in all the smart pointer
+types within Rust's standard library which implement `Receiver`.
+
+If you choose to add any methods to your smart pointer type, your users
+may run into errors from deshadowing, as described in the next section.
+
+## Avoiding shadowing
+
+With or without this feature, Rust emits an error if it finds two method
+candidates, like this:
+
+```rust,compile_fail
+use std::pin::Pin;
+use std::pin::pin;
+
+struct A;
+
+impl A {
+ fn get_ref(self: Pin<&A>) {}
+}
+
+fn main() {
+ let pinned_a: Pin<&A> = pin!(A).as_ref();
+ let pinned_a: Pin<&A> = pinned_a.as_ref();
+ pinned_a.get_ref(); // error[E0034]: multiple applicable items in scope
+}
+```
+
+(this is why Rust's smart pointers are mostly carefully designed to avoid
+having methods at all, and shouldn't add new methods in future.)
+
+With `arbitrary_self_types`, we take care to spot some other kinds of
+conflict:
+
+```rust,compile_fail
+#![feature(arbitrary_self_types)]
+
+use std::pin::Pin;
+use std::pin::pin;
+
+struct A;
+
+impl A {
+ fn get_ref(self: &Pin<&A>) {} // note &Pin
+}
+
+fn main() {
+ let pinned_a: Pin<&mut A> = pin!(A);
+ let pinned_a: Pin<&A> = pinned_a.as_ref();
+ pinned_a.get_ref();
+}
+```
+
+This is to guard against the case where an inner (referent) type has a
+method of a given name, taking the smart pointer by reference, and then
+the smart pointer implementer adds a similar method taking self by value.
+As noted in the previous section, the safe option is simply
+not to add methods to smart pointers, and then these errors can't occur.
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -2504,21 +2958,73 @@ The tracking issue for this feature is: [#44874]
The tracking issue for this feature is: [#44874]
-[#44874]: https://github.com/rust-lang/rust/issues/44874
+[#38788]: https://github.com/rust-lang/rust/issues/44874
------------------------
+
+This extends the [arbitrary self types] feature to allow methods to
+receive `self` by pointer. For example:
+
+```rust
+#![feature(arbitrary_self_types_pointers)]
+
+struct A;
+
+impl A {
+ fn m(self: *const Self) {}
+}
+
+fn main() {
+ let a = A;
+ let a_ptr: *const A = &a as *const A;
+ a_ptr.m();
+}
+```
+
+In general this is not advised: it's thought to be better practice to wrap
+raw pointers in a newtype wrapper which implements the `core::ops::Receiver`
+trait, then you need "only" the `arbitrary_self_types` feature. For example:
+
+```rust
+#![feature(arbitrary_self_types)]
+#![allow(dead_code)]
+
+struct A;
+
+impl A {
+ fn m(self: Wrapper<Self>) {} // can extract the pointer and do
+ // what it needs
+}
+
+struct Wrapper<T>(*const T);
+
+impl<T> core::ops::Receiver for Wrapper<T> {
+ type Target = T;
+}
+
+fn main() {
+ let a = A;
+ let a_ptr: *const A = &a as *const A;
+ let a_wrapper = Wrapper(a_ptr);
+ a_wrapper.m();
+}
+```
+
+[arbitrary self types]: arbitrary-self-types.md
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "arm_target_feature",
- description: r##"# `arm_target_feature`
+ label: "arc_is_unique",
+ description: r##"# `arc_is_unique`
+
-The tracking issue for this feature is: [#44839]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#138938]
+
+[#138938]: https://github.com/rust-lang/rust/issues/138938
------------------------
"##,
@@ -2527,12 +3033,14 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
- label: "array_chunks",
- description: r##"# `array_chunks`
+ label: "arm_target_feature",
+ description: r##"# `arm_target_feature`
+
+Target features on arm.
-The tracking issue for this feature is: [#74985]
+The tracking issue for this feature is: [#150246]
-[#74985]: https://github.com/rust-lang/rust/issues/74985
+[#150246]: https://github.com/rust-lang/rust/issues/150246
------------------------
"##,
@@ -2544,6 +3052,8 @@ The tracking issue for this feature is: [#74985]
label: "array_into_iter_constructors",
description: r##"# `array_into_iter_constructors`
+
+
The tracking issue for this feature is: [#91583]
[#91583]: https://github.com/rust-lang/rust/issues/91583
@@ -2558,23 +3068,11 @@ The tracking issue for this feature is: [#91583]
label: "array_ptr_get",
description: r##"# `array_ptr_get`
-The tracking issue for this feature is: [#119834]
-
-[#119834]: https://github.com/rust-lang/rust/issues/119834
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "array_repeat",
- description: r##"# `array_repeat`
-The tracking issue for this feature is: [#126695]
+The tracking issue for this feature is: [#119834]
-[#126695]: https://github.com/rust-lang/rust/issues/126695
+[#119834]: https://github.com/rust-lang/rust/issues/119834
------------------------
"##,
@@ -2586,6 +3084,8 @@ The tracking issue for this feature is: [#126695]
label: "array_try_from_fn",
description: r##"# `array_try_from_fn`
+
+
The tracking issue for this feature is: [#89379]
[#89379]: https://github.com/rust-lang/rust/issues/89379
@@ -2600,37 +3100,11 @@ The tracking issue for this feature is: [#89379]
label: "array_try_map",
description: r##"# `array_try_map`
-The tracking issue for this feature is: [#79711]
-
-[#79711]: https://github.com/rust-lang/rust/issues/79711
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "array_windows",
- description: r##"# `array_windows`
-
-The tracking issue for this feature is: [#75027]
-[#75027]: https://github.com/rust-lang/rust/issues/75027
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "as_array_of_cells",
- description: r##"# `as_array_of_cells`
-
-The tracking issue for this feature is: [#88248]
+The tracking issue for this feature is: [#79711]
-[#88248]: https://github.com/rust-lang/rust/issues/88248
+[#79711]: https://github.com/rust-lang/rust/issues/79711
------------------------
"##,
@@ -2642,6 +3116,8 @@ The tracking issue for this feature is: [#88248]
label: "ascii_char",
description: r##"# `ascii_char`
+
+
The tracking issue for this feature is: [#110998]
[#110998]: https://github.com/rust-lang/rust/issues/110998
@@ -2656,6 +3132,8 @@ The tracking issue for this feature is: [#110998]
label: "ascii_char_variants",
description: r##"# `ascii_char_variants`
+
+
The tracking issue for this feature is: [#110998]
[#110998]: https://github.com/rust-lang/rust/issues/110998
@@ -2678,7 +3156,6 @@ The tracking issue for this feature is: [#93335]
This feature tracks `asm!` and `global_asm!` support for the following architectures:
- NVPTX
-- PowerPC
- Hexagon
- MIPS32r2 and MIPS64r2
- wasm32
@@ -2701,12 +3178,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| NVPTX | `reg64` | None\* | `l` |
| Hexagon | `reg` | `r[0-28]` | `r` |
| Hexagon | `preg` | `p[0-3]` | Only clobbers |
-| PowerPC | `reg` | `r0`, `r[3-12]`, `r[14-28]` | `r` |
-| PowerPC | `reg_nonzero` | `r[3-12]`, `r[14-28]` | `b` |
-| PowerPC | `freg` | `f[0-31]` | `f` |
-| PowerPC | `vreg` | `v[0-31]` | `v` |
-| PowerPC | `cr` | `cr[0-7]`, `cr` | Only clobbers |
-| PowerPC | `xer` | `xer` | Only clobbers |
| wasm32 | `local` | None\* | `r` |
| BPF | `reg` | `r[0-10]` | `r` |
| BPF | `wreg` | `w[0-10]` | `w` |
@@ -2742,13 +3213,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| NVPTX | `reg64` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
| Hexagon | `reg` | None | `i8`, `i16`, `i32`, `f32` |
| Hexagon | `preg` | N/A | Only clobbers |
-| PowerPC | `reg` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) |
-| PowerPC | `reg_nonzero` | None | `i8`, `i16`, `i32`, `i64` (powerpc64 only) |
-| PowerPC | `freg` | None | `f32`, `f64` |
-| PowerPC | `vreg` | `altivec` | `i8x16`, `i16x8`, `i32x4`, `f32x4` |
-| PowerPC | `vreg` | `vsx` | `f32`, `f64`, `i64x2`, `f64x2` |
-| PowerPC | `cr` | N/A | Only clobbers |
-| PowerPC | `xer` | N/A | Only clobbers |
| wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` |
| BPF | `reg` | None | `i8` `i16` `i32` `i64` |
| BPF | `wreg` | `alu32` | `i8` `i16` `i32` |
@@ -2769,10 +3233,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| Hexagon | `r29` | `sp` |
| Hexagon | `r30` | `fr` |
| Hexagon | `r31` | `lr` |
-| PowerPC | `r1` | `sp` |
-| PowerPC | `r31` | `fp` |
-| PowerPC | `r[0-31]` | `[0-31]` |
-| PowerPC | `f[0-31]` | `fr[0-31]`|
| BPF | `r[0-10]` | `w[0-10]` |
| AVR | `XH` | `r27` |
| AVR | `XL` | `r26` |
@@ -2811,18 +3271,14 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| Architecture | Unsupported register | Reason |
| ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| All | `sp`, `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. |
-| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. |
-| All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. |
+| All | `fr` (Hexagon) `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r30`/`i6` (SPARC) | The frame pointer cannot be used as an input or output. |
+| All | `r19` (Hexagon) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. |
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
| MIPS | `$1` or `$at` | Reserved for assembler. |
| MIPS | `$26`/`$k0`, `$27`/`$k1` | OS-reserved registers. |
| MIPS | `$28`/`$gp` | Global pointer cannot be used as inputs or outputs. |
| MIPS | `$ra` | Return address cannot be used as inputs or outputs. |
| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |
-| PowerPC | `r2`, `r13` | These are system reserved registers. |
-| PowerPC | `lr` | The link register cannot be used as an input or output. |
-| PowerPC | `ctr` | The counter register cannot be used as an input or output. |
-| PowerPC | `vrsave` | The vrsave register cannot be used as an input or output. |
| AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. |
|MSP430 | `r0`, `r2`, `r3` | These are the program counter, status register, and constant generator respectively. Neither the status register nor constant generator can be written to. |
| M68k | `a4`, `a5` | Used internally by LLVM for the base pointer and global base pointer. |
@@ -2849,10 +3305,6 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| NVPTX | `reg32` | None | `r0` | None |
| NVPTX | `reg64` | None | `rd0` | None |
| Hexagon | `reg` | None | `r0` | None |
-| PowerPC | `reg` | None | `0` | None |
-| PowerPC | `reg_nonzero` | None | `3` | None |
-| PowerPC | `freg` | None | `0` | None |
-| PowerPC | `vreg` | None | `0` | None |
| SPARC | `reg` | None | `%o0` | None |
| CSKY | `reg` | None | `r0` | None |
| CSKY | `freg` | None | `f0` | None |
@@ -2869,6 +3321,8 @@ These flags registers must be restored upon exiting the asm block if the `preser
- SPARC
- Integer condition codes (`icc` and `xcc`)
- Floating-point condition codes (`fcc[0-3]`)
+- CSKY
+ - Condition/carry bit (C) in `PSR`.
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -2890,16 +3344,14 @@ This tracks support for additional registers in architectures where inline assem
| Architecture | Register class | Registers | LLVM constraint code |
| ------------ | -------------- | --------- | -------------------- |
-| s390x | `vreg` | `v[0-31]` | `v` |
-
-> **Notes**:
-> - s390x `vreg` is clobber-only in stable.
## Register class supported types
| Architecture | Register class | Target feature | Allowed types |
| ------------ | -------------- | -------------- | ------------- |
-| s390x | `vreg` | `vector` | `i32`, `f32`, `i64`, `f64`, `i128`, `f128`, `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |
+| x86 | `xmm_reg` | `sse` | `i128` |
+| x86 | `ymm_reg` | `avx` | `i128` |
+| x86 | `zmm_reg` | `avx512f` | `i128` |
## Register aliases
@@ -2915,15 +3367,14 @@ This tracks support for additional registers in architectures where inline assem
| Architecture | Register class | Modifier | Example output | LLVM modifier |
| ------------ | -------------- | -------- | -------------- | ------------- |
-| s390x | `vreg` | None | `%v0` | None |
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "asm_goto",
- description: r##"# `asm_goto`
+ label: "asm_goto_with_outputs",
+ description: r##"# `asm_goto_with_outputs`
The tracking issue for this feature is: [#119364]
@@ -2931,44 +3382,25 @@ The tracking issue for this feature is: [#119364]
------------------------
-This feature adds a `label <block>` operand type to `asm!`.
+This feature allows label operands to be used together with output operands.
Example:
```rust,ignore (partial-example, x86-only)
unsafe {
+ let a: usize;
asm!(
+ "mov {}, 1"
"jmp {}",
+ out(reg) a,
label {
- println!("Jumped from asm!");
+ println!("Jumped from asm {}!", a);
}
);
}
```
-The block must have unit type or diverge. The block starts a new safety context,
-so despite outer `unsafe`, you need extra unsafe to perform unsafe operations
-within `label <block>`.
-
-When `label <block>` is used together with `noreturn` option, it means that the
-assembly will not fallthrough. It's allowed to jump to a label within the
-assembly. In this case, the entire `asm!` expression will have an unit type as
-opposed to diverging, if not all label blocks diverge. The `asm!` expression
-still diverges if `noreturn` option is used and all label blocks diverge.
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "asm_goto_with_outputs",
- description: r##"# `asm_goto_with_outputs`
-
-The tracking issue for this feature is: [#119364]
-
-[#119364]: https://github.com/rust-lang/rust/issues/119364
-
-------------------------
+The output operands are assigned before the label blocks are executed.
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -2991,37 +3423,11 @@ This feature adds a `may_unwind` option to `asm!` which allows an `asm` block to
deny_since: None,
},
Lint {
- label: "assert_matches",
- description: r##"# `assert_matches`
-
-The tracking issue for this feature is: [#82775]
-
-[#82775]: https://github.com/rust-lang/rust/issues/82775
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "associated_const_equality",
- description: r##"# `associated_const_equality`
-
-The tracking issue for this feature is: [#92827]
-
-[#92827]: https://github.com/rust-lang/rust/issues/92827
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "associated_type_defaults",
description: r##"# `associated_type_defaults`
+Allows associated type defaults.
+
The tracking issue for this feature is: [#29661]
[#29661]: https://github.com/rust-lang/rust/issues/29661
@@ -3033,12 +3439,14 @@ The tracking issue for this feature is: [#29661]
deny_since: None,
},
Lint {
- label: "async_closure",
- description: r##"# `async_closure`
+ label: "async_drop",
+ description: r##"# `async_drop`
-The tracking issue for this feature is: [#62290]
+Allows implementing `AsyncDrop`.
-[#62290]: https://github.com/rust-lang/rust/issues/62290
+The tracking issue for this feature is: [#126482]
+
+[#126482]: https://github.com/rust-lang/rust/issues/126482
------------------------
"##,
@@ -3047,12 +3455,14 @@ The tracking issue for this feature is: [#62290]
deny_since: None,
},
Lint {
- label: "async_drop",
- description: r##"# `async_drop`
+ label: "async_fn_in_dyn_trait",
+ description: r##"# `async_fn_in_dyn_trait`
-The tracking issue for this feature is: [#126482]
+Allows async functions to be called from `dyn Trait`.
-[#126482]: https://github.com/rust-lang/rust/issues/126482
+The tracking issue for this feature is: [#133119]
+
+[#133119]: https://github.com/rust-lang/rust/issues/133119
------------------------
"##,
@@ -3064,6 +3474,8 @@ The tracking issue for this feature is: [#126482]
label: "async_fn_track_caller",
description: r##"# `async_fn_track_caller`
+Allows `#[track_caller]` on async functions.
+
The tracking issue for this feature is: [#110011]
[#110011]: https://github.com/rust-lang/rust/issues/110011
@@ -3098,6 +3510,8 @@ that borrows from itself (`FnOnce::Output` has no lifetime parameters, while `As
label: "async_for_loop",
description: r##"# `async_for_loop`
+Allows `for await` loops.
+
The tracking issue for this feature is: [#118898]
[#118898]: https://github.com/rust-lang/rust/issues/118898
@@ -3112,6 +3526,8 @@ The tracking issue for this feature is: [#118898]
label: "async_gen_internals",
description: r##"# `async_gen_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -3124,6 +3540,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "async_iter_from_iter",
description: r##"# `async_iter_from_iter`
+
+
The tracking issue for this feature is: [#81798]
[#81798]: https://github.com/rust-lang/rust/issues/81798
@@ -3138,6 +3556,8 @@ The tracking issue for this feature is: [#81798]
label: "async_iterator",
description: r##"# `async_iterator`
+
+
The tracking issue for this feature is: [#79024]
[#79024]: https://github.com/rust-lang/rust/issues/79024
@@ -3152,6 +3572,8 @@ The tracking issue for this feature is: [#79024]
label: "async_trait_bounds",
description: r##"# `async_trait_bounds`
+Allows `async` trait bound modifier.
+
The tracking issue for this feature is: [#62290]
[#62290]: https://github.com/rust-lang/rust/issues/62290
@@ -3166,6 +3588,8 @@ The tracking issue for this feature is: [#62290]
label: "atomic_from_mut",
description: r##"# `atomic_from_mut`
+
+
The tracking issue for this feature is: [#76314]
[#76314]: https://github.com/rust-lang/rust/issues/76314
@@ -3177,6 +3601,36 @@ The tracking issue for this feature is: [#76314]
deny_since: None,
},
Lint {
+ label: "atomic_internals",
+ description: r##"# `atomic_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: "atomic_ptr_null",
+ description: r##"# `atomic_ptr_null`
+
+
+
+The tracking issue for this feature is: [#150733]
+
+[#150733]: https://github.com/rust-lang/rust/issues/150733
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "auto_traits",
description: r##"# `auto_traits`
@@ -3293,6 +3747,8 @@ Auto traits cannot have supertraits. This is for soundness reasons, as the inter
label: "autodiff",
description: r##"# `autodiff`
+
+
The tracking issue for this feature is: [#124509]
[#124509]: https://github.com/rust-lang/rust/issues/124509
@@ -3304,12 +3760,30 @@ The tracking issue for this feature is: [#124509]
deny_since: None,
},
Lint {
- label: "avx512_target_feature",
- description: r##"# `avx512_target_feature`
+ label: "avr_target_feature",
+ description: r##"# `avr_target_feature`
+
+Target features on avr.
-The tracking issue for this feature is: [#44839]
+The tracking issue for this feature is: [#146889]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#146889]: https://github.com/rust-lang/rust/issues/146889
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "avx10_target_feature",
+ description: r##"# `avx10_target_feature`
+
+Allows using Intel AVX10 target features and intrinsics
+
+The tracking issue for this feature is: [#138843]
+
+[#138843]: https://github.com/rust-lang/rust/issues/138843
------------------------
"##,
@@ -3321,6 +3795,8 @@ The tracking issue for this feature is: [#44839]
label: "backtrace_frames",
description: r##"# `backtrace_frames`
+
+
The tracking issue for this feature is: [#79676]
[#79676]: https://github.com/rust-lang/rust/issues/79676
@@ -3332,12 +3808,28 @@ The tracking issue for this feature is: [#79676]
deny_since: None,
},
Lint {
- label: "bigint_helper_methods",
- description: r##"# `bigint_helper_methods`
+ label: "bikeshed_guaranteed_no_drop",
+ description: r##"# `bikeshed_guaranteed_no_drop`
-The tracking issue for this feature is: [#85532]
-[#85532]: https://github.com/rust-lang/rust/issues/85532
+
+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: "binary_heap_as_mut_slice",
+ description: r##"# `binary_heap_as_mut_slice`
+
+
+
+The tracking issue for this feature is: [#154009]
+
+[#154009]: https://github.com/rust-lang/rust/issues/154009
------------------------
"##,
@@ -3349,6 +3841,8 @@ The tracking issue for this feature is: [#85532]
label: "binary_heap_drain_sorted",
description: r##"# `binary_heap_drain_sorted`
+
+
The tracking issue for this feature is: [#59278]
[#59278]: https://github.com/rust-lang/rust/issues/59278
@@ -3360,9 +3854,27 @@ The tracking issue for this feature is: [#59278]
deny_since: None,
},
Lint {
+ label: "binary_heap_from_raw_vec",
+ description: r##"# `binary_heap_from_raw_vec`
+
+
+
+The tracking issue for this feature is: [#152500]
+
+[#152500]: https://github.com/rust-lang/rust/issues/152500
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "binary_heap_into_iter_sorted",
description: r##"# `binary_heap_into_iter_sorted`
+
+
The tracking issue for this feature is: [#59278]
[#59278]: https://github.com/rust-lang/rust/issues/59278
@@ -3374,9 +3886,75 @@ The tracking issue for this feature is: [#59278]
deny_since: None,
},
Lint {
+ label: "binary_heap_peek_mut_refresh",
+ description: r##"# `binary_heap_peek_mut_refresh`
+
+
+
+The tracking issue for this feature is: [#138355]
+
+[#138355]: https://github.com/rust-lang/rust/issues/138355
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "binary_heap_pop_if",
+ description: r##"# `binary_heap_pop_if`
+
+
+
+The tracking issue for this feature is: [#151828]
+
+[#151828]: https://github.com/rust-lang/rust/issues/151828
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "bool_to_result",
+ description: r##"# `bool_to_result`
+
+
+
+The tracking issue for this feature is: [#142748]
+
+[#142748]: https://github.com/rust-lang/rust/issues/142748
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "borrowed_buf_init",
+ description: r##"# `borrowed_buf_init`
+
+
+
+The tracking issue for this feature is: [#78485]
+
+[#78485]: https://github.com/rust-lang/rust/issues/78485
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "bound_as_ref",
description: r##"# `bound_as_ref`
+
+
The tracking issue for this feature is: [#80996]
[#80996]: https://github.com/rust-lang/rust/issues/80996
@@ -3388,9 +3966,27 @@ The tracking issue for this feature is: [#80996]
deny_since: None,
},
Lint {
+ label: "bound_copied",
+ description: r##"# `bound_copied`
+
+
+
+The tracking issue for this feature is: [#145966]
+
+[#145966]: https://github.com/rust-lang/rust/issues/145966
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "box_as_ptr",
description: r##"# `box_as_ptr`
+
+
The tracking issue for this feature is: [#129090]
[#129090]: https://github.com/rust-lang/rust/issues/129090
@@ -3405,6 +4001,8 @@ The tracking issue for this feature is: [#129090]
label: "box_into_boxed_slice",
description: r##"# `box_into_boxed_slice`
+
+
The tracking issue for this feature is: [#71582]
[#71582]: https://github.com/rust-lang/rust/issues/71582
@@ -3419,6 +4017,8 @@ The tracking issue for this feature is: [#71582]
label: "box_into_inner",
description: r##"# `box_into_inner`
+
+
The tracking issue for this feature is: [#80437]
[#80437]: https://github.com/rust-lang/rust/issues/80437
@@ -3439,6 +4039,8 @@ The tracking issue for this feature is: [#29641]
------------------------
+> **Note**: This feature will be superseded by [`deref_patterns`] in the future.
+
Box patterns let you match on `Box<T>`s:
@@ -3461,18 +4063,22 @@ fn main() {
}
}
```
+
+[`deref_patterns`]: ./deref-patterns.md
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "box_uninit_write",
- description: r##"# `box_uninit_write`
+ label: "box_take",
+ description: r##"# `box_take`
+
-The tracking issue for this feature is: [#129397]
-[#129397]: https://github.com/rust-lang/rust/issues/129397
+The tracking issue for this feature is: [#147212]
+
+[#147212]: https://github.com/rust-lang/rust/issues/147212
------------------------
"##,
@@ -3484,6 +4090,8 @@ The tracking issue for this feature is: [#129397]
label: "box_vec_non_null",
description: r##"# `box_vec_non_null`
+
+
The tracking issue for this feature is: [#130364]
[#130364]: https://github.com/rust-lang/rust/issues/130364
@@ -3498,9 +4106,11 @@ The tracking issue for this feature is: [#130364]
label: "bpf_target_feature",
description: r##"# `bpf_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on bpf.
+
+The tracking issue for this feature is: [#150247]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150247]: https://github.com/rust-lang/rust/issues/150247
------------------------
"##,
@@ -3512,6 +4122,8 @@ The tracking issue for this feature is: [#44839]
label: "breakpoint",
description: r##"# `breakpoint`
+
+
The tracking issue for this feature is: [#133724]
[#133724]: https://github.com/rust-lang/rust/issues/133724
@@ -3523,12 +4135,28 @@ The tracking issue for this feature is: [#133724]
deny_since: None,
},
Lint {
- label: "btree_cursors",
- description: r##"# `btree_cursors`
+ label: "bstr",
+ description: r##"# `bstr`
+
+
+
+The tracking issue for this feature is: [#134915]
+
+[#134915]: https://github.com/rust-lang/rust/issues/134915
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "bstr_internals",
+ description: r##"# `bstr_internals`
-The tracking issue for this feature is: [#107540]
-[#107540]: https://github.com/rust-lang/rust/issues/107540
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -3537,12 +4165,14 @@ The tracking issue for this feature is: [#107540]
deny_since: None,
},
Lint {
- label: "btree_entry_insert",
- description: r##"# `btree_entry_insert`
+ label: "btree_cursors",
+ description: r##"# `btree_cursors`
-The tracking issue for this feature is: [#65225]
-[#65225]: https://github.com/rust-lang/rust/issues/65225
+
+The tracking issue for this feature is: [#107540]
+
+[#107540]: https://github.com/rust-lang/rust/issues/107540
------------------------
"##,
@@ -3551,12 +4181,14 @@ The tracking issue for this feature is: [#65225]
deny_since: None,
},
Lint {
- label: "btree_extract_if",
- description: r##"# `btree_extract_if`
+ label: "btree_merge",
+ description: r##"# `btree_merge`
-The tracking issue for this feature is: [#70530]
-[#70530]: https://github.com/rust-lang/rust/issues/70530
+
+The tracking issue for this feature is: [#152152]
+
+[#152152]: https://github.com/rust-lang/rust/issues/152152
------------------------
"##,
@@ -3568,6 +4200,8 @@ The tracking issue for this feature is: [#70530]
label: "btree_set_entry",
description: r##"# `btree_set_entry`
+
+
The tracking issue for this feature is: [#133549]
[#133549]: https://github.com/rust-lang/rust/issues/133549
@@ -3582,6 +4216,8 @@ The tracking issue for this feature is: [#133549]
label: "btreemap_alloc",
description: r##"# `btreemap_alloc`
+
+
The tracking issue for this feature is: [#32838]
[#32838]: https://github.com/rust-lang/rust/issues/32838
@@ -3596,6 +4232,8 @@ The tracking issue for this feature is: [#32838]
label: "buf_read_has_data_left",
description: r##"# `buf_read_has_data_left`
+
+
The tracking issue for this feature is: [#86423]
[#86423]: https://github.com/rust-lang/rust/issues/86423
@@ -3610,6 +4248,8 @@ The tracking issue for this feature is: [#86423]
label: "bufreader_peek",
description: r##"# `bufreader_peek`
+
+
The tracking issue for this feature is: [#128405]
[#128405]: https://github.com/rust-lang/rust/issues/128405
@@ -3624,6 +4264,8 @@ The tracking issue for this feature is: [#128405]
label: "builtin_syntax",
description: r##"# `builtin_syntax`
+Allows builtin # foo() syntax
+
The tracking issue for this feature is: [#110680]
[#110680]: https://github.com/rust-lang/rust/issues/110680
@@ -3638,23 +4280,11 @@ The tracking issue for this feature is: [#110680]
label: "c_size_t",
description: r##"# `c_size_t`
-The tracking issue for this feature is: [#88345]
-[#88345]: https://github.com/rust-lang/rust/issues/88345
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "c_str_module",
- description: r##"# `c_str_module`
-
-The tracking issue for this feature is: [#112134]
+The tracking issue for this feature is: [#88345]
-[#112134]: https://github.com/rust-lang/rust/issues/112134
+[#88345]: https://github.com/rust-lang/rust/issues/88345
------------------------
"##,
@@ -3694,33 +4324,16 @@ pub unsafe extern "C" fn add(n: usize, mut args: ...) -> usize {
deny_since: None,
},
Lint {
- label: "c_variadic",
- description: r##"# `c_variadic`
+ label: "c_variadic_naked_functions",
+ description: r##"# `c_variadic_naked_functions`
-The tracking issue for this feature is: [#44930]
-
-[#44930]: https://github.com/rust-lang/rust/issues/44930
-
-------------------------
-
-The `c_variadic` library feature exposes the `VaList` structure,
-Rust's analogue of C's `va_list` type.
-
-## Examples
+Allows defining c-variadic naked functions with any extern ABI that is allowed on c-variadic foreign functions.
-```rust
-#![feature(c_variadic)]
+The tracking issue for this feature is: [#148767]
-use std::ffi::VaList;
+[#148767]: https://github.com/rust-lang/rust/issues/148767
-pub unsafe extern "C" fn vadd(n: usize, mut args: VaList) -> usize {
- let mut sum = 0;
- for _ in 0..n {
- sum += args.arg::<usize>();
- }
- sum
-}
-```
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -3742,6 +4355,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "can_vector",
description: r##"# `can_vector`
+
+
The tracking issue for this feature is: [#69941]
[#69941]: https://github.com/rust-lang/rust/issues/69941
@@ -3753,12 +4368,30 @@ The tracking issue for this feature is: [#69941]
deny_since: None,
},
Lint {
- label: "cell_leak",
- description: r##"# `cell_leak`
+ label: "case_ignorable",
+ description: r##"# `case_ignorable`
+
+
+
+The tracking issue for this feature is: [#154848]
+
+[#154848]: https://github.com/rust-lang/rust/issues/154848
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "cast_maybe_uninit",
+ description: r##"# `cast_maybe_uninit`
-The tracking issue for this feature is: [#69099]
-[#69099]: https://github.com/rust-lang/rust/issues/69099
+
+The tracking issue for this feature is: [#145036]
+
+[#145036]: https://github.com/rust-lang/rust/issues/145036
------------------------
"##,
@@ -3767,12 +4400,30 @@ The tracking issue for this feature is: [#69099]
deny_since: None,
},
Lint {
- label: "cell_update",
- description: r##"# `cell_update`
+ label: "cell_get_cloned",
+ description: r##"# `cell_get_cloned`
+
+
-The tracking issue for this feature is: [#50186]
+The tracking issue for this feature is: [#145329]
-[#50186]: https://github.com/rust-lang/rust/issues/50186
+[#145329]: https://github.com/rust-lang/rust/issues/145329
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "cell_leak",
+ description: r##"# `cell_leak`
+
+
+
+The tracking issue for this feature is: [#69099]
+
+[#69099]: https://github.com/rust-lang/rust/issues/69099
------------------------
"##,
@@ -3784,6 +4435,8 @@ The tracking issue for this feature is: [#50186]
label: "cfg_accessible",
description: r##"# `cfg_accessible`
+
+
The tracking issue for this feature is: [#64797]
[#64797]: https://github.com/rust-lang/rust/issues/64797
@@ -3795,12 +4448,14 @@ The tracking issue for this feature is: [#64797]
deny_since: None,
},
Lint {
- label: "cfg_eval",
- description: r##"# `cfg_eval`
+ label: "cfg_contract_checks",
+ description: r##"# `cfg_contract_checks`
-The tracking issue for this feature is: [#82679]
+Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
-[#82679]: https://github.com/rust-lang/rust/issues/82679
+The tracking issue for this feature is: [#128044]
+
+[#128044]: https://github.com/rust-lang/rust/issues/128044
------------------------
"##,
@@ -3809,12 +4464,28 @@ The tracking issue for this feature is: [#82679]
deny_since: None,
},
Lint {
- label: "cfg_match",
- description: r##"# `cfg_match`
+ label: "cfg_emscripten_wasm_eh",
+ description: r##"# `cfg_emscripten_wasm_eh`
-The tracking issue for this feature is: [#115585]
+Allows access to the emscripten_wasm_eh config, used by panic_unwind and unwind
-[#115585]: https://github.com/rust-lang/rust/issues/115585
+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: "cfg_eval",
+ description: r##"# `cfg_eval`
+
+
+
+The tracking issue for this feature is: [#82679]
+
+[#82679]: https://github.com/rust-lang/rust/issues/82679
------------------------
"##,
@@ -3826,6 +4497,8 @@ The tracking issue for this feature is: [#115585]
label: "cfg_overflow_checks",
description: r##"# `cfg_overflow_checks`
+Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.
+
The tracking issue for this feature is: [#111466]
[#111466]: https://github.com/rust-lang/rust/issues/111466
@@ -3840,6 +4513,8 @@ The tracking issue for this feature is: [#111466]
label: "cfg_relocation_model",
description: r##"# `cfg_relocation_model`
+Provides the relocation model information as cfg entry
+
The tracking issue for this feature is: [#114929]
[#114929]: https://github.com/rust-lang/rust/issues/114929
@@ -3895,6 +4570,8 @@ fn b() {
label: "cfg_sanitizer_cfi",
description: r##"# `cfg_sanitizer_cfi`
+Allows `cfg(sanitizer_cfi_generalize_pointers)` and `cfg(sanitizer_cfi_normalize_integers)`.
+
The tracking issue for this feature is: [#89653]
[#89653]: https://github.com/rust-lang/rust/issues/89653
@@ -3909,6 +4586,8 @@ The tracking issue for this feature is: [#89653]
label: "cfg_target_compact",
description: r##"# `cfg_target_compact`
+Allows `cfg(target(abi = "..."))`.
+
The tracking issue for this feature is: [#96901]
[#96901]: https://github.com/rust-lang/rust/issues/96901
@@ -3923,6 +4602,8 @@ The tracking issue for this feature is: [#96901]
label: "cfg_target_has_atomic",
description: r##"# `cfg_target_has_atomic`
+Allows `cfg(target_has_atomic_load_store = "...")`.
+
The tracking issue for this feature is: [#94039]
[#94039]: https://github.com/rust-lang/rust/issues/94039
@@ -3937,6 +4618,8 @@ The tracking issue for this feature is: [#94039]
label: "cfg_target_has_atomic_equal_alignment",
description: r##"# `cfg_target_has_atomic_equal_alignment`
+Allows `cfg(target_has_atomic_equal_alignment = "...")`.
+
The tracking issue for this feature is: [#93822]
[#93822]: https://github.com/rust-lang/rust/issues/93822
@@ -3948,9 +4631,66 @@ The tracking issue for this feature is: [#93822]
deny_since: None,
},
Lint {
+ label: "cfg_target_has_reliable_f16_f128",
+ description: r##"# `cfg_target_has_reliable_f16_f128`
+
+Allows checking whether or not the backend correctly supports unstable float types.
+
+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: "cfg_target_object_format",
+ description: r##"# `cfg_target_object_format`
+
+The tracking issue for this feature is: [#152586]
+
+[#152586]: https://github.com/rust-lang/rust/issues/152586
+
+------------------------
+
+The `cfg_target_object_format` feature makes it possible to execute different code
+depending on the current target's object file format.
+
+## Examples
+
+```rust
+#![feature(cfg_target_object_format)]
+
+#[cfg(target_object_format = "elf")]
+fn a() {
+ // ...
+}
+
+#[cfg(target_object_format = "mach-o")]
+fn a() {
+ // ...
+}
+
+fn b() {
+ if cfg!(target_object_format = "wasm") {
+ // ...
+ } else {
+ // ...
+ }
+}
+```
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "cfg_target_thread_local",
description: r##"# `cfg_target_thread_local`
+Allows `cfg(target_thread_local)`.
+
The tracking issue for this feature is: [#29594]
[#29594]: https://github.com/rust-lang/rust/issues/29594
@@ -3965,6 +4705,8 @@ The tracking issue for this feature is: [#29594]
label: "cfg_ub_checks",
description: r##"# `cfg_ub_checks`
+Allows the use of `#[cfg(ub_checks)` to check if UB checks are enabled.
+
The tracking issue for this feature is: [#123499]
[#123499]: https://github.com/rust-lang/rust/issues/123499
@@ -4053,6 +4795,8 @@ extern {
label: "char_internals",
description: r##"# `char_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -4062,9 +4806,59 @@ 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`
+
+
+
+The tracking issue for this feature is: [#148519]
+
+[#148519]: https://github.com/rust-lang/rust/issues/148519
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "clone_from_ref",
+ description: r##"# `clone_from_ref`
+
+
+
+The tracking issue for this feature is: [#149075]
+
+[#149075]: https://github.com/rust-lang/rust/issues/149075
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "clone_to_uninit",
description: r##"# `clone_to_uninit`
+
+
The tracking issue for this feature is: [#126799]
[#126799]: https://github.com/rust-lang/rust/issues/126799
@@ -4079,6 +4873,8 @@ The tracking issue for this feature is: [#126799]
label: "closure_lifetime_binder",
description: r##"# `closure_lifetime_binder`
+Allows `for<...>` on closures and coroutines.
+
The tracking issue for this feature is: [#97362]
[#97362]: https://github.com/rust-lang/rust/issues/97362
@@ -4112,6 +4908,8 @@ available through `std::panic::Location::caller()`, just like using
label: "cmp_minmax",
description: r##"# `cmp_minmax`
+
+
The tracking issue for this feature is: [#115939]
[#115939]: https://github.com/rust-lang/rust/issues/115939
@@ -4140,10 +4938,9 @@ LLVM, the Rust compiler and the linker are providing
[support](https://developer.arm.com/documentation/ecm0359818/latest/) for the
TrustZone-M feature.
-One of the things provided, with this unstable feature, is the
-`C-cmse-nonsecure-entry` ABI. This ABI marks a Secure function as an
-entry function (see [section
-5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
+One of the things provided with this unstable feature is the "cmse-nonsecure-entry" ABI.
+This ABI marks a Secure function as an entry function (see
+[section 5.4](https://developer.arm.com/documentation/ecm0359818/latest/) for details).
With this ABI, the compiler will do the following:
* add a special symbol on the function which is the `__acle_se_` prefix and the
standard function name
@@ -4154,9 +4951,7 @@ With this ABI, the compiler will do the following:
Because the stack can not be used to pass parameters, there will be compilation
errors if:
-* the total size of all parameters is too big (for example more than four 32
- bits integers)
-* the entry function is not using a C ABI
+* the total size of all parameters is too big (for example, more than four 32-bit integers)
The special symbol `__acle_se_` will be used by the linker to generate a secure
gateway veneer.
@@ -4168,7 +4963,7 @@ gateway veneer.
#![feature(cmse_nonsecure_entry)]
#[no_mangle]
-pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
+pub extern "cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 {
input + 6
}
```
@@ -4211,9 +5006,25 @@ $ arm-none-eabi-objdump -D function.o
deny_since: None,
},
Lint {
+ label: "coerce_pointee_validated",
+ description: r##"# `coerce_pointee_validated`
+
+
+
+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: "coerce_unsized",
description: r##"# `coerce_unsized`
+
+
The tracking issue for this feature is: [#18598]
[#18598]: https://github.com/rust-lang/rust/issues/18598
@@ -4225,6 +5036,22 @@ The tracking issue for this feature is: [#18598]
deny_since: None,
},
Lint {
+ label: "command_resolved_envs",
+ description: r##"# `command_resolved_envs`
+
+
+
+The tracking issue for this feature is: [#149070]
+
+[#149070]: https://github.com/rust-lang/rust/issues/149070
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "compiler_builtins",
description: r##"# `compiler_builtins`
@@ -4240,6 +5067,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "concat_bytes",
description: r##"# `concat_bytes`
+
+
The tracking issue for this feature is: [#87555]
[#87555]: https://github.com/rust-lang/rust/issues/87555
@@ -4251,41 +5080,46 @@ The tracking issue for this feature is: [#87555]
deny_since: None,
},
Lint {
- label: "concat_idents",
- description: r##"# `concat_idents`
+ label: "const_alloc_error",
+ description: r##"# `const_alloc_error`
-The tracking issue for this feature is: [#29599]
-[#29599]: https://github.com/rust-lang/rust/issues/29599
+
+The tracking issue for this feature is: [#92523]
+
+[#92523]: https://github.com/rust-lang/rust/issues/92523
------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_array",
+ description: r##"# `const_array`
-The `concat_idents` feature adds a macro for concatenating multiple identifiers
-into one identifier.
-## Examples
-```rust
-#![feature(concat_idents)]
+The tracking issue for this feature is: [#147606]
-fn main() {
- fn foobar() -> u32 { 23 }
- let f = concat_idents!(foo, bar);
- assert_eq!(f(), 23);
-}
-```
+[#147606]: https://github.com/rust-lang/rust/issues/147606
+
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "const_alloc_error",
- description: r##"# `const_alloc_error`
+ label: "const_async_blocks",
+ description: r##"# `const_async_blocks`
-The tracking issue for this feature is: [#92523]
+Allows `async {}` expressions in const contexts.
-[#92523]: https://github.com/rust-lang/rust/issues/92523
+The tracking issue for this feature is: [#85368]
+
+[#85368]: https://github.com/rust-lang/rust/issues/85368
------------------------
"##,
@@ -4294,12 +5128,14 @@ The tracking issue for this feature is: [#92523]
deny_since: None,
},
Lint {
- label: "const_alloc_layout",
- description: r##"# `const_alloc_layout`
+ label: "const_block_items",
+ description: r##"# `const_block_items`
-The tracking issue for this feature is: [#67521]
+Allows `const { ... }` as a shorthand for `const _: () = const { ... };` for module items.
-[#67521]: https://github.com/rust-lang/rust/issues/67521
+The tracking issue for this feature is: [#149226]
+
+[#149226]: https://github.com/rust-lang/rust/issues/149226
------------------------
"##,
@@ -4308,12 +5144,14 @@ The tracking issue for this feature is: [#67521]
deny_since: None,
},
Lint {
- label: "const_array_as_mut_slice",
- description: r##"# `const_array_as_mut_slice`
+ label: "const_bool",
+ description: r##"# `const_bool`
+
-The tracking issue for this feature is: [#133333]
-[#133333]: https://github.com/rust-lang/rust/issues/133333
+The tracking issue for this feature is: [#151531]
+
+[#151531]: https://github.com/rust-lang/rust/issues/151531
------------------------
"##,
@@ -4322,12 +5160,12 @@ The tracking issue for this feature is: [#133333]
deny_since: None,
},
Lint {
- label: "const_array_each_ref",
- description: r##"# `const_array_each_ref`
+ label: "const_btree_len",
+ description: r##"# `const_btree_len`
+
-The tracking issue for this feature is: [#133289]
-[#133289]: https://github.com/rust-lang/rust/issues/133289
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -4336,12 +5174,14 @@ The tracking issue for this feature is: [#133289]
deny_since: None,
},
Lint {
- label: "const_async_blocks",
- description: r##"# `const_async_blocks`
+ label: "const_c_variadic",
+ description: r##"# `const_c_variadic`
-The tracking issue for this feature is: [#85368]
+Allows defining and calling c-variadic functions in const contexts.
-[#85368]: https://github.com/rust-lang/rust/issues/85368
+The tracking issue for this feature is: [#151787]
+
+[#151787]: https://github.com/rust-lang/rust/issues/151787
------------------------
"##,
@@ -4350,10 +5190,14 @@ The tracking issue for this feature is: [#85368]
deny_since: None,
},
Lint {
- label: "const_black_box",
- description: r##"# `const_black_box`
+ label: "const_carrying_mul_add",
+ description: r##"# `const_carrying_mul_add`
+
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+The tracking issue for this feature is: [#85532]
+
+[#85532]: https://github.com/rust-lang/rust/issues/85532
------------------------
"##,
@@ -4362,12 +5206,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "const_box",
- description: r##"# `const_box`
+ label: "const_cell_traits",
+ description: r##"# `const_cell_traits`
+
-The tracking issue for this feature is: [#92521]
-[#92521]: https://github.com/rust-lang/rust/issues/92521
+The tracking issue for this feature is: [#147787]
+
+[#147787]: https://github.com/rust-lang/rust/issues/147787
------------------------
"##,
@@ -4376,10 +5222,14 @@ The tracking issue for this feature is: [#92521]
deny_since: None,
},
Lint {
- label: "const_btree_len",
- description: r##"# `const_btree_len`
+ label: "const_clone",
+ description: r##"# `const_clone`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#142757]
+
+[#142757]: https://github.com/rust-lang/rust/issues/142757
------------------------
"##,
@@ -4388,12 +5238,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "const_cell",
- description: r##"# `const_cell`
+ label: "const_closures",
+ description: r##"# `const_closures`
+
+Allows `const || {}` closures in const contexts.
-The tracking issue for this feature is: [#131283]
+The tracking issue for this feature is: [#106003]
-[#131283]: https://github.com/rust-lang/rust/issues/131283
+[#106003]: https://github.com/rust-lang/rust/issues/106003
------------------------
"##,
@@ -4402,12 +5254,14 @@ The tracking issue for this feature is: [#131283]
deny_since: None,
},
Lint {
- label: "const_char_classify",
- description: r##"# `const_char_classify`
+ label: "const_cmp",
+ description: r##"# `const_cmp`
-The tracking issue for this feature is: [#132241]
-[#132241]: https://github.com/rust-lang/rust/issues/132241
+
+The tracking issue for this feature is: [#143800]
+
+[#143800]: https://github.com/rust-lang/rust/issues/143800
------------------------
"##,
@@ -4416,12 +5270,14 @@ The tracking issue for this feature is: [#132241]
deny_since: None,
},
Lint {
- label: "const_closures",
- description: r##"# `const_closures`
+ label: "const_control_flow",
+ description: r##"# `const_control_flow`
-The tracking issue for this feature is: [#106003]
-[#106003]: https://github.com/rust-lang/rust/issues/106003
+
+The tracking issue for this feature is: [#148739]
+
+[#148739]: https://github.com/rust-lang/rust/issues/148739
------------------------
"##,
@@ -4430,12 +5286,30 @@ The tracking issue for this feature is: [#106003]
deny_since: None,
},
Lint {
- label: "const_copy_from_slice",
- description: r##"# `const_copy_from_slice`
+ label: "const_convert",
+ description: r##"# `const_convert`
-The tracking issue for this feature is: [#131415]
-[#131415]: https://github.com/rust-lang/rust/issues/131415
+
+The tracking issue for this feature is: [#143773]
+
+[#143773]: https://github.com/rust-lang/rust/issues/143773
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_default",
+ description: r##"# `const_default`
+
+
+
+The tracking issue for this feature is: [#143894]
+
+[#143894]: https://github.com/rust-lang/rust/issues/143894
------------------------
"##,
@@ -4447,6 +5321,8 @@ The tracking issue for this feature is: [#131415]
label: "const_destruct",
description: r##"# `const_destruct`
+Allows using `[const] Destruct` bounds and calling drop impls in const contexts.
+
The tracking issue for this feature is: [#133214]
[#133214]: https://github.com/rust-lang/rust/issues/133214
@@ -4458,9 +5334,41 @@ The tracking issue for this feature is: [#133214]
deny_since: None,
},
Lint {
+ label: "const_drop_guard",
+ description: r##"# `const_drop_guard`
+
+
+
+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_drop_in_place",
+ description: r##"# `const_drop_in_place`
+
+
+
+The tracking issue for this feature is: [#109342]
+
+[#109342]: https://github.com/rust-lang/rust/issues/109342
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "const_eval_select",
description: r##"# `const_eval_select`
+
+
The tracking issue for this feature is: [#124625]
[#124625]: https://github.com/rust-lang/rust/issues/124625
@@ -4475,6 +5383,8 @@ The tracking issue for this feature is: [#124625]
label: "const_for",
description: r##"# `const_for`
+Allows `for _ in _` loops in const contexts.
+
The tracking issue for this feature is: [#87575]
[#87575]: https://github.com/rust-lang/rust/issues/87575
@@ -4489,6 +5399,8 @@ The tracking issue for this feature is: [#87575]
label: "const_format_args",
description: r##"# `const_format_args`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -4501,6 +5413,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "const_heap",
description: r##"# `const_heap`
+
+
The tracking issue for this feature is: [#79597]
[#79597]: https://github.com/rust-lang/rust/issues/79597
@@ -4512,12 +5426,60 @@ The tracking issue for this feature is: [#79597]
deny_since: None,
},
Lint {
- label: "const_is_char_boundary",
- description: r##"# `const_is_char_boundary`
+ label: "const_index",
+ description: r##"# `const_index`
+
+
+
+The tracking issue for this feature is: [#143775]
+
+[#143775]: https://github.com/rust-lang/rust/issues/143775
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_iter",
+ description: r##"# `const_iter`
+
+
+
+The tracking issue for this feature is: [#92476]
+
+[#92476]: https://github.com/rust-lang/rust/issues/92476
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_manually_drop_take",
+ description: r##"# `const_manually_drop_take`
+
+
+
+The tracking issue for this feature is: [#148773]
+
+[#148773]: https://github.com/rust-lang/rust/issues/148773
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_never_short_circuit",
+ description: r##"# `const_never_short_circuit`
-The tracking issue for this feature is: [#131516]
-[#131516]: https://github.com/rust-lang/rust/issues/131516
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -4526,12 +5488,78 @@ The tracking issue for this feature is: [#131516]
deny_since: None,
},
Lint {
- label: "const_mut_cursor",
- description: r##"# `const_mut_cursor`
+ label: "const_nonnull_with_exposed_provenance",
+ description: r##"# `const_nonnull_with_exposed_provenance`
+
+
-The tracking issue for this feature is: [#130801]
+The tracking issue for this feature is: [#154215]
-[#130801]: https://github.com/rust-lang/rust/issues/130801
+[#154215]: https://github.com/rust-lang/rust/issues/154215
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_ops",
+ description: r##"# `const_ops`
+
+
+
+The tracking issue for this feature is: [#143802]
+
+[#143802]: https://github.com/rust-lang/rust/issues/143802
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_option_ops",
+ description: r##"# `const_option_ops`
+
+
+
+The tracking issue for this feature is: [#143956]
+
+[#143956]: https://github.com/rust-lang/rust/issues/143956
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_param_ty_trait",
+ description: r##"# `const_param_ty_trait`
+
+
+
+The tracking issue for this feature is: [#95174]
+
+[#95174]: https://github.com/rust-lang/rust/issues/95174
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_path_separators",
+ description: r##"# `const_path_separators`
+
+
+
+The tracking issue for this feature is: [#153106]
+
+[#153106]: https://github.com/rust-lang/rust/issues/153106
------------------------
"##,
@@ -4543,6 +5571,8 @@ The tracking issue for this feature is: [#130801]
label: "const_precise_live_drops",
description: r##"# `const_precise_live_drops`
+Be more precise when looking for live drops in a const context.
+
The tracking issue for this feature is: [#73255]
[#73255]: https://github.com/rust-lang/rust/issues/73255
@@ -4554,12 +5584,12 @@ The tracking issue for this feature is: [#73255]
deny_since: None,
},
Lint {
- label: "const_ptr_sub_ptr",
- description: r##"# `const_ptr_sub_ptr`
+ label: "const_range",
+ description: r##"# `const_range`
-The tracking issue for this feature is: [#95892]
-[#95892]: https://github.com/rust-lang/rust/issues/95892
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -4571,6 +5601,8 @@ The tracking issue for this feature is: [#95892]
label: "const_range_bounds",
description: r##"# `const_range_bounds`
+
+
The tracking issue for this feature is: [#108082]
[#108082]: https://github.com/rust-lang/rust/issues/108082
@@ -4585,6 +5617,8 @@ The tracking issue for this feature is: [#108082]
label: "const_raw_ptr_comparison",
description: r##"# `const_raw_ptr_comparison`
+
+
The tracking issue for this feature is: [#53020]
[#53020]: https://github.com/rust-lang/rust/issues/53020
@@ -4596,12 +5630,14 @@ The tracking issue for this feature is: [#53020]
deny_since: None,
},
Lint {
- label: "const_slice_flatten",
- description: r##"# `const_slice_flatten`
+ label: "const_ref_cell",
+ description: r##"# `const_ref_cell`
-The tracking issue for this feature is: [#95629]
-[#95629]: https://github.com/rust-lang/rust/issues/95629
+
+The tracking issue for this feature is: [#137844]
+
+[#137844]: https://github.com/rust-lang/rust/issues/137844
------------------------
"##,
@@ -4610,12 +5646,14 @@ The tracking issue for this feature is: [#95629]
deny_since: None,
},
Lint {
- label: "const_slice_from_mut_ptr_range",
- description: r##"# `const_slice_from_mut_ptr_range`
+ label: "const_result_trait_fn",
+ description: r##"# `const_result_trait_fn`
-The tracking issue for this feature is: [#89792]
-[#89792]: https://github.com/rust-lang/rust/issues/89792
+
+The tracking issue for this feature is: [#144211]
+
+[#144211]: https://github.com/rust-lang/rust/issues/144211
------------------------
"##,
@@ -4624,12 +5662,14 @@ The tracking issue for this feature is: [#89792]
deny_since: None,
},
Lint {
- label: "const_slice_from_ptr_range",
- description: r##"# `const_slice_from_ptr_range`
+ label: "const_result_unwrap_unchecked",
+ description: r##"# `const_result_unwrap_unchecked`
-The tracking issue for this feature is: [#89792]
-[#89792]: https://github.com/rust-lang/rust/issues/89792
+
+The tracking issue for this feature is: [#148714]
+
+[#148714]: https://github.com/rust-lang/rust/issues/148714
------------------------
"##,
@@ -4638,12 +5678,14 @@ The tracking issue for this feature is: [#89792]
deny_since: None,
},
Lint {
- label: "const_sockaddr_setters",
- description: r##"# `const_sockaddr_setters`
+ label: "const_select_unpredictable",
+ description: r##"# `const_select_unpredictable`
+
+
-The tracking issue for this feature is: [#131714]
+The tracking issue for this feature is: [#145938]
-[#131714]: https://github.com/rust-lang/rust/issues/131714
+[#145938]: https://github.com/rust-lang/rust/issues/145938
------------------------
"##,
@@ -4652,12 +5694,14 @@ The tracking issue for this feature is: [#131714]
deny_since: None,
},
Lint {
- label: "const_str_from_utf8",
- description: r##"# `const_str_from_utf8`
+ label: "const_slice_from_mut_ptr_range",
+ description: r##"# `const_slice_from_mut_ptr_range`
+
+
-The tracking issue for this feature is: [#91006]
+The tracking issue for this feature is: [#89792]
-[#91006]: https://github.com/rust-lang/rust/issues/91006
+[#89792]: https://github.com/rust-lang/rust/issues/89792
------------------------
"##,
@@ -4666,12 +5710,14 @@ The tracking issue for this feature is: [#91006]
deny_since: None,
},
Lint {
- label: "const_str_split_at",
- description: r##"# `const_str_split_at`
+ label: "const_slice_from_ptr_range",
+ description: r##"# `const_slice_from_ptr_range`
-The tracking issue for this feature is: [#131518]
-[#131518]: https://github.com/rust-lang/rust/issues/131518
+
+The tracking issue for this feature is: [#89792]
+
+[#89792]: https://github.com/rust-lang/rust/issues/89792
------------------------
"##,
@@ -4680,12 +5726,14 @@ The tracking issue for this feature is: [#131518]
deny_since: None,
},
Lint {
- label: "const_swap",
- description: r##"# `const_swap`
+ label: "const_slice_make_iter",
+ description: r##"# `const_slice_make_iter`
-The tracking issue for this feature is: [#83163]
-[#83163]: https://github.com/rust-lang/rust/issues/83163
+
+The tracking issue for this feature is: [#137737]
+
+[#137737]: https://github.com/rust-lang/rust/issues/137737
------------------------
"##,
@@ -4694,12 +5742,30 @@ The tracking issue for this feature is: [#83163]
deny_since: None,
},
Lint {
- label: "const_swap_nonoverlapping",
- description: r##"# `const_swap_nonoverlapping`
+ label: "const_split_off_first_last",
+ description: r##"# `const_split_off_first_last`
+
-The tracking issue for this feature is: [#133668]
-[#133668]: https://github.com/rust-lang/rust/issues/133668
+The tracking issue for this feature is: [#138539]
+
+[#138539]: https://github.com/rust-lang/rust/issues/138539
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "const_swap_with_slice",
+ description: r##"# `const_swap_with_slice`
+
+
+
+The tracking issue for this feature is: [#142204]
+
+[#142204]: https://github.com/rust-lang/rust/issues/142204
------------------------
"##,
@@ -4711,6 +5777,8 @@ The tracking issue for this feature is: [#133668]
label: "const_trait_impl",
description: r##"# `const_trait_impl`
+Allows `impl const Trait for T` syntax.
+
The tracking issue for this feature is: [#143874]
[#143874]: https://github.com/rust-lang/rust/issues/143874
@@ -4725,6 +5793,8 @@ The tracking issue for this feature is: [#143874]
label: "const_try",
description: r##"# `const_try`
+Allows the `?` operator in const contexts.
+
The tracking issue for this feature is: [#74935]
[#74935]: https://github.com/rust-lang/rust/issues/74935
@@ -4736,12 +5806,14 @@ The tracking issue for this feature is: [#74935]
deny_since: None,
},
Lint {
- label: "const_type_id",
- description: r##"# `const_type_id`
+ label: "const_try_residual",
+ description: r##"# `const_try_residual`
+
-The tracking issue for this feature is: [#77125]
-[#77125]: https://github.com/rust-lang/rust/issues/77125
+The tracking issue for this feature is: [#91285]
+
+[#91285]: https://github.com/rust-lang/rust/issues/91285
------------------------
"##,
@@ -4753,6 +5825,8 @@ The tracking issue for this feature is: [#77125]
label: "const_type_name",
description: r##"# `const_type_name`
+
+
The tracking issue for this feature is: [#63084]
[#63084]: https://github.com/rust-lang/rust/issues/63084
@@ -4764,8 +5838,26 @@ The tracking issue for this feature is: [#63084]
deny_since: None,
},
Lint {
- label: "const_typed_swap",
- description: r##"# `const_typed_swap`
+ label: "const_unsigned_bigint_helpers",
+ description: r##"# `const_unsigned_bigint_helpers`
+
+
+
+The tracking issue for this feature is: [#152015]
+
+[#152015]: https://github.com/rust-lang/rust/issues/152015
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "container_error_extra",
+ description: r##"# `container_error_extra`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -4776,12 +5868,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "const_vec_string_slice",
- description: r##"# `const_vec_string_slice`
+ label: "context_ext",
+ description: r##"# `context_ext`
+
-The tracking issue for this feature is: [#129041]
-[#129041]: https://github.com/rust-lang/rust/issues/129041
+The tracking issue for this feature is: [#123392]
+
+[#123392]: https://github.com/rust-lang/rust/issues/123392
------------------------
"##,
@@ -4790,10 +5884,14 @@ The tracking issue for this feature is: [#129041]
deny_since: None,
},
Lint {
- label: "container_error_extra",
- description: r##"# `container_error_extra`
+ label: "contracts",
+ description: r##"# `contracts`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+Allows use of contracts attributes.
+
+The tracking issue for this feature is: [#128044]
+
+[#128044]: https://github.com/rust-lang/rust/issues/128044
------------------------
"##,
@@ -4802,12 +5900,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "context_ext",
- description: r##"# `context_ext`
+ label: "contracts_internals",
+ description: r##"# `contracts_internals`
-The tracking issue for this feature is: [#123392]
+Allows access to internal machinery used to implement contracts.
+
+The tracking issue for this feature is: [#128044]
+
+[#128044]: https://github.com/rust-lang/rust/issues/128044
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "control_flow_into_value",
+ description: r##"# `control_flow_into_value`
-[#123392]: https://github.com/rust-lang/rust/issues/123392
+
+
+The tracking issue for this feature is: [#137461]
+
+[#137461]: https://github.com/rust-lang/rust/issues/137461
------------------------
"##,
@@ -4819,6 +5935,8 @@ The tracking issue for this feature is: [#123392]
label: "convert_float_to_int",
description: r##"# `convert_float_to_int`
+
+
The tracking issue for this feature is: [#67057]
[#67057]: https://github.com/rust-lang/rust/issues/67057
@@ -4830,6 +5948,36 @@ The tracking issue for this feature is: [#67057]
deny_since: None,
},
Lint {
+ label: "copied_into_inner",
+ description: r##"# `copied_into_inner`
+
+
+
+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_float_math",
+ description: r##"# `core_float_math`
+
+
+
+The tracking issue for this feature is: [#137578]
+
+[#137578]: https://github.com/rust-lang/rust/issues/137578
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "core_intrinsics",
description: r##"# `core_intrinsics`
@@ -4842,9 +5990,25 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
+ label: "core_intrinsics_fallbacks",
+ description: r##"# `core_intrinsics_fallbacks`
+
+
+
+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_io_borrowed_buf",
description: r##"# `core_io_borrowed_buf`
+
+
The tracking issue for this feature is: [#117693]
[#117693]: https://github.com/rust-lang/rust/issues/117693
@@ -4883,6 +6047,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "coroutine_clone",
description: r##"# `coroutine_clone`
+Allows coroutines to be cloned.
+
The tracking issue for this feature is: [#95360]
[#95360]: https://github.com/rust-lang/rust/issues/95360
@@ -4897,6 +6063,8 @@ The tracking issue for this feature is: [#95360]
label: "coroutine_trait",
description: r##"# `coroutine_trait`
+
+
The tracking issue for this feature is: [#43122]
[#43122]: https://github.com/rust-lang/rust/issues/43122
@@ -5202,6 +6370,8 @@ fn bar() {
label: "cow_is_borrowed",
description: r##"# `cow_is_borrowed`
+
+
The tracking issue for this feature is: [#65143]
[#65143]: https://github.com/rust-lang/rust/issues/65143
@@ -5216,9 +6386,11 @@ The tracking issue for this feature is: [#65143]
label: "csky_target_feature",
description: r##"# `csky_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on csky.
+
+The tracking issue for this feature is: [#150248]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150248]: https://github.com/rust-lang/rust/issues/150248
------------------------
"##,
@@ -5230,6 +6402,8 @@ The tracking issue for this feature is: [#44839]
label: "cstr_bytes",
description: r##"# `cstr_bytes`
+
+
The tracking issue for this feature is: [#112115]
[#112115]: https://github.com/rust-lang/rust/issues/112115
@@ -5241,9 +6415,27 @@ The tracking issue for this feature is: [#112115]
deny_since: None,
},
Lint {
+ label: "cstr_display",
+ description: r##"# `cstr_display`
+
+
+
+The tracking issue for this feature is: [#139984]
+
+[#139984]: https://github.com/rust-lang/rust/issues/139984
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "cstr_internals",
description: r##"# `cstr_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -5253,9 +6445,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "current_thread_id",
+ description: r##"# `current_thread_id`
+
+
+
+The tracking issue for this feature is: [#147194]
+
+[#147194]: https://github.com/rust-lang/rust/issues/147194
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "cursor_split",
description: r##"# `cursor_split`
+
+
The tracking issue for this feature is: [#86369]
[#86369]: https://github.com/rust-lang/rust/issues/86369
@@ -5270,6 +6480,8 @@ The tracking issue for this feature is: [#86369]
label: "custom_inner_attributes",
description: r##"# `custom_inner_attributes`
+Allows non-builtin attributes in inner attribute position.
+
The tracking issue for this feature is: [#54726]
[#54726]: https://github.com/rust-lang/rust/issues/54726
@@ -5284,6 +6496,8 @@ The tracking issue for this feature is: [#54726]
label: "custom_mir",
description: r##"# `custom_mir`
+Allows writing custom MIR
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -5332,9 +6546,27 @@ const WILL_FAIL: i32 = 4;
deny_since: None,
},
Lint {
+ label: "darwin_objc",
+ description: r##"# `darwin_objc`
+
+
+
+The tracking issue for this feature is: [#145496]
+
+[#145496]: https://github.com/rust-lang/rust/issues/145496
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "deadline_api",
description: r##"# `deadline_api`
+
+
The tracking issue for this feature is: [#46316]
[#46316]: https://github.com/rust-lang/rust/issues/46316
@@ -5349,6 +6581,8 @@ The tracking issue for this feature is: [#46316]
label: "debug_closure_helpers",
description: r##"# `debug_closure_helpers`
+
+
The tracking issue for this feature is: [#117729]
[#117729]: https://github.com/rust-lang/rust/issues/117729
@@ -5375,6 +6609,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "decl_macro",
description: r##"# `decl_macro`
+Allows declarative macros 2.0 (`macro`).
+
The tracking issue for this feature is: [#39412]
[#39412]: https://github.com/rust-lang/rust/issues/39412
@@ -5393,19 +6629,107 @@ The tracking issue for this feature is: [#132162]
[#132162]: https://github.com/rust-lang/rust/issues/132162
+The RFC for this feature is: [#3681]
+
+[#3681]: https://github.com/rust-lang/rfcs/blob/master/text/3681-default-field-values.md
+
------------------------
+
+The `default_field_values` feature allows users to specify a const value for
+individual fields in struct definitions, allowing those to be omitted from
+initializers.
+
+## Examples
+
+```rust
+#![feature(default_field_values)]
+
+#[derive(Default)]
+struct Pet {
+ name: Option<String>, // impl Default for Pet will use Default::default() for name
+ age: i128 = 42, // impl Default for Pet will use the literal 42 for age
+}
+
+fn main() {
+ let a = Pet { name: Some(String::new()), .. }; // Pet { name: Some(""), age: 42 }
+ let b = Pet::default(); // Pet { name: None, age: 42 }
+ assert_eq!(a.age, b.age);
+ // The following would be a compilation error: `name` needs to be specified
+ // let _ = Pet { .. };
+}
+```
+
+## `#[derive(Default)]`
+
+When deriving Default, the provided values are then used. On enum variants,
+the variant must still be marked with `#[default]` and have all its fields
+with default values.
+
+```rust
+#![feature(default_field_values)]
+
+#[derive(Default)]
+enum A {
+ #[default]
+ B {
+ x: i32 = 0,
+ y: i32 = 0,
+ },
+ C,
+}
+```
+
+## Enum variants
+
+This feature also supports enum variants for both specifying default values
+and `#[derive(Default)]`.
+
+## Interaction with `#[non_exhaustive]`
+
+A struct or enum variant marked with `#[non_exhaustive]` is not allowed to
+have default field values.
+
+## Lints
+
+When manually implementing the `Default` trait for a type that has default
+field values, if any of these are overridden in the impl the
+`default_overrides_default_fields` lint will trigger. This lint is in place
+to avoid surprising diverging behavior between `S { .. }` and
+`S::default()`, where using the same type in both ways could result in
+different values. The appropriate way to write a manual `Default`
+implementation is to use the functional update syntax:
+
+```rust
+#![feature(default_field_values)]
+
+struct Pet {
+ name: String,
+ age: i128 = 42, // impl Default for Pet will use the literal 42 for age
+}
+
+impl Default for Pet {
+ fn default() -> Pet {
+ Pet {
+ name: "no-name".to_string(),
+ ..
+ }
+ }
+}
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "deprecated_safe",
- description: r##"# `deprecated_safe`
+ label: "deprecated_suggestion",
+ description: r##"# `deprecated_suggestion`
-The tracking issue for this feature is: [#94978]
+Allows having using `suggestion` in the `#[deprecated]` attribute.
-[#94978]: https://github.com/rust-lang/rust/issues/94978
+The tracking issue for this feature is: [#94785]
+
+[#94785]: https://github.com/rust-lang/rust/issues/94785
------------------------
"##,
@@ -5414,12 +6738,14 @@ The tracking issue for this feature is: [#94978]
deny_since: None,
},
Lint {
- label: "deprecated_suggestion",
- description: r##"# `deprecated_suggestion`
+ label: "deque_extend_front",
+ description: r##"# `deque_extend_front`
-The tracking issue for this feature is: [#94785]
-[#94785]: https://github.com/rust-lang/rust/issues/94785
+
+The tracking issue for this feature is: [#146975]
+
+[#146975]: https://github.com/rust-lang/rust/issues/146975
------------------------
"##,
@@ -5436,6 +6762,101 @@ The tracking issue for this feature is: [#87121]
[#87121]: https://github.com/rust-lang/rust/issues/87121
------------------------
+
+> **Note**: This feature supersedes [`box_patterns`].
+
+This feature permits pattern matching on [smart pointers in the standard library] through their
+`Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which
+is currently a placeholder).
+
+```rust
+#![feature(deref_patterns)]
+
+let mut v = vec![Box::new(Some(0))];
+
+// Implicit dereferences are inserted when a pattern can match against the
+// result of repeatedly dereferencing but can't match against a smart
+// pointer itself. This works alongside match ergonomics for references.
+if let [Some(x)] = &mut v {
+ *x += 1;
+}
+
+// Explicit `deref!(_)` patterns may instead be used when finer control is
+// needed, e.g. to dereference only a single smart pointer, or to bind the
+// the result of dereferencing to a variable.
+if let deref!([deref!(opt_x @ Some(1))]) = &mut v {
+ opt_x.as_mut().map(|x| *x += 1);
+}
+
+assert_eq!(v, [Box::new(Some(2))]);
+```
+
+Without this feature, it may be necessary to introduce temporaries to represent dereferenced places
+when matching on nested structures:
+
+```rust
+let mut v = vec![Box::new(Some(0))];
+if let [b] = &mut *v {
+ if let Some(x) = &mut **b {
+ *x += 1;
+ }
+}
+if let [b] = &mut *v {
+ if let opt_x @ Some(1) = &mut **b {
+ opt_x.as_mut().map(|x| *x += 1);
+ }
+}
+assert_eq!(v, [Box::new(Some(2))]);
+```
+
+Like [`box_patterns`], deref patterns may move out of boxes:
+
+```rust
+# #![feature(deref_patterns)]
+struct NoCopy;
+let deref!(x) = Box::new(NoCopy);
+drop::<NoCopy>(x);
+```
+
+Additionally, `deref_patterns` implements changes to string and byte string literal patterns,
+allowing then to be used in deref patterns:
+
+```rust
+# #![feature(deref_patterns)]
+match ("test".to_string(), Box::from("test"), b"test".to_vec()) {
+ ("test", "test", b"test") => {}
+ _ => panic!(),
+}
+
+// This works through multiple layers of reference and smart pointer:
+match (&Box::new(&"test".to_string()), &&&"test") {
+ ("test", "test") => {}
+ _ => panic!(),
+}
+
+// `deref!("...")` syntax may also be used:
+match "test".to_string() {
+ deref!("test") => {}
+ _ => panic!(),
+}
+
+// Matching on slices and arrays using literals is possible elsewhere as well:
+match *"test" {
+ "test" => {}
+ _ => panic!(),
+}
+match *b"test" {
+ b"test" => {}
+ _ => panic!(),
+}
+match *(b"test" as &[u8]) {
+ b"test" => {}
+ _ => panic!(),
+}
+```
+
+[`box_patterns`]: ./box-patterns.md
+[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -5445,6 +6866,8 @@ The tracking issue for this feature is: [#87121]
label: "deref_pure_trait",
description: r##"# `deref_pure_trait`
+
+
The tracking issue for this feature is: [#87121]
[#87121]: https://github.com/rust-lang/rust/issues/87121
@@ -5456,8 +6879,8 @@ The tracking issue for this feature is: [#87121]
deny_since: None,
},
Lint {
- label: "derive_clone_copy",
- description: r##"# `derive_clone_copy`
+ label: "derive_clone_copy_internals",
+ description: r##"# `derive_clone_copy_internals`
This feature is internal to the Rust compiler and is not intended for general use.
@@ -5471,6 +6894,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "derive_coerce_pointee",
description: r##"# `derive_coerce_pointee`
+
+
The tracking issue for this feature is: [#123430]
[#123430]: https://github.com/rust-lang/rust/issues/123430
@@ -5485,7 +6910,11 @@ The tracking issue for this feature is: [#123430]
label: "derive_const",
description: r##"# `derive_const`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#118304]
+
+[#118304]: https://github.com/rust-lang/rust/issues/118304
------------------------
"##,
@@ -5494,8 +6923,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "derive_eq",
- description: r##"# `derive_eq`
+ label: "derive_eq_internals",
+ description: r##"# `derive_eq_internals`
This feature is internal to the Rust compiler and is not intended for general use.
@@ -5506,9 +6935,91 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
+ label: "derive_from",
+ description: r##"# `derive_from`
+
+Allows deriving the From trait on single-field structs.
+
+The tracking issue for this feature is: [#144889]
+
+[#144889]: https://github.com/rust-lang/rust/issues/144889
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "derive_macro_global_path",
+ description: r##"# `derive_macro_global_path`
+
+
+
+The tracking issue for this feature is: [#154645]
+
+[#154645]: https://github.com/rust-lang/rust/issues/154645
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "diagnostic_on_const",
+ description: r##"# `diagnostic_on_const`
+
+Allows giving non-const impls custom diagnostic messages if attempted to be used as const
+
+The tracking issue for this feature is: [#143874]
+
+[#143874]: https://github.com/rust-lang/rust/issues/143874
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ 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
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "diagnostic_on_unknown",
+ description: r##"# `diagnostic_on_unknown`
+
+Allows giving unresolved imports a custom diagnostic message
+
+The tracking issue for this feature is: [#152900]
+
+[#152900]: https://github.com/rust-lang/rust/issues/152900
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "dir_entry_ext2",
description: r##"# `dir_entry_ext2`
+
+
The tracking issue for this feature is: [#85573]
[#85573]: https://github.com/rust-lang/rust/issues/85573
@@ -5520,10 +7031,14 @@ The tracking issue for this feature is: [#85573]
deny_since: None,
},
Lint {
- label: "discriminant_kind",
- description: r##"# `discriminant_kind`
+ label: "dirfd",
+ description: r##"# `dirfd`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#120426]
+
+[#120426]: https://github.com/rust-lang/rust/issues/120426
------------------------
"##,
@@ -5532,8 +7047,26 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "dispatch_from_dyn",
- description: r##"# `dispatch_from_dyn`
+ label: "dirhandle",
+ description: r##"# `dirhandle`
+
+
+
+The tracking issue for this feature is: [#120426]
+
+[#120426]: https://github.com/rust-lang/rust/issues/120426
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "discriminant_kind",
+ description: r##"# `discriminant_kind`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -5544,12 +7077,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "do_not_recommend",
- description: r##"# `do_not_recommend`
+ label: "disjoint_bitor",
+ description: r##"# `disjoint_bitor`
+
+
-The tracking issue for this feature is: [#51992]
+The tracking issue for this feature is: [#135758]
-[#51992]: https://github.com/rust-lang/rust/issues/51992
+[#135758]: https://github.com/rust-lang/rust/issues/135758
------------------------
"##,
@@ -5558,12 +7093,12 @@ The tracking issue for this feature is: [#51992]
deny_since: None,
},
Lint {
- label: "doc_auto_cfg",
- description: r##"# `doc_auto_cfg`
+ label: "dispatch_from_dyn",
+ description: r##"# `dispatch_from_dyn`
-The tracking issue for this feature is: [#43781]
-[#43781]: https://github.com/rust-lang/rust/issues/43781
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -5625,20 +7160,6 @@ pub struct Icon {
deny_since: None,
},
Lint {
- label: "doc_cfg_hide",
- description: r##"# `doc_cfg_hide`
-
-The tracking issue for this feature is: [#43781]
-
-[#43781]: https://github.com/rust-lang/rust/issues/43781
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "doc_masked",
description: r##"# `doc_masked`
@@ -5713,6 +7234,8 @@ See also its documentation in [the rustdoc book][rustdoc-book-notable_trait].
label: "downcast_unchecked",
description: r##"# `downcast_unchecked`
+
+
The tracking issue for this feature is: [#90850]
[#90850]: https://github.com/rust-lang/rust/issues/90850
@@ -5727,6 +7250,8 @@ The tracking issue for this feature is: [#90850]
label: "drain_keep_rest",
description: r##"# `drain_keep_rest`
+
+
The tracking issue for this feature is: [#101122]
[#101122]: https://github.com/rust-lang/rust/issues/101122
@@ -5738,9 +7263,27 @@ The tracking issue for this feature is: [#101122]
deny_since: None,
},
Lint {
+ label: "drop_guard",
+ description: r##"# `drop_guard`
+
+
+
+The tracking issue for this feature is: [#144426]
+
+[#144426]: https://github.com/rust-lang/rust/issues/144426
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "dropck_eyepatch",
description: r##"# `dropck_eyepatch`
+Allows using the `may_dangle` attribute (RFC 1327).
+
The tracking issue for this feature is: [#34761]
[#34761]: https://github.com/rust-lang/rust/issues/34761
@@ -5755,6 +7298,8 @@ The tracking issue for this feature is: [#34761]
label: "duration_constants",
description: r##"# `duration_constants`
+
+
The tracking issue for this feature is: [#57391]
[#57391]: https://github.com/rust-lang/rust/issues/57391
@@ -5782,9 +7327,27 @@ Add the methods `from_days` and `from_weeks` to `Duration`.
deny_since: None,
},
Lint {
+ label: "duration_integer_division",
+ description: r##"# `duration_integer_division`
+
+
+
+The tracking issue for this feature is: [#149573]
+
+[#149573]: https://github.com/rust-lang/rust/issues/149573
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "duration_millis_float",
description: r##"# `duration_millis_float`
+
+
The tracking issue for this feature is: [#122451]
[#122451]: https://github.com/rust-lang/rust/issues/122451
@@ -5799,6 +7362,8 @@ The tracking issue for this feature is: [#122451]
label: "duration_units",
description: r##"# `duration_units`
+
+
The tracking issue for this feature is: [#120301]
[#120301]: https://github.com/rust-lang/rust/issues/120301
@@ -5810,12 +7375,12 @@ The tracking issue for this feature is: [#120301]
deny_since: None,
},
Lint {
- label: "dyn_compatible_for_dispatch",
- description: r##"# `dyn_compatible_for_dispatch`
+ label: "edition_panic",
+ description: r##"# `edition_panic`
-The tracking issue for this feature is: [#43561]
-[#43561]: https://github.com/rust-lang/rust/issues/43561
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -5824,12 +7389,14 @@ The tracking issue for this feature is: [#43561]
deny_since: None,
},
Lint {
- label: "dyn_star",
- description: r##"# `dyn_star`
+ label: "effective_target_features",
+ description: r##"# `effective_target_features`
-The tracking issue for this feature is: [#102425]
+Allows features to allow target_feature to better interact with traits.
-[#102425]: https://github.com/rust-lang/rust/issues/102425
+The tracking issue for this feature is: [#143352]
+
+[#143352]: https://github.com/rust-lang/rust/issues/143352
------------------------
"##,
@@ -5838,8 +7405,10 @@ The tracking issue for this feature is: [#102425]
deny_since: None,
},
Lint {
- label: "edition_panic",
- description: r##"# `edition_panic`
+ label: "eii_internals",
+ description: r##"# `eii_internals`
+
+Implementation details of externally implementable items
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -5850,12 +7419,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "ergonomic_clones",
+ description: r##"# `ergonomic_clones`
+
+Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
+
+The tracking issue for this feature is: [#132290]
+
+[#132290]: https://github.com/rust-lang/rust/issues/132290
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ermsb_target_feature",
description: r##"# `ermsb_target_feature`
-The tracking issue for this feature is: [#44839]
+ermsb target feature on x86.
+
+The tracking issue for this feature is: [#150249]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150249]: https://github.com/rust-lang/rust/issues/150249
------------------------
"##,
@@ -5867,6 +7454,8 @@ The tracking issue for this feature is: [#44839]
label: "error_generic_member_access",
description: r##"# `error_generic_member_access`
+
+
The tracking issue for this feature is: [#99301]
[#99301]: https://github.com/rust-lang/rust/issues/99301
@@ -5881,6 +7470,8 @@ The tracking issue for this feature is: [#99301]
label: "error_iter",
description: r##"# `error_iter`
+
+
The tracking issue for this feature is: [#58520]
[#58520]: https://github.com/rust-lang/rust/issues/58520
@@ -5895,6 +7486,8 @@ The tracking issue for this feature is: [#58520]
label: "error_reporter",
description: r##"# `error_reporter`
+
+
The tracking issue for this feature is: [#90172]
[#90172]: https://github.com/rust-lang/rust/issues/90172
@@ -5909,6 +7502,8 @@ The tracking issue for this feature is: [#90172]
label: "error_type_id",
description: r##"# `error_type_id`
+
+
The tracking issue for this feature is: [#60784]
[#60784]: https://github.com/rust-lang/rust/issues/60784
@@ -5920,9 +7515,43 @@ The tracking issue for this feature is: [#60784]
deny_since: None,
},
Lint {
+ label: "exact_bitshifts",
+ description: r##"# `exact_bitshifts`
+
+
+
+The tracking issue for this feature is: [#144336]
+
+[#144336]: https://github.com/rust-lang/rust/issues/144336
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "exact_div",
+ description: r##"# `exact_div`
+
+
+
+The tracking issue for this feature is: [#139911]
+
+[#139911]: https://github.com/rust-lang/rust/issues/139911
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "exact_size_is_empty",
description: r##"# `exact_size_is_empty`
+
+
The tracking issue for this feature is: [#35428]
[#35428]: https://github.com/rust-lang/rust/issues/35428
@@ -5937,6 +7566,8 @@ The tracking issue for this feature is: [#35428]
label: "exclusive_wrapper",
description: r##"# `exclusive_wrapper`
+
+
The tracking issue for this feature is: [#98407]
[#98407]: https://github.com/rust-lang/rust/issues/98407
@@ -5951,6 +7582,8 @@ The tracking issue for this feature is: [#98407]
label: "exhaustive_patterns",
description: r##"# `exhaustive_patterns`
+Allows exhaustive pattern matching on types that contain uninhabited types.
+
The tracking issue for this feature is: [#51085]
[#51085]: https://github.com/rust-lang/rust/issues/51085
@@ -5965,6 +7598,8 @@ The tracking issue for this feature is: [#51085]
label: "exit_status_error",
description: r##"# `exit_status_error`
+
+
The tracking issue for this feature is: [#84908]
[#84908]: https://github.com/rust-lang/rust/issues/84908
@@ -5979,6 +7614,8 @@ The tracking issue for this feature is: [#84908]
label: "exitcode_exit_method",
description: r##"# `exitcode_exit_method`
+
+
The tracking issue for this feature is: [#97100]
[#97100]: https://github.com/rust-lang/rust/issues/97100
@@ -5990,9 +7627,43 @@ The tracking issue for this feature is: [#97100]
deny_since: None,
},
Lint {
+ label: "explicit_extern_abis",
+ description: r##"# `explicit_extern_abis`
+
+The tracking issue for this feature is: [#134986]
+
+------
+
+Disallow `extern` without an explicit ABI. We should write `extern "C"`
+(or another ABI) instead of just `extern`.
+
+By making the ABI explicit, it becomes much clearer that "C" is just one of the
+possible choices, rather than the "standard" way for external functions.
+Removing the default makes it easier to add a new ABI on equal footing as "C".
+
+```rust,editionfuture,compile_fail
+#![feature(explicit_extern_abis)]
+
+extern fn function1() {} // ERROR `extern` declarations without an explicit ABI
+ // are disallowed
+
+extern "C" fn function2() {} // compiles
+
+extern "aapcs" fn function3() {} // compiles
+```
+
+[#134986]: https://github.com/rust-lang/rust/issues/134986
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "explicit_tail_calls",
description: r##"# `explicit_tail_calls`
+Allows explicit tail calls via `become` expression.
+
The tracking issue for this feature is: [#112788]
[#112788]: https://github.com/rust-lang/rust/issues/112788
@@ -6004,9 +7675,27 @@ The tracking issue for this feature is: [#112788]
deny_since: None,
},
Lint {
+ label: "export_stable",
+ description: r##"# `export_stable`
+
+Allows using `#[export_stable]` which indicates that an item is exportable.
+
+The tracking issue for this feature is: [#139939]
+
+[#139939]: https://github.com/rust-lang/rust/issues/139939
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "extend_one",
description: r##"# `extend_one`
+
+
The tracking issue for this feature is: [#72631]
[#72631]: https://github.com/rust-lang/rust/issues/72631
@@ -6021,6 +7710,8 @@ The tracking issue for this feature is: [#72631]
label: "extend_one_unchecked",
description: r##"# `extend_one_unchecked`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6030,12 +7721,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "extern_types",
- description: r##"# `extern_types`
+ label: "extern_item_impls",
+ description: r##"# `extern_item_impls`
-The tracking issue for this feature is: [#43467]
+Externally implementable items
-[#43467]: https://github.com/rust-lang/rust/issues/43467
+The tracking issue for this feature is: [#125418]
+
+[#125418]: https://github.com/rust-lang/rust/issues/125418
------------------------
"##,
@@ -6044,12 +7737,14 @@ The tracking issue for this feature is: [#43467]
deny_since: None,
},
Lint {
- label: "extract_if",
- description: r##"# `extract_if`
+ label: "extern_types",
+ description: r##"# `extern_types`
+
+Allows defining `extern type`s.
-The tracking issue for this feature is: [#43244]
+The tracking issue for this feature is: [#43467]
-[#43244]: https://github.com/rust-lang/rust/issues/43244
+[#43467]: https://github.com/rust-lang/rust/issues/43467
------------------------
"##,
@@ -6067,7 +7762,7 @@ The tracking issue for this feature is: [#116909]
---
-Enable the `f128` type for IEEE 128-bit floating numbers (quad precision).
+Enable the `f128` type for IEEE 128-bit floating numbers (quad precision).
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -6083,17 +7778,21 @@ The tracking issue for this feature is: [#116909]
---
-Enable the `f16` type for IEEE 16-bit floating numbers (half precision).
+Enable the `f16` type for IEEE 16-bit floating numbers (half precision).
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "fd",
- description: r##"# `fd`
+ label: "f32_from_f16",
+ description: r##"# `f32_from_f16`
-This feature is internal to the Rust compiler and is not intended for general use.
+
+
+The tracking issue for this feature is: [#154005]
+
+[#154005]: https://github.com/rust-lang/rust/issues/154005
------------------------
"##,
@@ -6102,8 +7801,8 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
- label: "fd_read",
- description: r##"# `fd_read`
+ label: "fd",
+ description: r##"# `fd`
This feature is internal to the Rust compiler and is not intended for general use.
@@ -6236,9 +7935,41 @@ against are compatible with those of the `#[ffi_pure]`.
deny_since: None,
},
Lint {
+ label: "field_projections",
+ description: r##"# `field_projections`
+
+Experimental field projections.
+
+The tracking issue for this feature is: [#145383]
+
+[#145383]: https://github.com/rust-lang/rust/issues/145383
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "field_representing_type_raw",
+ description: r##"# `field_representing_type_raw`
+
+Implementation details of field representing types.
+
+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: "file_buffered",
description: r##"# `file_buffered`
+
+
The tracking issue for this feature is: [#130804]
[#130804]: https://github.com/rust-lang/rust/issues/130804
@@ -6250,12 +7981,78 @@ The tracking issue for this feature is: [#130804]
deny_since: None,
},
Lint {
- label: "file_lock",
- description: r##"# `file_lock`
+ label: "final_associated_functions",
+ description: r##"# `final_associated_functions`
+
+Allows marking trait functions as `final` to prevent overriding impls
-The tracking issue for this feature is: [#130994]
+The tracking issue for this feature is: [#131179]
-[#130994]: https://github.com/rust-lang/rust/issues/130994
+[#131179]: https://github.com/rust-lang/rust/issues/131179
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "float_algebraic",
+ description: r##"# `float_algebraic`
+
+
+
+The tracking issue for this feature is: [#136469]
+
+[#136469]: https://github.com/rust-lang/rust/issues/136469
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "float_bits_const",
+ description: r##"# `float_bits_const`
+
+
+
+The tracking issue for this feature is: [#151073]
+
+[#151073]: https://github.com/rust-lang/rust/issues/151073
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "float_erf",
+ description: r##"# `float_erf`
+
+
+
+The tracking issue for this feature is: [#136321]
+
+[#136321]: https://github.com/rust-lang/rust/issues/136321
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "float_exact_integer_constants",
+ description: r##"# `float_exact_integer_constants`
+
+
+
+The tracking issue for this feature is: [#152466]
+
+[#152466]: https://github.com/rust-lang/rust/issues/152466
------------------------
"##,
@@ -6267,6 +8064,8 @@ The tracking issue for this feature is: [#130994]
label: "float_gamma",
description: r##"# `float_gamma`
+
+
The tracking issue for this feature is: [#99842]
[#99842]: https://github.com/rust-lang/rust/issues/99842
@@ -6281,6 +8080,8 @@ The tracking issue for this feature is: [#99842]
label: "float_minimum_maximum",
description: r##"# `float_minimum_maximum`
+
+
The tracking issue for this feature is: [#91079]
[#91079]: https://github.com/rust-lang/rust/issues/91079
@@ -6292,12 +8093,26 @@ The tracking issue for this feature is: [#91079]
deny_since: None,
},
Lint {
- label: "float_next_up_down",
- description: r##"# `float_next_up_down`
+ label: "flt2dec",
+ description: r##"# `flt2dec`
+
+This feature is internal to the Rust compiler and is not intended for general use.
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "fma4_target_feature",
+ description: r##"# `fma4_target_feature`
+
+fma4 target feature on x86.
-The tracking issue for this feature is: [#91399]
+The tracking issue for this feature is: [#155233]
-[#91399]: https://github.com/rust-lang/rust/issues/91399
+[#155233]: https://github.com/rust-lang/rust/issues/155233
------------------------
"##,
@@ -6306,10 +8121,14 @@ The tracking issue for this feature is: [#91399]
deny_since: None,
},
Lint {
- label: "flt2dec",
- description: r##"# `flt2dec`
+ label: "fmt_arguments_from_str",
+ description: r##"# `fmt_arguments_from_str`
-This feature is internal to the Rust compiler and is not intended for general use.
+
+
+The tracking issue for this feature is: [#148905]
+
+[#148905]: https://github.com/rust-lang/rust/issues/148905
------------------------
"##,
@@ -6321,6 +8140,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "fmt_debug",
description: r##"# `fmt_debug`
+Controlling the behavior of fmt::Debug
+
The tracking issue for this feature is: [#129709]
[#129709]: https://github.com/rust-lang/rust/issues/129709
@@ -6335,6 +8156,8 @@ The tracking issue for this feature is: [#129709]
label: "fmt_helpers_for_derive",
description: r##"# `fmt_helpers_for_derive`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6359,6 +8182,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "fn_align",
description: r##"# `fn_align`
+Allows using `#[align(...)]` on function items
+
The tracking issue for this feature is: [#82232]
[#82232]: https://github.com/rust-lang/rust/issues/82232
@@ -6373,6 +8198,8 @@ The tracking issue for this feature is: [#82232]
label: "fn_delegation",
description: r##"# `fn_delegation`
+Support delegating implementation of functions to other already implemented functions.
+
The tracking issue for this feature is: [#118212]
[#118212]: https://github.com/rust-lang/rust/issues/118212
@@ -6387,6 +8214,8 @@ The tracking issue for this feature is: [#118212]
label: "fn_ptr_trait",
description: r##"# `fn_ptr_trait`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6441,6 +8270,8 @@ fn main() {
label: "forget_unsized",
description: r##"# `forget_unsized`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6453,6 +8284,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "format_args_nl",
description: r##"# `format_args_nl`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6465,6 +8298,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "formatting_options",
description: r##"# `formatting_options`
+
+
The tracking issue for this feature is: [#118117]
[#118117]: https://github.com/rust-lang/rust/issues/118117
@@ -6479,6 +8314,8 @@ The tracking issue for this feature is: [#118117]
label: "freeze",
description: r##"# `freeze`
+
+
The tracking issue for this feature is: [#121675]
[#121675]: https://github.com/rust-lang/rust/issues/121675
@@ -6493,6 +8330,8 @@ The tracking issue for this feature is: [#121675]
label: "freeze_impls",
description: r##"# `freeze_impls`
+Allows impls for the Freeze trait.
+
The tracking issue for this feature is: [#121675]
[#121675]: https://github.com/rust-lang/rust/issues/121675
@@ -6504,9 +8343,59 @@ The tracking issue for this feature is: [#121675]
deny_since: None,
},
Lint {
+ label: "frontmatter",
+ description: r##"# `frontmatter`
+
+The tracking issue for this feature is: [#136889]
+
+------
+
+The `frontmatter` feature allows an extra metadata block at the top of files for consumption by
+external tools. For example, it can be used by [`cargo-script`] files to specify dependencies.
+
+```rust
+#!/usr/bin/env -S cargo -Zscript
+---
+[dependencies]
+libc = "0.2.172"
+---
+#![feature(frontmatter)]
+# mod libc { pub type c_int = i32; }
+
+fn main() {
+ let x: libc::c_int = 1i32;
+}
+```
+
+[#136889]: https://github.com/rust-lang/rust/issues/136889
+[`cargo-script`]: https://rust-lang.github.io/rfcs/3502-cargo-script.html
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "fs_set_times",
+ description: r##"# `fs_set_times`
+
+
+
+The tracking issue for this feature is: [#147455]
+
+[#147455]: https://github.com/rust-lang/rust/issues/147455
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "fundamental",
description: r##"# `fundamental`
+Allows using the `#[fundamental]` attribute.
+
The tracking issue for this feature is: [#29635]
[#29635]: https://github.com/rust-lang/rust/issues/29635
@@ -6518,9 +8407,27 @@ The tracking issue for this feature is: [#29635]
deny_since: None,
},
Lint {
+ label: "funnel_shifts",
+ description: r##"# `funnel_shifts`
+
+
+
+The tracking issue for this feature is: [#145686]
+
+[#145686]: https://github.com/rust-lang/rust/issues/145686
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "future_join",
description: r##"# `future_join`
+
+
The tracking issue for this feature is: [#91642]
[#91642]: https://github.com/rust-lang/rust/issues/91642
@@ -6535,6 +8442,8 @@ The tracking issue for this feature is: [#91642]
label: "gen_blocks",
description: r##"# `gen_blocks`
+Allows defining gen blocks and `gen fn`.
+
The tracking issue for this feature is: [#117078]
[#117078]: https://github.com/rust-lang/rust/issues/117078
@@ -6549,9 +8458,9 @@ The tracking issue for this feature is: [#117078]
label: "gen_future",
description: r##"# `gen_future`
-The tracking issue for this feature is: [#50547]
-[#50547]: https://github.com/rust-lang/rust/issues/50547
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -6560,12 +8469,12 @@ The tracking issue for this feature is: [#50547]
deny_since: None,
},
Lint {
- label: "generic_arg_infer",
- description: r##"# `generic_arg_infer`
+ label: "generic_assert",
+ description: r##"# `generic_assert`
-The tracking issue for this feature is: [#85077]
+Outputs useful `assert!` messages
-[#85077]: https://github.com/rust-lang/rust/issues/85077
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -6574,10 +8483,14 @@ The tracking issue for this feature is: [#85077]
deny_since: None,
},
Lint {
- label: "generic_assert",
- description: r##"# `generic_assert`
+ label: "generic_assert_internals",
+ description: r##"# `generic_assert_internals`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#44838]
+
+[#44838]: https://github.com/rust-lang/rust/issues/44838
------------------------
"##,
@@ -6586,12 +8499,30 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "generic_assert_internals",
- description: r##"# `generic_assert_internals`
+ label: "generic_atomic",
+ description: r##"# `generic_atomic`
-The tracking issue for this feature is: [#44838]
-[#44838]: https://github.com/rust-lang/rust/issues/44838
+
+The tracking issue for this feature is: [#130539]
+
+[#130539]: https://github.com/rust-lang/rust/issues/130539
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "generic_const_args",
+ description: r##"# `generic_const_args`
+
+Allows using generics in more complex const expressions, based on definitional equality.
+
+The tracking issue for this feature is: [#151972]
+
+[#151972]: https://github.com/rust-lang/rust/issues/151972
------------------------
"##,
@@ -6603,6 +8534,8 @@ The tracking issue for this feature is: [#44838]
label: "generic_const_exprs",
description: r##"# `generic_const_exprs`
+Allows non-trivial generic constants which have to have wfness manually propagated to callers
+
The tracking issue for this feature is: [#76560]
[#76560]: https://github.com/rust-lang/rust/issues/76560
@@ -6617,6 +8550,8 @@ The tracking issue for this feature is: [#76560]
label: "generic_const_items",
description: r##"# `generic_const_items`
+Allows generic parameters and where-clauses on free & associated const items.
+
The tracking issue for this feature is: [#113521]
[#113521]: https://github.com/rust-lang/rust/issues/113521
@@ -6628,12 +8563,30 @@ The tracking issue for this feature is: [#113521]
deny_since: None,
},
Lint {
- label: "get_many_mut",
- description: r##"# `get_many_mut`
+ label: "generic_const_parameter_types",
+ description: r##"# `generic_const_parameter_types`
-The tracking issue for this feature is: [#104642]
+Allows the type of const generics to depend on generic parameters
-[#104642]: https://github.com/rust-lang/rust/issues/104642
+The tracking issue for this feature is: [#137626]
+
+[#137626]: https://github.com/rust-lang/rust/issues/137626
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "generic_pattern_types",
+ description: r##"# `generic_pattern_types`
+
+Allows any generic constants being used as pattern type range ends
+
+The tracking issue for this feature is: [#136574]
+
+[#136574]: https://github.com/rust-lang/rust/issues/136574
------------------------
"##,
@@ -6642,8 +8595,10 @@ The tracking issue for this feature is: [#104642]
deny_since: None,
},
Lint {
- label: "get_many_mut_helpers",
- description: r##"# `get_many_mut_helpers`
+ label: "get_disjoint_mut_helpers",
+ description: r##"# `get_disjoint_mut_helpers`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -6657,6 +8612,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "get_mut_unchecked",
description: r##"# `get_mut_unchecked`
+
+
The tracking issue for this feature is: [#63292]
[#63292]: https://github.com/rust-lang/rust/issues/63292
@@ -6668,9 +8625,27 @@ The tracking issue for this feature is: [#63292]
deny_since: None,
},
Lint {
+ label: "gethostname",
+ description: r##"# `gethostname`
+
+
+
+The tracking issue for this feature is: [#135142]
+
+[#135142]: https://github.com/rust-lang/rust/issues/135142
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "global_registration",
description: r##"# `global_registration`
+Allows registering static items globally, possibly across crates, to iterate over at runtime.
+
The tracking issue for this feature is: [#125119]
[#125119]: https://github.com/rust-lang/rust/issues/125119
@@ -6682,9 +8657,25 @@ The tracking issue for this feature is: [#125119]
deny_since: None,
},
Lint {
+ label: "gpu_intrinsics",
+ description: r##"# `gpu_intrinsics`
+
+
+
+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: "guard_patterns",
description: r##"# `guard_patterns`
+Allows using guards in patterns.
+
The tracking issue for this feature is: [#129967]
[#129967]: https://github.com/rust-lang/rust/issues/129967
@@ -6732,12 +8723,12 @@ fn main() {
deny_since: None,
},
Lint {
- label: "hash_extract_if",
- description: r##"# `hash_extract_if`
+ label: "hash_map_internals",
+ description: r##"# `hash_map_internals`
+
-The tracking issue for this feature is: [#59618]
-[#59618]: https://github.com/rust-lang/rust/issues/59618
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -6746,12 +8737,14 @@ The tracking issue for this feature is: [#59618]
deny_since: None,
},
Lint {
- label: "hash_raw_entry",
- description: r##"# `hash_raw_entry`
+ label: "hash_map_macro",
+ description: r##"# `hash_map_macro`
+
-The tracking issue for this feature is: [#56167]
-[#56167]: https://github.com/rust-lang/rust/issues/56167
+The tracking issue for this feature is: [#144032]
+
+[#144032]: https://github.com/rust-lang/rust/issues/144032
------------------------
"##,
@@ -6763,6 +8756,8 @@ The tracking issue for this feature is: [#56167]
label: "hash_set_entry",
description: r##"# `hash_set_entry`
+
+
The tracking issue for this feature is: [#60896]
[#60896]: https://github.com/rust-lang/rust/issues/60896
@@ -6777,6 +8772,8 @@ The tracking issue for this feature is: [#60896]
label: "hasher_prefixfree_extras",
description: r##"# `hasher_prefixfree_extras`
+
+
The tracking issue for this feature is: [#96762]
[#96762]: https://github.com/rust-lang/rust/issues/96762
@@ -6791,6 +8788,8 @@ The tracking issue for this feature is: [#96762]
label: "hashmap_internals",
description: r##"# `hashmap_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -6803,9 +8802,11 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "hexagon_target_feature",
description: r##"# `hexagon_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on hexagon.
+
+The tracking issue for this feature is: [#150250]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150250]: https://github.com/rust-lang/rust/issues/150250
------------------------
"##,
@@ -6817,6 +8818,8 @@ The tracking issue for this feature is: [#44839]
label: "hint_must_use",
description: r##"# `hint_must_use`
+
+
The tracking issue for this feature is: [#94745]
[#94745]: https://github.com/rust-lang/rust/issues/94745
@@ -6828,12 +8831,30 @@ The tracking issue for this feature is: [#94745]
deny_since: None,
},
Lint {
- label: "if_let_guard",
- description: r##"# `if_let_guard`
+ label: "hint_prefetch",
+ description: r##"# `hint_prefetch`
+
+
+
+The tracking issue for this feature is: [#146941]
+
+[#146941]: https://github.com/rust-lang/rust/issues/146941
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "impl_restriction",
+ description: r##"# `impl_restriction`
-The tracking issue for this feature is: [#51114]
+Allows `impl(crate) trait Foo` restrictions.
-[#51114]: https://github.com/rust-lang/rust/issues/51114
+The tracking issue for this feature is: [#105077]
+
+[#105077]: https://github.com/rust-lang/rust/issues/105077
------------------------
"##,
@@ -6845,6 +8866,8 @@ The tracking issue for this feature is: [#51114]
label: "impl_trait_in_assoc_type",
description: r##"# `impl_trait_in_assoc_type`
+Allows `impl Trait` to be used inside associated types (RFC 2515).
+
The tracking issue for this feature is: [#63063]
[#63063]: https://github.com/rust-lang/rust/issues/63063
@@ -6856,9 +8879,27 @@ The tracking issue for this feature is: [#63063]
deny_since: None,
},
Lint {
+ label: "impl_trait_in_bindings",
+ description: r##"# `impl_trait_in_bindings`
+
+Allows `impl Trait` in bindings (`let`).
+
+The tracking issue for this feature is: [#63065]
+
+[#63065]: https://github.com/rust-lang/rust/issues/63065
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "impl_trait_in_fn_trait_return",
description: r##"# `impl_trait_in_fn_trait_return`
+Allows `impl Trait` as output type in `Fn` traits in return position of functions.
+
The tracking issue for this feature is: [#99697]
[#99697]: https://github.com/rust-lang/rust/issues/99697
@@ -6870,9 +8911,40 @@ The tracking issue for this feature is: [#99697]
deny_since: None,
},
Lint {
+ label: "import_trait_associated_functions",
+ description: r##"# import_trait_associated_functions
+
+The tracking issue for this feature is: [#134691]
+
+[#134691]: https://github.com/rust-lang/rust/issues/134691
+
+------------------------
+
+This feature allows importing associated functions and constants from traits and then using them like regular items.
+
+```rust
+#![feature(import_trait_associated_functions)]
+
+use std::ops::Add::add;
+
+fn main() {
+ let numbers = vec![1, 2, 3, 4, 5, 6];
+ let sum = numbers.into_iter().reduce(add); // instead of `.reduce(Add:add)`
+
+ assert_eq!(sum, Some(21));
+}
+```
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "inherent_associated_types",
description: r##"# `inherent_associated_types`
+Allows associated types in inherent impls.
+
The tracking issue for this feature is: [#8995]
[#8995]: https://github.com/rust-lang/rust/issues/8995
@@ -6884,39 +8956,44 @@ The tracking issue for this feature is: [#8995]
deny_since: None,
},
Lint {
- label: "inline_const_pat",
- description: r##"# `inline_const_pat`
+ label: "inplace_iteration",
+ description: r##"# `inplace_iteration`
-The tracking issue for this feature is: [#76001]
-------
-This feature allows you to use inline constant expressions in pattern position:
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
-```rust
-#![feature(inline_const_pat)]
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "int_format_into",
+ description: r##"# `int_format_into`
-const fn one() -> i32 { 1 }
-let some_int = 3;
-match some_int {
- const { 1 + 2 } => println!("Matched 1 + 2"),
- const { one() } => println!("Matched const fn returning 1"),
- _ => println!("Didn't match anything :("),
-}
-```
-[#76001]: https://github.com/rust-lang/rust/issues/76001
+The tracking issue for this feature is: [#138215]
+
+[#138215]: https://github.com/rust-lang/rust/issues/138215
+
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "inplace_iteration",
- description: r##"# `inplace_iteration`
+ label: "int_from_ascii",
+ description: r##"# `int_from_ascii`
+
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+The tracking issue for this feature is: [#134821]
+
+[#134821]: https://github.com/rust-lang/rust/issues/134821
------------------------
"##,
@@ -6928,6 +9005,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "int_roundings",
description: r##"# `int_roundings`
+
+
The tracking issue for this feature is: [#88581]
[#88581]: https://github.com/rust-lang/rust/issues/88581
@@ -6942,6 +9021,8 @@ The tracking issue for this feature is: [#88581]
label: "integer_atomics",
description: r##"# `integer_atomics`
+
+
The tracking issue for this feature is: [#99069]
[#99069]: https://github.com/rust-lang/rust/issues/99069
@@ -6953,24 +9034,14 @@ The tracking issue for this feature is: [#99069]
deny_since: None,
},
Lint {
- label: "integer_sign_cast",
- description: r##"# `integer_sign_cast`
+ label: "integer_extend_truncate",
+ description: r##"# `integer_extend_truncate`
-The tracking issue for this feature is: [#125882]
-[#125882]: https://github.com/rust-lang/rust/issues/125882
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "internal_impls_macro",
- description: r##"# `internal_impls_macro`
+The tracking issue for this feature is: [#154330]
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+[#154330]: https://github.com/rust-lang/rust/issues/154330
------------------------
"##,
@@ -7068,9 +9139,9 @@ with any regular function.
Various intrinsics have native MIR operations that they correspond to. Instead of requiring
backends to implement both the intrinsic and the MIR operation, the `lower_intrinsics` pass
will convert the calls to the MIR operation. Backends do not need to know about these intrinsics
-at all. These intrinsics only make sense without a body, and can either be declared as a "rust-intrinsic"
-or as a `#[rustc_intrinsic]`. The body is never used, as calls to the intrinsic do not exist
-anymore after MIR analyses.
+at all. These intrinsics only make sense without a body, and can be declared as a `#[rustc_intrinsic]`.
+The body is never used as the lowering pass implements support for all backends, so we never have to
+use the fallback logic.
## Intrinsics without fallback logic
@@ -7078,33 +9149,14 @@ These must be implemented by all backends.
### `#[rustc_intrinsic]` declarations
-These are written like intrinsics with fallback bodies, but the body is irrelevant.
-Use `loop {}` for the body or call the intrinsic recursively and add
-`#[rustc_intrinsic_must_be_overridden]` to the function to ensure that backends don't
-invoke the body.
-
-### Legacy extern ABI based intrinsics
-
-These are imported as if they were FFI functions, with the special
-`rust-intrinsic` ABI. For example, if one was in a freestanding
-context, but wished to be able to `transmute` between types, and
-perform efficient pointer arithmetic, one would import those functions
-via a declaration like
-
+These are written without a body:
```rust
#![feature(intrinsics)]
#![allow(internal_features)]
-# fn main() {}
-
-extern "rust-intrinsic" {
- fn transmute<T, U>(x: T) -> U;
- fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
-}
+#[rustc_intrinsic]
+pub fn abort() -> !;
```
-
-As with any other FFI functions, these are by default always `unsafe` to call.
-You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call.
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -7114,6 +9166,8 @@ You can add `#[rustc_safe_intrinsic]` to the intrinsic to make it safe to call.
label: "io_const_error",
description: r##"# `io_const_error`
+
+
The tracking issue for this feature is: [#133448]
[#133448]: https://github.com/rust-lang/rust/issues/133448
@@ -7128,6 +9182,8 @@ The tracking issue for this feature is: [#133448]
label: "io_const_error_internals",
description: r##"# `io_const_error_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -7140,6 +9196,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "io_error_inprogress",
description: r##"# `io_error_inprogress`
+
+
The tracking issue for this feature is: [#130840]
[#130840]: https://github.com/rust-lang/rust/issues/130840
@@ -7154,6 +9212,8 @@ The tracking issue for this feature is: [#130840]
label: "io_error_more",
description: r##"# `io_error_more`
+
+
The tracking issue for this feature is: [#86442]
[#86442]: https://github.com/rust-lang/rust/issues/86442
@@ -7168,6 +9228,8 @@ The tracking issue for this feature is: [#86442]
label: "io_error_uncategorized",
description: r##"# `io_error_uncategorized`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -7180,6 +9242,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "io_slice_as_bytes",
description: r##"# `io_slice_as_bytes`
+
+
The tracking issue for this feature is: [#132818]
[#132818]: https://github.com/rust-lang/rust/issues/132818
@@ -7194,6 +9258,8 @@ The tracking issue for this feature is: [#132818]
label: "ip",
description: r##"# `ip`
+
+
The tracking issue for this feature is: [#27709]
[#27709]: https://github.com/rust-lang/rust/issues/27709
@@ -7205,12 +9271,28 @@ The tracking issue for this feature is: [#27709]
deny_since: None,
},
Lint {
- label: "ip_from",
- description: r##"# `ip_from`
+ label: "ip_as_octets",
+ description: r##"# `ip_as_octets`
+
+
-The tracking issue for this feature is: [#131360]
+The tracking issue for this feature is: [#137259]
-[#131360]: https://github.com/rust-lang/rust/issues/131360
+[#137259]: https://github.com/rust-lang/rust/issues/137259
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "ip_multicast_reserved",
+ description: r##"# `ip_multicast_reserved`
+
+
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -7222,6 +9304,8 @@ The tracking issue for this feature is: [#131360]
label: "is_ascii_octdigit",
description: r##"# `is_ascii_octdigit`
+
+
The tracking issue for this feature is: [#101288]
[#101288]: https://github.com/rust-lang/rust/issues/101288
@@ -7236,6 +9320,8 @@ The tracking issue for this feature is: [#101288]
label: "is_loongarch_feature_detected",
description: r##"# `is_loongarch_feature_detected`
+
+
The tracking issue for this feature is: [#117425]
[#117425]: https://github.com/rust-lang/rust/issues/117425
@@ -7250,6 +9336,8 @@ The tracking issue for this feature is: [#117425]
label: "is_riscv_feature_detected",
description: r##"# `is_riscv_feature_detected`
+
+
The tracking issue for this feature is: [#111192]
[#111192]: https://github.com/rust-lang/rust/issues/111192
@@ -7264,6 +9352,8 @@ The tracking issue for this feature is: [#111192]
label: "iter_advance_by",
description: r##"# `iter_advance_by`
+
+
The tracking issue for this feature is: [#77404]
[#77404]: https://github.com/rust-lang/rust/issues/77404
@@ -7278,23 +9368,11 @@ The tracking issue for this feature is: [#77404]
label: "iter_array_chunks",
description: r##"# `iter_array_chunks`
-The tracking issue for this feature is: [#100450]
-
-[#100450]: https://github.com/rust-lang/rust/issues/100450
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "iter_chain",
- description: r##"# `iter_chain`
-The tracking issue for this feature is: [#125964]
+The tracking issue for this feature is: [#100450]
-[#125964]: https://github.com/rust-lang/rust/issues/125964
+[#100450]: https://github.com/rust-lang/rust/issues/100450
------------------------
"##,
@@ -7306,6 +9384,8 @@ The tracking issue for this feature is: [#125964]
label: "iter_collect_into",
description: r##"# `iter_collect_into`
+
+
The tracking issue for this feature is: [#94780]
[#94780]: https://github.com/rust-lang/rust/issues/94780
@@ -7320,6 +9400,8 @@ The tracking issue for this feature is: [#94780]
label: "iter_from_coroutine",
description: r##"# `iter_from_coroutine`
+
+
The tracking issue for this feature is: [#43122]
[#43122]: https://github.com/rust-lang/rust/issues/43122
@@ -7334,6 +9416,8 @@ The tracking issue for this feature is: [#43122]
label: "iter_intersperse",
description: r##"# `iter_intersperse`
+
+
The tracking issue for this feature is: [#79524]
[#79524]: https://github.com/rust-lang/rust/issues/79524
@@ -7348,6 +9432,8 @@ The tracking issue for this feature is: [#79524]
label: "iter_is_partitioned",
description: r##"# `iter_is_partitioned`
+
+
The tracking issue for this feature is: [#62544]
[#62544]: https://github.com/rust-lang/rust/issues/62544
@@ -7359,9 +9445,27 @@ The tracking issue for this feature is: [#62544]
deny_since: None,
},
Lint {
+ label: "iter_macro",
+ description: r##"# `iter_macro`
+
+
+
+The tracking issue for this feature is: [#142269]
+
+[#142269]: https://github.com/rust-lang/rust/issues/142269
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "iter_map_windows",
description: r##"# `iter_map_windows`
+
+
The tracking issue for this feature is: [#87155]
[#87155]: https://github.com/rust-lang/rust/issues/87155
@@ -7376,6 +9480,8 @@ The tracking issue for this feature is: [#87155]
label: "iter_next_chunk",
description: r##"# `iter_next_chunk`
+
+
The tracking issue for this feature is: [#98326]
[#98326]: https://github.com/rust-lang/rust/issues/98326
@@ -7390,6 +9496,8 @@ The tracking issue for this feature is: [#98326]
label: "iter_order_by",
description: r##"# `iter_order_by`
+
+
The tracking issue for this feature is: [#64295]
[#64295]: https://github.com/rust-lang/rust/issues/64295
@@ -7404,6 +9512,8 @@ The tracking issue for this feature is: [#64295]
label: "iter_partition_in_place",
description: r##"# `iter_partition_in_place`
+
+
The tracking issue for this feature is: [#62543]
[#62543]: https://github.com/rust-lang/rust/issues/62543
@@ -7418,6 +9528,8 @@ The tracking issue for this feature is: [#62543]
label: "iterator_try_collect",
description: r##"# `iterator_try_collect`
+
+
The tracking issue for this feature is: [#94047]
[#94047]: https://github.com/rust-lang/rust/issues/94047
@@ -7432,6 +9544,8 @@ The tracking issue for this feature is: [#94047]
label: "iterator_try_reduce",
description: r##"# `iterator_try_reduce`
+
+
The tracking issue for this feature is: [#87053]
[#87053]: https://github.com/rust-lang/rust/issues/87053
@@ -7446,6 +9560,8 @@ The tracking issue for this feature is: [#87053]
label: "junction_point",
description: r##"# `junction_point`
+
+
The tracking issue for this feature is: [#121709]
[#121709]: https://github.com/rust-lang/rust/issues/121709
@@ -7460,9 +9576,11 @@ The tracking issue for this feature is: [#121709]
label: "lahfsahf_target_feature",
description: r##"# `lahfsahf_target_feature`
-The tracking issue for this feature is: [#44839]
+lahfsahf target feature on x86.
+
+The tracking issue for this feature is: [#150251]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150251]: https://github.com/rust-lang/rust/issues/150251
------------------------
"##,
@@ -7520,14 +9638,15 @@ allocation. A freestanding program that uses the `Box` sugar for dynamic
allocations via `malloc` and `free`:
```rust,ignore (libc-is-finicky)
-#![feature(lang_items, start, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)]
+#![feature(lang_items, core_intrinsics, rustc_private, panic_unwind, rustc_attrs)]
#![allow(internal_features)]
#![no_std]
+#![no_main]
extern crate libc;
extern crate unwind;
-use core::ffi::c_void;
+use core::ffi::{c_int, c_void};
use core::intrinsics;
use core::panic::PanicInfo;
use core::ptr::NonNull;
@@ -7565,8 +9684,8 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
p
}
-#[start]
-fn main(_argc: isize, _argv: *const *const u8) -> isize {
+#[no_mangle]
+extern "C" fn main(_argc: c_int, _argv: *const *const u8) -> c_int {
let _x = Box::new(1);
0
@@ -7596,6 +9715,8 @@ An up-to-date list of all language items can be found [here] in the compiler cod
label: "large_assignments",
description: r##"# `large_assignments`
+Allows setting the threshold for the `large_assignments` lint.
+
The tracking issue for this feature is: [#83518]
[#83518]: https://github.com/rust-lang/rust/issues/83518
@@ -7610,6 +9731,8 @@ The tracking issue for this feature is: [#83518]
label: "layout_for_ptr",
description: r##"# `layout_for_ptr`
+
+
The tracking issue for this feature is: [#69835]
[#69835]: https://github.com/rust-lang/rust/issues/69835
@@ -7624,23 +9747,11 @@ The tracking issue for this feature is: [#69835]
label: "lazy_cell_into_inner",
description: r##"# `lazy_cell_into_inner`
-The tracking issue for this feature is: [#125623]
-
-[#125623]: https://github.com/rust-lang/rust/issues/125623
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "lazy_get",
- description: r##"# `lazy_get`
-The tracking issue for this feature is: [#129333]
+The tracking issue for this feature is: [#125623]
-[#129333]: https://github.com/rust-lang/rust/issues/129333
+[#125623]: https://github.com/rust-lang/rust/issues/125623
------------------------
"##,
@@ -7652,6 +9763,8 @@ The tracking issue for this feature is: [#129333]
label: "lazy_type_alias",
description: r##"# `lazy_type_alias`
+Allow to have type alias types for inter-crate use.
+
The tracking issue for this feature is: [#112792]
[#112792]: https://github.com/rust-lang/rust/issues/112792
@@ -7666,21 +9779,9 @@ The tracking issue for this feature is: [#112792]
label: "legacy_receiver_trait",
description: r##"# `legacy_receiver_trait`
-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: "let_chains",
- description: r##"# `let_chains`
-The tracking issue for this feature is: [#53667]
-[#53667]: https://github.com/rust-lang/rust/issues/53667
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -7692,6 +9793,8 @@ The tracking issue for this feature is: [#53667]
label: "liballoc_internals",
description: r##"# `liballoc_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -7713,10 +9816,14 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
- label: "lifetime_capture_rules_2024",
- description: r##"# `lifetime_capture_rules_2024`
+ label: "likely_unlikely",
+ description: r##"# `likely_unlikely`
+
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+The tracking issue for this feature is: [#151619]
+
+[#151619]: https://github.com/rust-lang/rust/issues/151619
------------------------
"##,
@@ -7768,6 +9875,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "link_llvm_intrinsics",
description: r##"# `link_llvm_intrinsics`
+Allows using `#[link_name="llvm.*"]`.
+
The tracking issue for this feature is: [#29602]
[#29602]: https://github.com/rust-lang/rust/issues/29602
@@ -7782,6 +9891,8 @@ The tracking issue for this feature is: [#29602]
label: "linkage",
description: r##"# `linkage`
+Allows using the `#[linkage = ".."]` attribute.
+
The tracking issue for this feature is: [#29603]
[#29603]: https://github.com/rust-lang/rust/issues/29603
@@ -7796,6 +9907,8 @@ The tracking issue for this feature is: [#29603]
label: "linked_list_cursors",
description: r##"# `linked_list_cursors`
+
+
The tracking issue for this feature is: [#58533]
[#58533]: https://github.com/rust-lang/rust/issues/58533
@@ -7810,6 +9923,8 @@ The tracking issue for this feature is: [#58533]
label: "linked_list_remove",
description: r##"# `linked_list_remove`
+
+
The tracking issue for this feature is: [#69210]
[#69210]: https://github.com/rust-lang/rust/issues/69210
@@ -7824,6 +9939,8 @@ The tracking issue for this feature is: [#69210]
label: "linked_list_retain",
description: r##"# `linked_list_retain`
+
+
The tracking issue for this feature is: [#114135]
[#114135]: https://github.com/rust-lang/rust/issues/114135
@@ -7838,6 +9955,8 @@ The tracking issue for this feature is: [#114135]
label: "linux_pidfd",
description: r##"# `linux_pidfd`
+
+
The tracking issue for this feature is: [#82971]
[#82971]: https://github.com/rust-lang/rust/issues/82971
@@ -7849,9 +9968,27 @@ The tracking issue for this feature is: [#82971]
deny_since: None,
},
Lint {
+ label: "local_key_cell_update",
+ description: r##"# `local_key_cell_update`
+
+
+
+The tracking issue for this feature is: [#143989]
+
+[#143989]: https://github.com/rust-lang/rust/issues/143989
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "local_waker",
description: r##"# `local_waker`
+
+
The tracking issue for this feature is: [#118959]
[#118959]: https://github.com/rust-lang/rust/issues/118959
@@ -7863,9 +10000,27 @@ The tracking issue for this feature is: [#118959]
deny_since: None,
},
Lint {
+ label: "lock_value_accessors",
+ description: r##"# `lock_value_accessors`
+
+
+
+The tracking issue for this feature is: [#133407]
+
+[#133407]: https://github.com/rust-lang/rust/issues/133407
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "log_syntax",
description: r##"# `log_syntax`
+
+
The tracking issue for this feature is: [#29598]
[#29598]: https://github.com/rust-lang/rust/issues/29598
@@ -7880,9 +10035,11 @@ The tracking issue for this feature is: [#29598]
label: "loongarch_target_feature",
description: r##"# `loongarch_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on loongarch.
+
+The tracking issue for this feature is: [#150252]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150252]: https://github.com/rust-lang/rust/issues/150252
------------------------
"##,
@@ -7891,12 +10048,73 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
- label: "macro_metavar_expr",
- description: r##"# `macro_metavar_expr`
+ label: "loop_match",
+ description: r##"# `loop_match`
-The tracking issue for this feature is: [#83527]
+The tracking issue for this feature is: [#132306]
-[#83527]: https://github.com/rust-lang/rust/issues/83527
+[#132306]: https://github.com/rust-lang/rust/issues/132306
+
+------
+
+The `#[loop_match]` and `#[const_continue]` attributes can be used to improve the code
+generation of logic that fits this shape:
+
+```ignore (pseudo-rust)
+loop {
+ state = 'blk: {
+ match state {
+ State::A => {
+ break 'blk State::B
+ }
+ State::B => { /* ... */ }
+ /* ... */
+ }
+ }
+}
+```
+
+Here the loop itself can be annotated with `#[loop_match]`, and any `break 'blk` with
+`#[const_continue]` if the value is know at compile time:
+
+```ignore (pseudo-rust)
+#[loop_match]
+loop {
+ state = 'blk: {
+ match state {
+ State::A => {
+ #[const_continue]
+ break 'blk State::B
+ }
+ State::B => { /* ... */ }
+ /* ... */
+ }
+ }
+}
+```
+
+The observable behavior of this loop is exactly the same as without the extra attributes.
+The difference is in the generated output: normally, when the state is `A`, control flow
+moves from the `A` branch, back to the top of the loop, then to the `B` branch. With the
+attributes, The `A` branch will immediately jump to the `B` branch.
+
+Removing the indirection can be beneficial for stack usage and branch prediction, and
+enables other optimizations by clearly splitting out the control flow paths that your
+program will actually use.
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "m68k_target_feature",
+ description: r##"# `m68k_target_feature`
+
+Target features on m68k.
+
+The tracking issue for this feature is: [#134328]
+
+[#134328]: https://github.com/rust-lang/rust/issues/134328
------------------------
"##,
@@ -7905,12 +10123,14 @@ The tracking issue for this feature is: [#83527]
deny_since: None,
},
Lint {
- label: "macro_metavar_expr_concat",
- description: r##"# `macro_metavar_expr_concat`
+ label: "macro_attr",
+ description: r##"# `macro_attr`
-The tracking issue for this feature is: [#124225]
+Allow `macro_rules!` attribute rules
-[#124225]: https://github.com/rust-lang/rust/issues/124225
+The tracking issue for this feature is: [#143547]
+
+[#143547]: https://github.com/rust-lang/rust/issues/143547
------------------------
"##,
@@ -7919,12 +10139,14 @@ The tracking issue for this feature is: [#124225]
deny_since: None,
},
Lint {
- label: "map_many_mut",
- description: r##"# `map_many_mut`
+ label: "macro_derive",
+ description: r##"# `macro_derive`
+
+Allow `macro_rules!` derive rules
-The tracking issue for this feature is: [#97601]
+The tracking issue for this feature is: [#143549]
-[#97601]: https://github.com/rust-lang/rust/issues/97601
+[#143549]: https://github.com/rust-lang/rust/issues/143549
------------------------
"##,
@@ -7933,9 +10155,184 @@ The tracking issue for this feature is: [#97601]
deny_since: None,
},
Lint {
+ label: "macro_guard_matcher",
+ description: r##"# `macro_guard_matcher`
+
+Allow `$x:guard` matcher in macros
+
+The tracking issue for this feature is: [#153104]
+
+[#153104]: https://github.com/rust-lang/rust/issues/153104
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "macro_metavar_expr",
+ description: r##"# `macro_metavar_expr`
+
+The tracking issue for this feature is: [#83527]
+
+------------------------
+
+> This feature is not to be confused with [`macro_metavar_expr_concat`].
+
+[`macro_metavar_expr_concat`]: ./macro-metavar-expr-concat.md
+[#83527]: https://github.com/rust-lang/rust/issues/83527
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "macro_metavar_expr_concat",
+ description: r##"# `macro_metavar_expr_concat`
+
+The tracking issue for this feature is: [#124225]
+
+------------------------
+
+In stable Rust, there is no way to create new identifiers by joining identifiers to literals or other identifiers without using procedural macros such as [`paste`].
+ `#![feature(macro_metavar_expr_concat)]` introduces a way to do this, using the concat metavariable expression.
+
+> This feature uses the syntax from [`macro_metavar_expr`] but is otherwise
+> independent. It replaces the since-removed unstable feature
+> [`concat_idents`].
+
+> This is an experimental feature; it and its syntax will require a RFC before stabilization.
+
+
+### Overview
+
+`#![feature(macro_metavar_expr_concat)]` provides the `concat` metavariable expression for creating new identifiers:
+
+```rust
+#![feature(macro_metavar_expr_concat)]
+
+macro_rules! create_some_structs {
+ ($name:ident) => {
+ pub struct ${ concat(First, $name) };
+ pub struct ${ concat(Second, $name) };
+ pub struct ${ concat(Third, $name) };
+ }
+}
+
+create_some_structs!(Thing);
+```
+
+This macro invocation expands to:
+
+```rust
+pub struct FirstThing;
+pub struct SecondThing;
+pub struct ThirdThing;
+```
+
+### Syntax
+
+This feature builds upon the metavariable expression syntax `${ .. }` as specified in [RFC 3086] ([`macro_metavar_expr`]).
+ `concat` is available like `${ concat(items) }`, where `items` is a comma separated sequence of idents and/or literals.
+
+### Examples
+
+#### Create a function or method with a concatenated name
+
+```rust
+#![feature(macro_metavar_expr_concat)]
+
+macro_rules! make_getter {
+ ($name:ident, $field: ident, $ret:ty) => {
+ impl $name {
+ pub fn ${ concat(get_, $field) }(&self) -> &$ret {
+ &self.$field
+ }
+ }
+ }
+}
+
+pub struct Thing {
+ description: String,
+}
+
+make_getter!(Thing, description, String);
+```
+
+This expands to:
+
+```rust
+pub struct Thing {
+ description: String,
+}
+
+impl Thing {
+ pub fn get_description(&self) -> &String {
+ &self.description
+ }
+}
+```
+
+#### Create names for macro generated tests
+
+```rust
+#![feature(macro_metavar_expr_concat)]
+
+macro_rules! test_math {
+ ($integer:ident) => {
+ #[test]
+ fn ${ concat(test_, $integer, _, addition) } () {
+ let a: $integer = 73;
+ let b: $integer = 42;
+ assert_eq!(a + b, 115)
+ }
+
+ #[test]
+ fn ${ concat(test_, $integer, _, subtraction) } () {
+ let a: $integer = 73;
+ let b: $integer = 42;
+ assert_eq!(a - b, 31)
+ }
+ }
+}
+
+test_math!(i32);
+test_math!(u64);
+test_math!(u128);
+```
+
+Running this returns the following output:
+
+```text
+running 6 tests
+test test_i32_subtraction ... ok
+test test_i32_addition ... ok
+test test_u128_addition ... ok
+test test_u128_subtraction ... ok
+test test_u64_addition ... ok
+test test_u64_subtraction ... ok
+
+test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
+```
+
+[`paste`]: https://crates.io/crates/paste
+[RFC 3086]: https://rust-lang.github.io/rfcs/3086-macro-metavar-expr.html
+[`macro_metavar_expr`]: ../language-features/macro-metavar-expr.md
+[`concat_idents`]: https://github.com/rust-lang/rust/issues/29599
+[#124225]: https://github.com/rust-lang/rust/issues/124225
+[declarative macros]: https://doc.rust-lang.org/stable/reference/macros-by-example.html
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "map_try_insert",
description: r##"# `map_try_insert`
+
+
The tracking issue for this feature is: [#82766]
[#82766]: https://github.com/rust-lang/rust/issues/82766
@@ -7950,6 +10347,8 @@ The tracking issue for this feature is: [#82766]
label: "mapped_lock_guards",
description: r##"# `mapped_lock_guards`
+
+
The tracking issue for this feature is: [#117108]
[#117108]: https://github.com/rust-lang/rust/issues/117108
@@ -8003,9 +10402,27 @@ feature, which applied to all empty traits (without needing an opt-in).
deny_since: None,
},
Lint {
+ label: "maybe_dangling",
+ description: r##"# `maybe_dangling`
+
+
+
+The tracking issue for this feature is: [#118166]
+
+[#118166]: https://github.com/rust-lang/rust/issues/118166
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "maybe_uninit_array_assume_init",
description: r##"# `maybe_uninit_array_assume_init`
+
+
The tracking issue for this feature is: [#96097]
[#96097]: https://github.com/rust-lang/rust/issues/96097
@@ -8020,6 +10437,8 @@ The tracking issue for this feature is: [#96097]
label: "maybe_uninit_as_bytes",
description: r##"# `maybe_uninit_as_bytes`
+
+
The tracking issue for this feature is: [#93092]
[#93092]: https://github.com/rust-lang/rust/issues/93092
@@ -8034,6 +10453,8 @@ The tracking issue for this feature is: [#93092]
label: "maybe_uninit_fill",
description: r##"# `maybe_uninit_fill`
+
+
The tracking issue for this feature is: [#117428]
[#117428]: https://github.com/rust-lang/rust/issues/117428
@@ -8045,12 +10466,14 @@ The tracking issue for this feature is: [#117428]
deny_since: None,
},
Lint {
- label: "maybe_uninit_slice",
- description: r##"# `maybe_uninit_slice`
+ label: "maybe_uninit_uninit_array_transpose",
+ description: r##"# `maybe_uninit_uninit_array_transpose`
-The tracking issue for this feature is: [#63569]
-[#63569]: https://github.com/rust-lang/rust/issues/63569
+
+The tracking issue for this feature is: [#96097]
+
+[#96097]: https://github.com/rust-lang/rust/issues/96097
------------------------
"##,
@@ -8059,12 +10482,14 @@ The tracking issue for this feature is: [#63569]
deny_since: None,
},
Lint {
- label: "maybe_uninit_uninit_array",
- description: r##"# `maybe_uninit_uninit_array`
+ label: "mem_conjure_zst",
+ description: r##"# `mem_conjure_zst`
-The tracking issue for this feature is: [#96097]
-[#96097]: https://github.com/rust-lang/rust/issues/96097
+
+The tracking issue for this feature is: [#95383]
+
+[#95383]: https://github.com/rust-lang/rust/issues/95383
------------------------
"##,
@@ -8073,12 +10498,14 @@ The tracking issue for this feature is: [#96097]
deny_since: None,
},
Lint {
- label: "maybe_uninit_uninit_array_transpose",
- description: r##"# `maybe_uninit_uninit_array_transpose`
+ label: "mem_copy_fn",
+ description: r##"# `mem_copy_fn`
-The tracking issue for this feature is: [#96097]
-[#96097]: https://github.com/rust-lang/rust/issues/96097
+
+The tracking issue for this feature is: [#98262]
+
+[#98262]: https://github.com/rust-lang/rust/issues/98262
------------------------
"##,
@@ -8087,12 +10514,14 @@ The tracking issue for this feature is: [#96097]
deny_since: None,
},
Lint {
- label: "maybe_uninit_write_slice",
- description: r##"# `maybe_uninit_write_slice`
+ label: "mgca_type_const_syntax",
+ description: r##"# `mgca_type_const_syntax`
-The tracking issue for this feature is: [#79995]
+Enable mgca `type const` syntax before expansion.
+
+The tracking issue for this feature is: [#132980]
-[#79995]: https://github.com/rust-lang/rust/issues/79995
+[#132980]: https://github.com/rust-lang/rust/issues/132980
------------------------
"##,
@@ -8101,12 +10530,14 @@ The tracking issue for this feature is: [#79995]
deny_since: None,
},
Lint {
- label: "mem_copy_fn",
- description: r##"# `mem_copy_fn`
+ label: "min_adt_const_params",
+ description: r##"# `min_adt_const_params`
-The tracking issue for this feature is: [#98262]
+Allows additional const parameter types, such as [u8; 10] or user defined types. User defined types must not have fields more private than the type itself.
-[#98262]: https://github.com/rust-lang/rust/issues/98262
+The tracking issue for this feature is: [#154042]
+
+[#154042]: https://github.com/rust-lang/rust/issues/154042
------------------------
"##,
@@ -8118,6 +10549,8 @@ The tracking issue for this feature is: [#98262]
label: "min_generic_const_args",
description: r##"# `min_generic_const_args`
+Enables the generic const args MVP (only bare paths, not arbitrary computation).
+
The tracking issue for this feature is: [#132980]
[#132980]: https://github.com/rust-lang/rust/issues/132980
@@ -8132,6 +10565,8 @@ The tracking issue for this feature is: [#132980]
label: "min_specialization",
description: r##"# `min_specialization`
+A minimal, sound subset of specialization intended to be used by the standard library until the soundness issues with specialization are fixed.
+
The tracking issue for this feature is: [#31844]
[#31844]: https://github.com/rust-lang/rust/issues/31844
@@ -8146,23 +10581,11 @@ The tracking issue for this feature is: [#31844]
label: "mips_target_feature",
description: r##"# `mips_target_feature`
-The tracking issue for this feature is: [#44839]
-
-[#44839]: https://github.com/rust-lang/rust/issues/44839
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "mixed_integer_ops_unsigned_sub",
- description: r##"# `mixed_integer_ops_unsigned_sub`
+Target features on mips.
-The tracking issue for this feature is: [#126043]
+The tracking issue for this feature is: [#150253]
-[#126043]: https://github.com/rust-lang/rust/issues/126043
+[#150253]: https://github.com/rust-lang/rust/issues/150253
------------------------
"##,
@@ -8174,9 +10597,11 @@ The tracking issue for this feature is: [#126043]
label: "more_float_constants",
description: r##"# `more_float_constants`
-The tracking issue for this feature is: [#103883]
-[#103883]: https://github.com/rust-lang/rust/issues/103883
+
+The tracking issue for this feature is: [#146939]
+
+[#146939]: https://github.com/rust-lang/rust/issues/146939
------------------------
"##,
@@ -8188,6 +10613,8 @@ The tracking issue for this feature is: [#103883]
label: "more_maybe_bounds",
description: r##"# `more_maybe_bounds`
+Allows using `?Trait` trait bounds in more contexts.
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -8237,9 +10664,43 @@ impl A for Foo {
deny_since: None,
},
Lint {
+ label: "motor_ext",
+ description: r##"# `motor_ext`
+
+
+
+The tracking issue for this feature is: [#147456]
+
+[#147456]: https://github.com/rust-lang/rust/issues/147456
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "movrs_target_feature",
+ description: r##"# `movrs_target_feature`
+
+The `movrs` target feature on x86.
+
+The tracking issue for this feature is: [#137976]
+
+[#137976]: https://github.com/rust-lang/rust/issues/137976
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "mpmc_channel",
description: r##"# `mpmc_channel`
+
+
The tracking issue for this feature is: [#126840]
[#126840]: https://github.com/rust-lang/rust/issues/126840
@@ -8251,10 +10712,30 @@ The tracking issue for this feature is: [#126840]
deny_since: None,
},
Lint {
+ label: "mpsc_is_disconnected",
+ description: r##"# `mpsc_is_disconnected`
+
+
+
+The tracking issue for this feature is: [#153668]
+
+[#153668]: https://github.com/rust-lang/rust/issues/153668
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "multiple_supertrait_upcastable",
description: r##"# `multiple_supertrait_upcastable`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+Allows the `multiple_supertrait_upcastable` lint.
+
+The tracking issue for this feature is: [#150833]
+
+[#150833]: https://github.com/rust-lang/rust/issues/150833
------------------------
"##,
@@ -8266,6 +10747,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "must_not_suspend",
description: r##"# `must_not_suspend`
+Allows the `#[must_not_suspend]` attribute.
+
The tracking issue for this feature is: [#83310]
[#83310]: https://github.com/rust-lang/rust/issues/83310
@@ -8280,6 +10763,8 @@ The tracking issue for this feature is: [#83310]
label: "mut_ref",
description: r##"# `mut_ref`
+Allows `mut ref` and `mut ref mut` identifier patterns.
+
The tracking issue for this feature is: [#123076]
[#123076]: https://github.com/rust-lang/rust/issues/123076
@@ -8291,12 +10776,46 @@ The tracking issue for this feature is: [#123076]
deny_since: None,
},
Lint {
- label: "naked_functions",
- description: r##"# `naked_functions`
+ label: "mutex_data_ptr",
+ description: r##"# `mutex_data_ptr`
-The tracking issue for this feature is: [#90957]
-[#90957]: https://github.com/rust-lang/rust/issues/90957
+
+The tracking issue for this feature is: [#140368]
+
+[#140368]: https://github.com/rust-lang/rust/issues/140368
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "naked_functions_rustic_abi",
+ description: r##"# `naked_functions_rustic_abi`
+
+Allows using `#[naked]` on `extern "Rust"` functions.
+
+The tracking issue for this feature is: [#138997]
+
+[#138997]: https://github.com/rust-lang/rust/issues/138997
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "naked_functions_target_feature",
+ description: r##"# `naked_functions_target_feature`
+
+Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions.
+
+The tracking issue for this feature is: [#138568]
+
+[#138568]: https://github.com/rust-lang/rust/issues/138568
------------------------
"##,
@@ -8333,6 +10852,8 @@ The default for this modifier is unclear, some targets currently specify it as `
label: "needs_panic_runtime",
description: r##"# `needs_panic_runtime`
+Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed.
+
The tracking issue for this feature is: [#32837]
[#32837]: https://github.com/rust-lang/rust/issues/32837
@@ -8347,6 +10868,8 @@ The tracking issue for this feature is: [#32837]
label: "negative_bounds",
description: r##"# `negative_bounds`
+Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -8423,6 +10946,8 @@ This serves two purposes:
label: "never_patterns",
description: r##"# `never_patterns`
+Allows the `!` pattern.
+
The tracking issue for this feature is: [#118155]
[#118155]: https://github.com/rust-lang/rust/issues/118155
@@ -8437,6 +10962,8 @@ The tracking issue for this feature is: [#118155]
label: "never_type",
description: r##"# `never_type`
+Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
+
The tracking issue for this feature is: [#35121]
[#35121]: https://github.com/rust-lang/rust/issues/35121
@@ -8448,22 +10975,26 @@ The tracking issue for this feature is: [#35121]
deny_since: None,
},
Lint {
- label: "never_type_fallback",
- description: r##"# `never_type_fallback`
+ label: "new_range",
+ description: r##"# `new_range`
-The tracking issue for this feature is: [#65992]
+The tracking issue for this feature is: [#123741]
-[#65992]: https://github.com/rust-lang/rust/issues/65992
+[#123741]: https://github.com/rust-lang/rust/issues/123741
-------------------------
+---
+
+Switch the syntaxes `a..`, `a..b`, and `a..=b` to resolve the new range types.
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "new_range_api",
- description: r##"# `new_range_api`
+ label: "new_range_api_legacy",
+ description: r##"# `new_range_api_legacy`
+
+
The tracking issue for this feature is: [#125687]
@@ -8476,12 +11007,14 @@ The tracking issue for this feature is: [#125687]
deny_since: None,
},
Lint {
- label: "new_zeroed_alloc",
- description: r##"# `new_zeroed_alloc`
+ label: "new_range_remainder",
+ description: r##"# `new_range_remainder`
-The tracking issue for this feature is: [#129396]
-[#129396]: https://github.com/rust-lang/rust/issues/129396
+
+The tracking issue for this feature is: [#154458]
+
+[#154458]: https://github.com/rust-lang/rust/issues/154458
------------------------
"##,
@@ -8490,12 +11023,14 @@ The tracking issue for this feature is: [#129396]
deny_since: None,
},
Lint {
- label: "no_core",
- description: r##"# `no_core`
+ label: "next_index",
+ description: r##"# `next_index`
-The tracking issue for this feature is: [#29639]
-[#29639]: https://github.com/rust-lang/rust/issues/29639
+
+The tracking issue for this feature is: [#130711]
+
+[#130711]: https://github.com/rust-lang/rust/issues/130711
------------------------
"##,
@@ -8504,36 +11039,16 @@ The tracking issue for this feature is: [#29639]
deny_since: None,
},
Lint {
- label: "no_sanitize",
- description: r##"# `no_sanitize`
-
-The tracking issue for this feature is: [#39699]
-
-[#39699]: https://github.com/rust-lang/rust/issues/39699
-
-------------------------
-
-The `no_sanitize` attribute can be used to selectively disable sanitizer
-instrumentation in an annotated function. This might be useful to: avoid
-instrumentation overhead in a performance critical function, or avoid
-instrumenting code that contains constructs unsupported by given sanitizer.
+ label: "no_core",
+ description: r##"# `no_core`
-The precise effect of this annotation depends on particular sanitizer in use.
-For example, with `no_sanitize(thread)`, the thread sanitizer will no longer
-instrument non-atomic store / load operations, but it will instrument atomic
-operations to avoid reporting false positives and provide meaning full stack
-traces.
+Allows `#![no_core]`.
-## Examples
+The tracking issue for this feature is: [#29639]
-``` rust
-#![feature(no_sanitize)]
+[#29639]: https://github.com/rust-lang/rust/issues/29639
-#[no_sanitize(address)]
-fn foo() {
- // ...
-}
-```
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -8543,6 +11058,8 @@ fn foo() {
label: "non_exhaustive_omitted_patterns_lint",
description: r##"# `non_exhaustive_omitted_patterns_lint`
+Allows using the `non_exhaustive_omitted_patterns` lint.
+
The tracking issue for this feature is: [#89554]
[#89554]: https://github.com/rust-lang/rust/issues/89554
@@ -8557,6 +11074,8 @@ The tracking issue for this feature is: [#89554]
label: "non_lifetime_binders",
description: r##"# `non_lifetime_binders`
+Allows `for<T>` binders in where-clauses
+
The tracking issue for this feature is: [#108185]
[#108185]: https://github.com/rust-lang/rust/issues/108185
@@ -8568,12 +11087,14 @@ The tracking issue for this feature is: [#108185]
deny_since: None,
},
Lint {
- label: "non_null_from_ref",
- description: r##"# `non_null_from_ref`
+ label: "nonpoison_condvar",
+ description: r##"# `nonpoison_condvar`
-The tracking issue for this feature is: [#130823]
-[#130823]: https://github.com/rust-lang/rust/issues/130823
+
+The tracking issue for this feature is: [#134645]
+
+[#134645]: https://github.com/rust-lang/rust/issues/134645
------------------------
"##,
@@ -8582,12 +11103,30 @@ The tracking issue for this feature is: [#130823]
deny_since: None,
},
Lint {
- label: "non_zero_count_ones",
- description: r##"# `non_zero_count_ones`
+ label: "nonpoison_mutex",
+ description: r##"# `nonpoison_mutex`
+
+
-The tracking issue for this feature is: [#120287]
+The tracking issue for this feature is: [#134645]
-[#120287]: https://github.com/rust-lang/rust/issues/120287
+[#134645]: https://github.com/rust-lang/rust/issues/134645
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "nonpoison_rwlock",
+ description: r##"# `nonpoison_rwlock`
+
+
+
+The tracking issue for this feature is: [#134645]
+
+[#134645]: https://github.com/rust-lang/rust/issues/134645
------------------------
"##,
@@ -8599,6 +11138,8 @@ The tracking issue for this feature is: [#120287]
label: "nonzero_bitwise",
description: r##"# `nonzero_bitwise`
+
+
The tracking issue for this feature is: [#128281]
[#128281]: https://github.com/rust-lang/rust/issues/128281
@@ -8613,6 +11154,8 @@ The tracking issue for this feature is: [#128281]
label: "nonzero_from_mut",
description: r##"# `nonzero_from_mut`
+
+
The tracking issue for this feature is: [#106290]
[#106290]: https://github.com/rust-lang/rust/issues/106290
@@ -8624,9 +11167,27 @@ The tracking issue for this feature is: [#106290]
deny_since: None,
},
Lint {
+ label: "nonzero_from_str_radix",
+ description: r##"# `nonzero_from_str_radix`
+
+
+
+The tracking issue for this feature is: [#152193]
+
+[#152193]: https://github.com/rust-lang/rust/issues/152193
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "nonzero_internals",
description: r##"# `nonzero_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -8639,6 +11200,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "nonzero_ops",
description: r##"# `nonzero_ops`
+
+
The tracking issue for this feature is: [#84186]
[#84186]: https://github.com/rust-lang/rust/issues/84186
@@ -8650,12 +11213,28 @@ The tracking issue for this feature is: [#84186]
deny_since: None,
},
Lint {
- label: "num_midpoint_signed",
- description: r##"# `num_midpoint_signed`
+ label: "normalize_lexically",
+ description: r##"# `normalize_lexically`
+
-The tracking issue for this feature is: [#110840]
-[#110840]: https://github.com/rust-lang/rust/issues/110840
+The tracking issue for this feature is: [#134694]
+
+[#134694]: https://github.com/rust-lang/rust/issues/134694
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "num_internals",
+ description: r##"# `num_internals`
+
+
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -8667,6 +11246,8 @@ The tracking issue for this feature is: [#110840]
label: "numfmt",
description: r##"# `numfmt`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -8676,12 +11257,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "offset_of_enum",
- description: r##"# `offset_of_enum`
+ label: "nvptx_target_feature",
+ description: r##"# `nvptx_target_feature`
-The tracking issue for this feature is: [#120141]
+Target feaures on nvptx.
-[#120141]: https://github.com/rust-lang/rust/issues/120141
+The tracking issue for this feature is: [#150254]
+
+[#150254]: https://github.com/rust-lang/rust/issues/150254
------------------------
"##,
@@ -8690,12 +11273,12 @@ The tracking issue for this feature is: [#120141]
deny_since: None,
},
Lint {
- label: "offset_of_slice",
- description: r##"# `offset_of_slice`
+ label: "objc_class_variant",
+ description: r##"# `objc_class_variant`
-The tracking issue for this feature is: [#126151]
-[#126151]: https://github.com/rust-lang/rust/issues/126151
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -8704,8 +11287,10 @@ The tracking issue for this feature is: [#126151]
deny_since: None,
},
Lint {
- label: "omit_gdb_pretty_printer_section",
- description: r##"# `omit_gdb_pretty_printer_section`
+ label: "objc_selector_variant",
+ description: r##"# `objc_selector_variant`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -8716,9 +11301,84 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "offset_of_enum",
+ description: r##"# `offset_of_enum`
+
+The tracking issue for this feature is: [#120141]
+
+[#120141]: https://github.com/rust-lang/rust/issues/120141
+
+------------------------
+
+When the `offset_of_enum` feature is enabled, the [`offset_of!`] macro may be used to obtain the
+offsets of fields of `enum`s; to express this, `enum` variants may be traversed as if they were
+fields. Variants themselves do not have an offset, so they cannot appear as the last path component.
+
+```rust
+#![feature(offset_of_enum)]
+use std::mem;
+
+#[repr(u8)]
+enum Enum {
+ A(u8, u16),
+ B { one: u8, two: u16 },
+}
+
+assert_eq!(mem::offset_of!(Enum, A.0), 1);
+assert_eq!(mem::offset_of!(Enum, B.two), 2);
+
+assert_eq!(mem::offset_of!(Option<&u8>, Some.0), 0);
+```
+
+[`offset_of!`]: ../../std/mem/macro.offset_of.html
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "offset_of_slice",
+ description: r##"# `offset_of_slice`
+
+The tracking issue for this feature is: [#126151]
+
+[#126151]: https://github.com/rust-lang/rust/issues/126151
+
+------------------------
+
+When the `offset_of_slice` feature is enabled, the [`offset_of!`] macro may be used to determine
+the offset of fields whose type is `[T]`, that is, a slice of dynamic size.
+
+In general, fields whose type is dynamically sized do not have statically known offsets because
+they do not have statically known alignments. However, `[T]` has the same alignment as `T`, so
+it specifically may be allowed.
+
+```rust
+#![feature(offset_of_slice)]
+
+#[repr(C)]
+pub struct Struct {
+ head: u32,
+ tail: [u8],
+}
+
+fn main() {
+ assert_eq!(std::mem::offset_of!(Struct, tail), 4);
+}
+```
+
+[`offset_of!`]: ../../std/mem/macro.offset_of.html
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "once_cell_get_mut",
description: r##"# `once_cell_get_mut`
+
+
The tracking issue for this feature is: [#121641]
[#121641]: https://github.com/rust-lang/rust/issues/121641
@@ -8733,6 +11393,8 @@ The tracking issue for this feature is: [#121641]
label: "once_cell_try",
description: r##"# `once_cell_try`
+
+
The tracking issue for this feature is: [#109737]
[#109737]: https://github.com/rust-lang/rust/issues/109737
@@ -8747,6 +11409,8 @@ The tracking issue for this feature is: [#109737]
label: "once_cell_try_insert",
description: r##"# `once_cell_try_insert`
+
+
The tracking issue for this feature is: [#116693]
[#116693]: https://github.com/rust-lang/rust/issues/116693
@@ -8758,12 +11422,14 @@ The tracking issue for this feature is: [#116693]
deny_since: None,
},
Lint {
- label: "once_wait",
- description: r##"# `once_wait`
+ label: "one_sided_range",
+ description: r##"# `one_sided_range`
+
-The tracking issue for this feature is: [#127527]
-[#127527]: https://github.com/rust-lang/rust/issues/127527
+The tracking issue for this feature is: [#69780]
+
+[#69780]: https://github.com/rust-lang/rust/issues/69780
------------------------
"##,
@@ -8772,12 +11438,14 @@ The tracking issue for this feature is: [#127527]
deny_since: None,
},
Lint {
- label: "one_sided_range",
- description: r##"# `one_sided_range`
+ label: "oneshot_channel",
+ description: r##"# `oneshot_channel`
-The tracking issue for this feature is: [#69780]
-[#69780]: https://github.com/rust-lang/rust/issues/69780
+
+The tracking issue for this feature is: [#143674]
+
+[#143674]: https://github.com/rust-lang/rust/issues/143674
------------------------
"##,
@@ -8789,6 +11457,8 @@ The tracking issue for this feature is: [#69780]
label: "optimize_attribute",
description: r##"# `optimize_attribute`
+Allows using `#[optimize(X)]`.
+
The tracking issue for this feature is: [#54882]
[#54882]: https://github.com/rust-lang/rust/issues/54882
@@ -8803,6 +11473,8 @@ The tracking issue for this feature is: [#54882]
label: "option_array_transpose",
description: r##"# `option_array_transpose`
+
+
The tracking issue for this feature is: [#130828]
[#130828]: https://github.com/rust-lang/rust/issues/130828
@@ -8814,12 +11486,14 @@ The tracking issue for this feature is: [#130828]
deny_since: None,
},
Lint {
- label: "option_zip",
- description: r##"# `option_zip`
+ label: "option_get_or_try_insert_with",
+ description: r##"# `option_get_or_try_insert_with`
-The tracking issue for this feature is: [#70086]
-[#70086]: https://github.com/rust-lang/rust/issues/70086
+
+The tracking issue for this feature is: [#143648]
+
+[#143648]: https://github.com/rust-lang/rust/issues/143648
------------------------
"##,
@@ -8828,12 +11502,14 @@ The tracking issue for this feature is: [#70086]
deny_since: None,
},
Lint {
- label: "os_str_display",
- description: r##"# `os_str_display`
+ label: "option_into_flat_iter",
+ description: r##"# `option_into_flat_iter`
+
-The tracking issue for this feature is: [#120048]
-[#120048]: https://github.com/rust-lang/rust/issues/120048
+The tracking issue for this feature is: [#148441]
+
+[#148441]: https://github.com/rust-lang/rust/issues/148441
------------------------
"##,
@@ -8842,12 +11518,46 @@ The tracking issue for this feature is: [#120048]
deny_since: None,
},
Lint {
- label: "os_str_slice",
- description: r##"# `os_str_slice`
+ label: "option_reduce",
+ description: r##"# `option_reduce`
-The tracking issue for this feature is: [#118485]
-[#118485]: https://github.com/rust-lang/rust/issues/118485
+
+The tracking issue for this feature is: [#144273]
+
+[#144273]: https://github.com/rust-lang/rust/issues/144273
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "option_reference_flattening",
+ description: r##"# `option_reference_flattening`
+
+
+
+The tracking issue for this feature is: [#149221]
+
+[#149221]: https://github.com/rust-lang/rust/issues/149221
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "option_zip",
+ description: r##"# `option_zip`
+
+
+
+The tracking issue for this feature is: [#70086]
+
+[#70086]: https://github.com/rust-lang/rust/issues/70086
------------------------
"##,
@@ -8856,12 +11566,14 @@ The tracking issue for this feature is: [#118485]
deny_since: None,
},
Lint {
- label: "os_string_pathbuf_leak",
- description: r##"# `os_string_pathbuf_leak`
+ label: "os_str_slice",
+ description: r##"# `os_str_slice`
+
+
-The tracking issue for this feature is: [#125965]
+The tracking issue for this feature is: [#118485]
-[#125965]: https://github.com/rust-lang/rust/issues/125965
+[#118485]: https://github.com/rust-lang/rust/issues/118485
------------------------
"##,
@@ -8873,6 +11585,8 @@ The tracking issue for this feature is: [#125965]
label: "os_string_truncate",
description: r##"# `os_string_truncate`
+
+
The tracking issue for this feature is: [#133262]
[#133262]: https://github.com/rust-lang/rust/issues/133262
@@ -8887,6 +11601,8 @@ The tracking issue for this feature is: [#133262]
label: "panic_abort",
description: r##"# `panic_abort`
+
+
The tracking issue for this feature is: [#32837]
[#32837]: https://github.com/rust-lang/rust/issues/32837
@@ -8901,6 +11617,8 @@ The tracking issue for this feature is: [#32837]
label: "panic_always_abort",
description: r##"# `panic_always_abort`
+
+
The tracking issue for this feature is: [#84438]
[#84438]: https://github.com/rust-lang/rust/issues/84438
@@ -8915,6 +11633,8 @@ The tracking issue for this feature is: [#84438]
label: "panic_backtrace_config",
description: r##"# `panic_backtrace_config`
+
+
The tracking issue for this feature is: [#93346]
[#93346]: https://github.com/rust-lang/rust/issues/93346
@@ -8929,6 +11649,8 @@ The tracking issue for this feature is: [#93346]
label: "panic_can_unwind",
description: r##"# `panic_can_unwind`
+
+
The tracking issue for this feature is: [#92988]
[#92988]: https://github.com/rust-lang/rust/issues/92988
@@ -8943,21 +11665,9 @@ The tracking issue for this feature is: [#92988]
label: "panic_internals",
description: r##"# `panic_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: "panic_payload_as_str",
- description: r##"# `panic_payload_as_str`
-The tracking issue for this feature is: [#125175]
-
-[#125175]: https://github.com/rust-lang/rust/issues/125175
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -8969,6 +11679,8 @@ The tracking issue for this feature is: [#125175]
label: "panic_runtime",
description: r##"# `panic_runtime`
+Allows using the `#![panic_runtime]` attribute.
+
The tracking issue for this feature is: [#32837]
[#32837]: https://github.com/rust-lang/rust/issues/32837
@@ -8983,6 +11695,8 @@ The tracking issue for this feature is: [#32837]
label: "panic_unwind",
description: r##"# `panic_unwind`
+
+
The tracking issue for this feature is: [#32837]
[#32837]: https://github.com/rust-lang/rust/issues/32837
@@ -8997,6 +11711,8 @@ The tracking issue for this feature is: [#32837]
label: "panic_update_hook",
description: r##"# `panic_update_hook`
+
+
The tracking issue for this feature is: [#92649]
[#92649]: https://github.com/rust-lang/rust/issues/92649
@@ -9008,9 +11724,25 @@ The tracking issue for this feature is: [#92649]
deny_since: None,
},
Lint {
+ label: "partial_ord_chaining_methods",
+ description: r##"# `partial_ord_chaining_methods`
+
+
+
+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: "patchable_function_entry",
description: r##"# `patchable_function_entry`
+Allows specifying nop padding on functions for dynamic patching.
+
The tracking issue for this feature is: [#123115]
[#123115]: https://github.com/rust-lang/rust/issues/123115
@@ -9022,12 +11754,14 @@ The tracking issue for this feature is: [#123115]
deny_since: None,
},
Lint {
- label: "path_add_extension",
- description: r##"# `path_add_extension`
+ label: "path_absolute_method",
+ description: r##"# `path_absolute_method`
+
-The tracking issue for this feature is: [#127292]
-[#127292]: https://github.com/rust-lang/rust/issues/127292
+The tracking issue for this feature is: [#153328]
+
+[#153328]: https://github.com/rust-lang/rust/issues/153328
------------------------
"##,
@@ -9036,12 +11770,30 @@ The tracking issue for this feature is: [#127292]
deny_since: None,
},
Lint {
- label: "path_file_prefix",
- description: r##"# `path_file_prefix`
+ label: "path_is_empty",
+ description: r##"# `path_is_empty`
+
-The tracking issue for this feature is: [#86319]
-[#86319]: https://github.com/rust-lang/rust/issues/86319
+The tracking issue for this feature is: [#148494]
+
+[#148494]: https://github.com/rust-lang/rust/issues/148494
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "path_trailing_sep",
+ description: r##"# `path_trailing_sep`
+
+
+
+The tracking issue for this feature is: [#142503]
+
+[#142503]: https://github.com/rust-lang/rust/issues/142503
------------------------
"##,
@@ -9053,6 +11805,8 @@ The tracking issue for this feature is: [#86319]
label: "pattern",
description: r##"# `pattern`
+
+
The tracking issue for this feature is: [#27721]
[#27721]: https://github.com/rust-lang/rust/issues/27721
@@ -9067,6 +11821,8 @@ The tracking issue for this feature is: [#27721]
label: "pattern_complexity_limit",
description: r##"# `pattern_complexity_limit`
+Set the maximum pattern complexity allowed (not limited by default).
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9079,6 +11835,24 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "pattern_type_macro",
description: r##"# `pattern_type_macro`
+
+
+The tracking issue for this feature is: [#123646]
+
+[#123646]: https://github.com/rust-lang/rust/issues/123646
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "pattern_type_range_trait",
+ description: r##"# `pattern_type_range_trait`
+
+
+
The tracking issue for this feature is: [#123646]
[#123646]: https://github.com/rust-lang/rust/issues/123646
@@ -9093,6 +11867,8 @@ The tracking issue for this feature is: [#123646]
label: "pattern_types",
description: r##"# `pattern_types`
+Allows using pattern types.
+
The tracking issue for this feature is: [#123646]
[#123646]: https://github.com/rust-lang/rust/issues/123646
@@ -9107,6 +11883,8 @@ The tracking issue for this feature is: [#123646]
label: "peer_credentials_unix_socket",
description: r##"# `peer_credentials_unix_socket`
+
+
The tracking issue for this feature is: [#42839]
[#42839]: https://github.com/rust-lang/rust/issues/42839
@@ -9118,12 +11896,44 @@ The tracking issue for this feature is: [#42839]
deny_since: None,
},
Lint {
+ label: "phantom_variance_markers",
+ description: r##"# `phantom_variance_markers`
+
+
+
+The tracking issue for this feature is: [#135806]
+
+[#135806]: https://github.com/rust-lang/rust/issues/135806
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "pin_coerce_unsized_trait",
description: r##"# `pin_coerce_unsized_trait`
-The tracking issue for this feature is: [#123430]
-[#123430]: https://github.com/rust-lang/rust/issues/123430
+
+The tracking issue for this feature is: [#150112]
+
+[#150112]: https://github.com/rust-lang/rust/issues/150112
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "pin_derefmut_internals",
+ description: r##"# `pin_derefmut_internals`
+
+
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -9135,6 +11945,8 @@ The tracking issue for this feature is: [#123430]
label: "pin_ergonomics",
description: r##"# `pin_ergonomics`
+Experimental features that make `Pin` more ergonomic.
+
The tracking issue for this feature is: [#130494]
[#130494]: https://github.com/rust-lang/rust/issues/130494
@@ -9149,6 +11961,8 @@ The tracking issue for this feature is: [#130494]
label: "pointer_is_aligned_to",
description: r##"# `pointer_is_aligned_to`
+
+
The tracking issue for this feature is: [#96284]
[#96284]: https://github.com/rust-lang/rust/issues/96284
@@ -9160,10 +11974,14 @@ The tracking issue for this feature is: [#96284]
deny_since: None,
},
Lint {
- label: "pointer_like_trait",
- description: r##"# `pointer_like_trait`
+ label: "pointer_try_cast_aligned",
+ description: r##"# `pointer_try_cast_aligned`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#141221]
+
+[#141221]: https://github.com/rust-lang/rust/issues/141221
------------------------
"##,
@@ -9175,6 +11993,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "portable_simd",
description: r##"# `portable_simd`
+
+
The tracking issue for this feature is: [#86656]
[#86656]: https://github.com/rust-lang/rust/issues/86656
@@ -9222,9 +12042,11 @@ get_foo().match {
label: "powerpc_target_feature",
description: r##"# `powerpc_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on powerpc.
+
+The tracking issue for this feature is: [#150255]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150255]: https://github.com/rust-lang/rust/issues/150255
------------------------
"##,
@@ -9233,12 +12055,12 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
- label: "precise_capturing_in_traits",
- description: r##"# `precise_capturing_in_traits`
+ label: "prelude_future",
+ description: r##"# `prelude_future`
-The tracking issue for this feature is: [#130044]
-[#130044]: https://github.com/rust-lang/rust/issues/130044
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -9247,12 +12069,12 @@ The tracking issue for this feature is: [#130044]
deny_since: None,
},
Lint {
- label: "prelude_2024",
- description: r##"# `prelude_2024`
+ label: "prelude_import",
+ description: r##"# `prelude_import`
-The tracking issue for this feature is: [#121042]
+Allows using `#[prelude_import]` on glob `use` items.
-[#121042]: https://github.com/rust-lang/rust/issues/121042
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -9261,8 +12083,10 @@ The tracking issue for this feature is: [#121042]
deny_since: None,
},
Lint {
- label: "prelude_import",
- description: r##"# `prelude_import`
+ label: "prelude_next",
+ description: r##"# `prelude_next`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -9276,9 +12100,11 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "prfchw_target_feature",
description: r##"# `prfchw_target_feature`
-The tracking issue for this feature is: [#44839]
+The prfchw target feature on x86.
+
+The tracking issue for this feature is: [#150256]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#150256]: https://github.com/rust-lang/rust/issues/150256
------------------------
"##,
@@ -9302,6 +12128,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "proc_macro_def_site",
description: r##"# `proc_macro_def_site`
+
+
The tracking issue for this feature is: [#54724]
[#54724]: https://github.com/rust-lang/rust/issues/54724
@@ -9316,6 +12144,8 @@ The tracking issue for this feature is: [#54724]
label: "proc_macro_diagnostic",
description: r##"# `proc_macro_diagnostic`
+
+
The tracking issue for this feature is: [#54140]
[#54140]: https://github.com/rust-lang/rust/issues/54140
@@ -9330,6 +12160,8 @@ The tracking issue for this feature is: [#54140]
label: "proc_macro_expand",
description: r##"# `proc_macro_expand`
+
+
The tracking issue for this feature is: [#90765]
[#90765]: https://github.com/rust-lang/rust/issues/90765
@@ -9344,6 +12176,8 @@ The tracking issue for this feature is: [#90765]
label: "proc_macro_hygiene",
description: r##"# `proc_macro_hygiene`
+Allows macro attributes on expressions, statements and non-inline modules.
+
The tracking issue for this feature is: [#54727]
[#54727]: https://github.com/rust-lang/rust/issues/54727
@@ -9358,6 +12192,8 @@ The tracking issue for this feature is: [#54727]
label: "proc_macro_internals",
description: r##"# `proc_macro_internals`
+
+
The tracking issue for this feature is: [#27812]
[#27812]: https://github.com/rust-lang/rust/issues/27812
@@ -9372,6 +12208,8 @@ The tracking issue for this feature is: [#27812]
label: "proc_macro_quote",
description: r##"# `proc_macro_quote`
+
+
The tracking issue for this feature is: [#54722]
[#54722]: https://github.com/rust-lang/rust/issues/54722
@@ -9386,6 +12224,8 @@ The tracking issue for this feature is: [#54722]
label: "proc_macro_span",
description: r##"# `proc_macro_span`
+
+
The tracking issue for this feature is: [#54725]
[#54725]: https://github.com/rust-lang/rust/issues/54725
@@ -9400,6 +12240,8 @@ The tracking issue for this feature is: [#54725]
label: "proc_macro_totokens",
description: r##"# `proc_macro_totokens`
+
+
The tracking issue for this feature is: [#130977]
[#130977]: https://github.com/rust-lang/rust/issues/130977
@@ -9414,6 +12256,24 @@ The tracking issue for this feature is: [#130977]
label: "proc_macro_tracked_env",
description: r##"# `proc_macro_tracked_env`
+
+
+The tracking issue for this feature is: [#99515]
+
+[#99515]: https://github.com/rust-lang/rust/issues/99515
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "proc_macro_tracked_path",
+ description: r##"# `proc_macro_tracked_path`
+
+
+
The tracking issue for this feature is: [#99515]
[#99515]: https://github.com/rust-lang/rust/issues/99515
@@ -9425,9 +12285,43 @@ The tracking issue for this feature is: [#99515]
deny_since: None,
},
Lint {
+ label: "proc_macro_value",
+ description: r##"# `proc_macro_value`
+
+
+
+The tracking issue for this feature is: [#136652]
+
+[#136652]: https://github.com/rust-lang/rust/issues/136652
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "process_chroot",
+ description: r##"# `process_chroot`
+
+
+
+The tracking issue for this feature is: [#141298]
+
+[#141298]: https://github.com/rust-lang/rust/issues/141298
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "process_exitcode_internals",
description: r##"# `process_exitcode_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9440,6 +12334,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "process_internals",
description: r##"# `process_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9449,6 +12345,22 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "process_setsid",
+ description: r##"# `process_setsid`
+
+
+
+The tracking issue for this feature is: [#105376]
+
+[#105376]: https://github.com/rust-lang/rust/issues/105376
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "profiler_runtime",
description: r##"# `profiler_runtime`
@@ -9473,12 +12385,14 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
- label: "ptr_alignment_type",
- description: r##"# `ptr_alignment_type`
+ label: "profiling_marker_api",
+ description: r##"# `profiling_marker_api`
-The tracking issue for this feature is: [#102070]
-[#102070]: https://github.com/rust-lang/rust/issues/102070
+
+The tracking issue for this feature is: [#148197]
+
+[#148197]: https://github.com/rust-lang/rust/issues/148197
------------------------
"##,
@@ -9487,12 +12401,14 @@ The tracking issue for this feature is: [#102070]
deny_since: None,
},
Lint {
- label: "ptr_as_ref_unchecked",
- description: r##"# `ptr_as_ref_unchecked`
+ label: "ptr_alignment_type",
+ description: r##"# `ptr_alignment_type`
+
+
-The tracking issue for this feature is: [#122034]
+The tracking issue for this feature is: [#102070]
-[#122034]: https://github.com/rust-lang/rust/issues/122034
+[#102070]: https://github.com/rust-lang/rust/issues/102070
------------------------
"##,
@@ -9504,6 +12420,8 @@ The tracking issue for this feature is: [#122034]
label: "ptr_as_uninit",
description: r##"# `ptr_as_uninit`
+
+
The tracking issue for this feature is: [#75402]
[#75402]: https://github.com/rust-lang/rust/issues/75402
@@ -9515,9 +12433,43 @@ The tracking issue for this feature is: [#75402]
deny_since: None,
},
Lint {
+ label: "ptr_cast_array",
+ description: r##"# `ptr_cast_array`
+
+
+
+The tracking issue for this feature is: [#144514]
+
+[#144514]: https://github.com/rust-lang/rust/issues/144514
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "ptr_cast_slice",
+ description: r##"# `ptr_cast_slice`
+
+
+
+The tracking issue for this feature is: [#149103]
+
+[#149103]: https://github.com/rust-lang/rust/issues/149103
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ptr_internals",
description: r##"# `ptr_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9530,6 +12482,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "ptr_mask",
description: r##"# `ptr_mask`
+
+
The tracking issue for this feature is: [#98290]
[#98290]: https://github.com/rust-lang/rust/issues/98290
@@ -9544,6 +12498,8 @@ The tracking issue for this feature is: [#98290]
label: "ptr_metadata",
description: r##"# `ptr_metadata`
+
+
The tracking issue for this feature is: [#81513]
[#81513]: https://github.com/rust-lang/rust/issues/81513
@@ -9555,12 +12511,12 @@ The tracking issue for this feature is: [#81513]
deny_since: None,
},
Lint {
- label: "ptr_sub_ptr",
- description: r##"# `ptr_sub_ptr`
+ label: "pub_crate_should_not_need_unstable_attr",
+ description: r##"# `pub_crate_should_not_need_unstable_attr`
+
-The tracking issue for this feature is: [#95892]
-[#95892]: https://github.com/rust-lang/rust/issues/95892
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -9569,10 +12525,14 @@ The tracking issue for this feature is: [#95892]
deny_since: None,
},
Lint {
- label: "pub_crate_should_not_need_unstable_attr",
- description: r##"# `pub_crate_should_not_need_unstable_attr`
+ label: "random",
+ description: r##"# `random`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#130703]
+
+[#130703]: https://github.com/rust-lang/rust/issues/130703
------------------------
"##,
@@ -9581,12 +12541,46 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "random",
- description: r##"# `random`
+ label: "range_bounds_is_empty",
+ description: r##"# `range_bounds_is_empty`
-The tracking issue for this feature is: [#130703]
-[#130703]: https://github.com/rust-lang/rust/issues/130703
+
+The tracking issue for this feature is: [#137300]
+
+[#137300]: https://github.com/rust-lang/rust/issues/137300
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "range_into_bounds",
+ description: r##"# `range_into_bounds`
+
+
+
+The tracking issue for this feature is: [#136903]
+
+[#136903]: https://github.com/rust-lang/rust/issues/136903
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "raw_dylib_elf",
+ description: r##"# `raw_dylib_elf`
+
+Allows the use of raw-dylibs on ELF platforms
+
+The tracking issue for this feature is: [#135694]
+
+[#135694]: https://github.com/rust-lang/rust/issues/135694
------------------------
"##,
@@ -9598,6 +12592,8 @@ The tracking issue for this feature is: [#130703]
label: "raw_os_error_ty",
description: r##"# `raw_os_error_ty`
+
+
The tracking issue for this feature is: [#107792]
[#107792]: https://github.com/rust-lang/rust/issues/107792
@@ -9612,6 +12608,8 @@ The tracking issue for this feature is: [#107792]
label: "raw_slice_split",
description: r##"# `raw_slice_split`
+
+
The tracking issue for this feature is: [#95595]
[#95595]: https://github.com/rust-lang/rust/issues/95595
@@ -9626,6 +12624,8 @@ The tracking issue for this feature is: [#95595]
label: "raw_vec_internals",
description: r##"# `raw_vec_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9635,9 +12635,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "read_array",
+ description: r##"# `read_array`
+
+
+
+The tracking issue for this feature is: [#148848]
+
+[#148848]: https://github.com/rust-lang/rust/issues/148848
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "read_buf",
description: r##"# `read_buf`
+
+
The tracking issue for this feature is: [#78485]
[#78485]: https://github.com/rust-lang/rust/issues/78485
@@ -9649,9 +12667,43 @@ The tracking issue for this feature is: [#78485]
deny_since: None,
},
Lint {
+ label: "read_buf_at",
+ description: r##"# `read_buf_at`
+
+
+
+The tracking issue for this feature is: [#140771]
+
+[#140771]: https://github.com/rust-lang/rust/issues/140771
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "reborrow",
+ description: r##"# `reborrow`
+
+
+
+The tracking issue for this feature is: [#145612]
+
+[#145612]: https://github.com/rust-lang/rust/issues/145612
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "reentrant_lock",
description: r##"# `reentrant_lock`
+
+
The tracking issue for this feature is: [#121440]
[#121440]: https://github.com/rust-lang/rust/issues/121440
@@ -9663,6 +12715,22 @@ The tracking issue for this feature is: [#121440]
deny_since: None,
},
Lint {
+ label: "reentrant_lock_data_ptr",
+ description: r##"# `reentrant_lock_data_ptr`
+
+
+
+The tracking issue for this feature is: [#140368]
+
+[#140368]: https://github.com/rust-lang/rust/issues/140368
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ref_pat_eat_one_layer_2024",
description: r##"# `ref_pat_eat_one_layer_2024`
@@ -9670,7 +12738,41 @@ The tracking issue for this feature is: [#123076]
[#123076]: https://github.com/rust-lang/rust/issues/123076
-------------------------
+---
+
+This feature is incomplete and not yet intended for general use.
+
+This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
+in Rust, allowing `&` patterns in more places. For example:
+
+```rust,edition2024
+#![feature(ref_pat_eat_one_layer_2024)]
+#![allow(incomplete_features)]
+#
+# // Tests type equality in a way that avoids coercing `&&T` or `&mut T` to `&T`.
+# trait Eq<T> {}
+# impl<T> Eq<T> for T {}
+# fn has_type<T>(_: impl Eq<T>) {}
+
+// `&` can match against a `ref` binding mode instead of a reference type:
+let (x, &y) = &(0, 1);
+has_type::<&u8>(x);
+has_type::<u8>(y);
+
+// `&` can match against `&mut` references:
+let &z = &mut 2;
+has_type::<u8>(z);
+```
+
+For specifics, see the corresponding typing rules for [Editions 2021 and earlier] and for
+[Editions 2024 and later]. For more information on binding modes, see [The Rust Reference].
+
+For alternative experimental match ergonomics, see the feature
+[`ref_pat_eat_one_layer_2024_structural`](./ref-pat-eat-one-layer-2024-structural.md).
+
+[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQABAAAAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
+[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAAABAQABAgIAAQEBAAEBAAABAAA%3D&mode=rules&do_cmp=false
+[The Rust Reference]: https://doc.rust-lang.org/reference/patterns.html#binding-modes
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -9684,19 +12786,54 @@ The tracking issue for this feature is: [#123076]
[#123076]: https://github.com/rust-lang/rust/issues/123076
-------------------------
+---
+
+This feature is incomplete and not yet intended for general use.
+
+This implements experimental, Edition-dependent match ergonomics under consideration for inclusion
+in Rust, allowing `&` patterns in more places. For example:
+```rust,edition2024
+#![feature(ref_pat_eat_one_layer_2024_structural)]
+#![allow(incomplete_features)]
+#
+# // Tests type equality in a way that avoids coercing `&&T` or `&mut T` to `&T`.
+# trait Eq<T> {}
+# impl<T> Eq<T> for T {}
+# fn has_type<T>(_: impl Eq<T>) {}
+
+// `&` can match against a `ref` binding mode instead of a reference type:
+let (x, &y) = &(0, 1);
+has_type::<&u8>(x);
+has_type::<u8>(y);
+
+// `&` can match against `&mut` references:
+let &z = &mut 2;
+has_type::<u8>(z);
+```
+
+For specifics, see the corresponding typing rules for [Editions 2021 and earlier] and for
+[Editions 2024 and later]. For more information on binding modes, see [The Rust Reference].
+
+For alternative experimental match ergonomics, see the feature
+[`ref_pat_eat_one_layer_2024`](./ref-pat-eat-one-layer-2024.md).
+
+[Editions 2021 and earlier]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAQIBAQEBAAAAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
+[Editions 2024 and later]: https://nadrieril.github.io/typing-rust-patterns/?compare=false&opts1=AQEBAgEBAQEBAgIAAAAAAAAAAAAAAAA%3D&mode=rules&do_cmp=false
+[The Rust Reference]: https://doc.rust-lang.org/reference/patterns.html#binding-modes
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "register_tool",
- description: r##"# `register_tool`
+ label: "refcell_try_map",
+ description: r##"# `refcell_try_map`
-The tracking issue for this feature is: [#66079]
-[#66079]: https://github.com/rust-lang/rust/issues/66079
+
+The tracking issue for this feature is: [#143801]
+
+[#143801]: https://github.com/rust-lang/rust/issues/143801
------------------------
"##,
@@ -9705,25 +12842,62 @@ The tracking issue for this feature is: [#66079]
deny_since: None,
},
Lint {
- label: "repr128",
- description: r##"# `repr128`
+ label: "register_tool",
+ description: r##"# `register_tool`
-The tracking issue for this feature is: [#56071]
+The tracking issue for this feature is: [#66079]
-[#56071]: https://github.com/rust-lang/rust/issues/56071
+[#66079]: https://github.com/rust-lang/rust/issues/66079
------------------------
-The `repr128` feature adds support for `#[repr(u128)]` on `enum`s.
+The `register_tool` language feature informs the compiler that attributes in your code are meant to be used with tools other than the compiler itself. This can be useful if your code has semantic meaning without the external tool, but enables additional features when the tool is present.
-```rust
-#![feature(repr128)]
+`register_tool` also allows configuring lint levels for external tools.
-#[repr(u128)]
-enum Foo {
- Bar(u64),
+Tool attributes are only meant for ignorable attributes. If your code *changes* meaning when the attribute is present, it should not use a tool attribute (because it cannot be compiled with anything other than the external tool, and in a sense is a fork of the language).
+
+------------------------
+
+`#![register_tool(tool)]` is an attribute, and is only valid at the crate root.
+Attributes using the registered tool are checked for valid syntax, and lint attributes are checked to be in a valid format. However, the compiler cannot validate the semantics of the attribute, nor can it tell whether the configured lint is present in the external tool.
+
+Semantically, `clippy::*`, `rustdoc::*`, and `rustfmt::*` lints and attributes all behave as if `#![register_tool(clippy, rustdoc, rustfmt)]` were injected into the crate root, except that the `rustdoc` namespace can only be used for lints, not for attributes.
+When compiling with `-Z unstable-features`, `rustc::*` lints can also be used. Like `rustdoc`, the `rustc` namespace can only be used with lints, not attributes.
+
+The compiler will emit an error if it encounters a lint/attribute whose namespace isn't a registered tool.
+
+Tool namespaces cannot be nested; `register_tool(main_tool::subtool)` is an error.
+
+## Examples
+
+Tool attributes:
+
+```rust
+#![feature(register_tool)]
+#![register_tool(c2rust)]
+
+// Mark which C header file this module was generated from.
+#[c2rust::header_src = "operations.h"]
+pub mod operations_h {
+ use std::ffi::c_int;
+
+ // Mark which source line this struct was generated from.
+ #[c2rust::src_loc = "11:0"]
+ pub struct Point {
+ pub x: c_int,
+ pub y: c_int,
+ }
}
```
+
+Tool lints:
+
+```
+#![feature(register_tool)]
+#![register_tool(bevy)]
+#![deny(bevy::duplicate_bevy_dependencies)]
+```
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -9733,6 +12907,8 @@ enum Foo {
label: "repr_simd",
description: r##"# `repr_simd`
+Allows `repr(simd)` and importing the various simd intrinsics.
+
The tracking issue for this feature is: [#27731]
[#27731]: https://github.com/rust-lang/rust/issues/27731
@@ -9747,6 +12923,8 @@ The tracking issue for this feature is: [#27731]
label: "restricted_std",
description: r##"# `restricted_std`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -9756,12 +12934,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "result_flattening",
- description: r##"# `result_flattening`
+ label: "result_option_map_or_default",
+ description: r##"# `result_option_map_or_default`
-The tracking issue for this feature is: [#70142]
-[#70142]: https://github.com/rust-lang/rust/issues/70142
+
+The tracking issue for this feature is: [#138099]
+
+[#138099]: https://github.com/rust-lang/rust/issues/138099
------------------------
"##,
@@ -9773,6 +12953,8 @@ The tracking issue for this feature is: [#70142]
label: "return_type_notation",
description: r##"# `return_type_notation`
+Allows bounding the return type of AFIT/RPITIT.
+
The tracking issue for this feature is: [#109417]
[#109417]: https://github.com/rust-lang/rust/issues/109417
@@ -9784,12 +12966,14 @@ The tracking issue for this feature is: [#109417]
deny_since: None,
},
Lint {
- label: "riscv_target_feature",
- description: r##"# `riscv_target_feature`
+ label: "rev_into_inner",
+ description: r##"# `rev_into_inner`
+
-The tracking issue for this feature is: [#44839]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#144277]
+
+[#144277]: https://github.com/rust-lang/rust/issues/144277
------------------------
"##,
@@ -9798,12 +12982,14 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
- label: "round_char_boundary",
- description: r##"# `round_char_boundary`
+ label: "riscv_target_feature",
+ description: r##"# `riscv_target_feature`
+
+Target features on riscv.
-The tracking issue for this feature is: [#93743]
+The tracking issue for this feature is: [#150257]
-[#93743]: https://github.com/rust-lang/rust/issues/93743
+[#150257]: https://github.com/rust-lang/rust/issues/150257
------------------------
"##,
@@ -9827,9 +13013,11 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "rtm_target_feature",
description: r##"# `rtm_target_feature`
-The tracking issue for this feature is: [#44839]
+The rtm target feature on x86.
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#150258]
+
+[#150258]: https://github.com/rust-lang/rust/issues/150258
------------------------
"##,
@@ -9841,6 +13029,8 @@ The tracking issue for this feature is: [#44839]
label: "rust_cold_cc",
description: r##"# `rust_cold_cc`
+Allows `extern "rust-cold"`.
+
The tracking issue for this feature is: [#97544]
[#97544]: https://github.com/rust-lang/rust/issues/97544
@@ -9852,12 +13042,14 @@ The tracking issue for this feature is: [#97544]
deny_since: None,
},
Lint {
- label: "rustc_allow_const_fn_unstable",
- description: r##"# `rustc_allow_const_fn_unstable`
+ label: "rust_preserve_none_cc",
+ description: r##"# `rust_preserve_none_cc`
-The tracking issue for this feature is: [#69399]
+Allows `extern "rust-preserve-none"`.
-[#69399]: https://github.com/rust-lang/rust/issues/69399
+The tracking issue for this feature is: [#151401]
+
+[#151401]: https://github.com/rust-lang/rust/issues/151401
------------------------
"##,
@@ -9878,19 +13070,20 @@ only discuss a few of them.
------------------------
The `rustc_attrs` feature allows debugging rustc type layouts by using
-`#[rustc_layout(...)]` to debug layout at compile time (it even works
+`#[rustc_dump_layout(...)]` to debug layout at compile time (it even works
with `cargo check`) as an alternative to `rustc -Z print-type-sizes`
that is way more verbose.
-Options provided by `#[rustc_layout(...)]` are `debug`, `size`, `align`,
-`abi`. Note that it only works on sized types without generics.
+Options provided by `#[rustc_dump_layout(...)]` are `backend_repr`, `align`,
+`debug`, `homogeneous_aggregate` and `size`.
+Note that it only works on sized types without generics.
## Examples
```rust,compile_fail
#![feature(rustc_attrs)]
-#[rustc_layout(abi, size)]
+#[rustc_dump_layout(backend_repr, size)]
pub enum X {
Y(u8, u8, u8),
Z(isize),
@@ -9900,7 +13093,7 @@ pub enum X {
When that is compiled, the compiler will error with something like
```text
-error: abi: Aggregate { sized: true }
+error: backend_repr: Aggregate { sized: true }
--> src/lib.rs:4:1
|
4 | / pub enum T {
@@ -9926,18 +13119,6 @@ error: aborting due to 2 previous errors
deny_since: None,
},
Lint {
- label: "rustc_encodable_decodable",
- description: r##"# `rustc_encodable_decodable`
-
-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: "rustc_private",
description: r##"# `rustc_private`
@@ -9947,9 +13128,42 @@ The tracking issue for this feature is: [#27812]
------------------------
-This feature allows access to unstable internal compiler crates.
+This feature allows access to unstable internal compiler crates such as `rustc_driver`.
+
+The presence of this feature changes the way the linkage format for dylibs is calculated in a way
+that is necessary for linking against dylibs that statically link `std` (such as `rustc_driver`).
+This makes this feature "viral" in linkage; its use in a given crate makes its use required in
+dependent crates which link to it (including integration tests, which are built as separate crates).
+
+## Common linker failures related to missing LLVM libraries
+
+### When using `rustc-private` with Official Toolchains
+
+When using the `rustc_private` feature with official toolchains distributed via rustup, you'll need to install:
+
+1. The `rustc-dev` component (provides compiler libraries)
+2. The `llvm-tools` component (provides LLVM libraries needed for linking)
+
+You can install these components using `rustup`:
+
+```text
+rustup component add rustc-dev llvm-tools
+```
+
+Without the `llvm-tools` component, you may encounter linking errors like:
-Additionally it changes the linking behavior of crates which have this feature enabled. It will prevent linking to a dylib if there's a static variant of it already statically linked into another dylib dependency. This is required to successfully link to `rustc_driver`.
+```text
+error: linking with `cc` failed: exit status: 1
+ |
+ = note: rust-lld: error: unable to find library -lLLVM-{version}
+```
+
+### When using `rustc-private` with Custom Toolchains
+
+For custom-built toolchains or environments not using rustup, different configuration may be required:
+
+- Ensure LLVM libraries are available in your library search paths
+- You might need to configure library paths explicitly depending on your LLVM installation
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -9959,6 +13173,8 @@ Additionally it changes the linking behavior of crates which have this feature e
label: "rustdoc_internals",
description: r##"# `rustdoc_internals`
+Allows using internal rustdoc features like `doc(keyword)`.
+
The tracking issue for this feature is: [#90418]
[#90418]: https://github.com/rust-lang/rust/issues/90418
@@ -9973,6 +13189,8 @@ The tracking issue for this feature is: [#90418]
label: "rustdoc_missing_doc_code_examples",
description: r##"# `rustdoc_missing_doc_code_examples`
+Allows using the `rustdoc::missing_doc_code_examples` lint
+
The tracking issue for this feature is: [#101730]
[#101730]: https://github.com/rust-lang/rust/issues/101730
@@ -9984,12 +13202,14 @@ The tracking issue for this feature is: [#101730]
deny_since: None,
},
Lint {
- label: "rwlock_downgrade",
- description: r##"# `rwlock_downgrade`
+ label: "rwlock_data_ptr",
+ description: r##"# `rwlock_data_ptr`
-The tracking issue for this feature is: [#128203]
-[#128203]: https://github.com/rust-lang/rust/issues/128203
+
+The tracking issue for this feature is: [#140368]
+
+[#140368]: https://github.com/rust-lang/rust/issues/140368
------------------------
"##,
@@ -10001,9 +13221,11 @@ The tracking issue for this feature is: [#128203]
label: "s390x_target_feature",
description: r##"# `s390x_target_feature`
-The tracking issue for this feature is: [#44839]
+Target features on s390x.
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#150259]
+
+[#150259]: https://github.com/rust-lang/rust/issues/150259
------------------------
"##,
@@ -10012,9 +13234,91 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
+ label: "sanitize",
+ description: r##"# `sanitize`
+
+The tracking issue for this feature is: [#39699]
+
+[#39699]: https://github.com/rust-lang/rust/issues/39699
+
+------------------------
+
+The `sanitize` attribute can be used to selectively disable or enable sanitizer
+instrumentation in an annotated function. This might be useful to: avoid
+instrumentation overhead in a performance critical function, or avoid
+instrumenting code that contains constructs unsupported by given sanitizer.
+
+The precise effect of this annotation depends on particular sanitizer in use.
+For example, with `sanitize(thread = "off")`, the thread sanitizer will no
+longer instrument non-atomic store / load operations, but it will instrument
+atomic operations to avoid reporting false positives and provide meaning full
+stack traces.
+
+This attribute was previously named `no_sanitize`.
+
+## Examples
+
+``` rust
+#![feature(sanitize)]
+
+#[sanitize(address = "off")]
+fn foo() {
+ // ...
+}
+```
+
+It is also possible to disable sanitizers for entire modules and enable them
+for single items or functions.
+
+```rust
+#![feature(sanitize)]
+
+#[sanitize(address = "off")]
+mod foo {
+ fn unsanitized() {
+ // ...
+ }
+
+ #[sanitize(address = "on")]
+ fn sanitized() {
+ // ...
+ }
+}
+```
+
+It's also applicable to impl blocks.
+
+```rust
+#![feature(sanitize)]
+
+trait MyTrait {
+ fn foo(&self);
+ fn bar(&self);
+}
+
+#[sanitize(address = "off")]
+impl MyTrait for () {
+ fn foo(&self) {
+ // ...
+ }
+
+ #[sanitize(address = "on")]
+ fn bar(&self) {
+ // ...
+ }
+}
+```
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "sealed",
description: r##"# `sealed`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -10024,9 +13328,27 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
+ label: "seek_io_take_position",
+ description: r##"# `seek_io_take_position`
+
+
+
+The tracking issue for this feature is: [#97227]
+
+[#97227]: https://github.com/rust-lang/rust/issues/97227
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "seek_stream_len",
description: r##"# `seek_stream_len`
+
+
The tracking issue for this feature is: [#59359]
[#59359]: https://github.com/rust-lang/rust/issues/59359
@@ -10038,9 +13360,27 @@ The tracking issue for this feature is: [#59359]
deny_since: None,
},
Lint {
+ label: "set_permissions_nofollow",
+ description: r##"# `set_permissions_nofollow`
+
+
+
+The tracking issue for this feature is: [#141607]
+
+[#141607]: https://github.com/rust-lang/rust/issues/141607
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "set_ptr_value",
description: r##"# `set_ptr_value`
+
+
The tracking issue for this feature is: [#75091]
[#75091]: https://github.com/rust-lang/rust/issues/75091
@@ -10055,6 +13395,8 @@ The tracking issue for this feature is: [#75091]
label: "setgroups",
description: r##"# `setgroups`
+
+
The tracking issue for this feature is: [#90747]
[#90747]: https://github.com/rust-lang/rust/issues/90747
@@ -10069,6 +13411,8 @@ The tracking issue for this feature is: [#90747]
label: "sgx_platform",
description: r##"# `sgx_platform`
+
+
The tracking issue for this feature is: [#56975]
[#56975]: https://github.com/rust-lang/rust/issues/56975
@@ -10080,12 +13424,14 @@ The tracking issue for this feature is: [#56975]
deny_since: None,
},
Lint {
- label: "sha512_sm_x86",
- description: r##"# `sha512_sm_x86`
+ label: "signed_bigint_helpers",
+ description: r##"# `signed_bigint_helpers`
-The tracking issue for this feature is: [#126624]
-[#126624]: https://github.com/rust-lang/rust/issues/126624
+
+The tracking issue for this feature is: [#151989]
+
+[#151989]: https://github.com/rust-lang/rust/issues/151989
------------------------
"##,
@@ -10097,6 +13443,8 @@ The tracking issue for this feature is: [#126624]
label: "simd_ffi",
description: r##"# `simd_ffi`
+Allows the use of SIMD types in functions declared in `extern` blocks.
+
The tracking issue for this feature is: [#27731]
[#27731]: https://github.com/rust-lang/rust/issues/27731
@@ -10108,24 +13456,14 @@ The tracking issue for this feature is: [#27731]
deny_since: None,
},
Lint {
- label: "sized_type_properties",
- description: r##"# `sized_type_properties`
-
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+ label: "sized_hierarchy",
+ description: r##"# `sized_hierarchy`
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "slice_as_array",
- description: r##"# `slice_as_array`
+Introduces a hierarchy of `Sized` traits (RFC 3729).
-The tracking issue for this feature is: [#133508]
+The tracking issue for this feature is: [#144404]
-[#133508]: https://github.com/rust-lang/rust/issues/133508
+[#144404]: https://github.com/rust-lang/rust/issues/144404
------------------------
"##,
@@ -10134,12 +13472,12 @@ The tracking issue for this feature is: [#133508]
deny_since: None,
},
Lint {
- label: "slice_as_chunks",
- description: r##"# `slice_as_chunks`
+ label: "sized_type_properties",
+ description: r##"# `sized_type_properties`
-The tracking issue for this feature is: [#74985]
-[#74985]: https://github.com/rust-lang/rust/issues/74985
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -10151,6 +13489,8 @@ The tracking issue for this feature is: [#74985]
label: "slice_concat_ext",
description: r##"# `slice_concat_ext`
+
+
The tracking issue for this feature is: [#27747]
[#27747]: https://github.com/rust-lang/rust/issues/27747
@@ -10165,6 +13505,8 @@ The tracking issue for this feature is: [#27747]
label: "slice_concat_trait",
description: r##"# `slice_concat_trait`
+
+
The tracking issue for this feature is: [#27747]
[#27747]: https://github.com/rust-lang/rust/issues/27747
@@ -10179,6 +13521,8 @@ The tracking issue for this feature is: [#27747]
label: "slice_from_ptr_range",
description: r##"# `slice_from_ptr_range`
+
+
The tracking issue for this feature is: [#89792]
[#89792]: https://github.com/rust-lang/rust/issues/89792
@@ -10193,6 +13537,8 @@ The tracking issue for this feature is: [#89792]
label: "slice_index_methods",
description: r##"# `slice_index_methods`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -10205,6 +13551,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "slice_internals",
description: r##"# `slice_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -10217,6 +13565,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "slice_iter_mut_as_mut_slice",
description: r##"# `slice_iter_mut_as_mut_slice`
+
+
The tracking issue for this feature is: [#93079]
[#93079]: https://github.com/rust-lang/rust/issues/93079
@@ -10228,9 +13578,27 @@ The tracking issue for this feature is: [#93079]
deny_since: None,
},
Lint {
+ label: "slice_partial_sort_unstable",
+ description: r##"# `slice_partial_sort_unstable`
+
+
+
+The tracking issue for this feature is: [#149046]
+
+[#149046]: https://github.com/rust-lang/rust/issues/149046
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "slice_partition_dedup",
description: r##"# `slice_partition_dedup`
+
+
The tracking issue for this feature is: [#54279]
[#54279]: https://github.com/rust-lang/rust/issues/54279
@@ -10245,6 +13613,8 @@ The tracking issue for this feature is: [#54279]
label: "slice_pattern",
description: r##"# `slice_pattern`
+
+
The tracking issue for this feature is: [#56345]
[#56345]: https://github.com/rust-lang/rust/issues/56345
@@ -10259,6 +13629,8 @@ The tracking issue for this feature is: [#56345]
label: "slice_ptr_get",
description: r##"# `slice_ptr_get`
+
+
The tracking issue for this feature is: [#74265]
[#74265]: https://github.com/rust-lang/rust/issues/74265
@@ -10273,6 +13645,8 @@ The tracking issue for this feature is: [#74265]
label: "slice_range",
description: r##"# `slice_range`
+
+
The tracking issue for this feature is: [#76393]
[#76393]: https://github.com/rust-lang/rust/issues/76393
@@ -10284,9 +13658,27 @@ The tracking issue for this feature is: [#76393]
deny_since: None,
},
Lint {
+ label: "slice_shift",
+ description: r##"# `slice_shift`
+
+
+
+The tracking issue for this feature is: [#151772]
+
+[#151772]: https://github.com/rust-lang/rust/issues/151772
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "slice_split_once",
description: r##"# `slice_split_once`
+
+
The tracking issue for this feature is: [#112811]
[#112811]: https://github.com/rust-lang/rust/issues/112811
@@ -10301,6 +13693,8 @@ The tracking issue for this feature is: [#112811]
label: "slice_swap_unchecked",
description: r##"# `slice_swap_unchecked`
+
+
The tracking issue for this feature is: [#88539]
[#88539]: https://github.com/rust-lang/rust/issues/88539
@@ -10312,12 +13706,30 @@ The tracking issue for this feature is: [#88539]
deny_since: None,
},
Lint {
- label: "slice_take",
- description: r##"# `slice_take`
+ label: "sliceindex_wrappers",
+ description: r##"# `sliceindex_wrappers`
+
+
+
+The tracking issue for this feature is: [#146179]
+
+[#146179]: https://github.com/rust-lang/rust/issues/146179
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "smart_pointer_try_map",
+ description: r##"# `smart_pointer_try_map`
+
-The tracking issue for this feature is: [#62280]
-[#62280]: https://github.com/rust-lang/rust/issues/62280
+The tracking issue for this feature is: [#144419]
+
+[#144419]: https://github.com/rust-lang/rust/issues/144419
------------------------
"##,
@@ -10329,6 +13741,8 @@ The tracking issue for this feature is: [#62280]
label: "solid_ext",
description: r##"# `solid_ext`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -10341,6 +13755,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "sort_floats",
description: r##"# `sort_floats`
+
+
The tracking issue for this feature is: [#93396]
[#93396]: https://github.com/rust-lang/rust/issues/93396
@@ -10355,6 +13771,8 @@ The tracking issue for this feature is: [#93396]
label: "sparc_target_feature",
description: r##"# `sparc_target_feature`
+Target features on sparc.
+
The tracking issue for this feature is: [#132783]
[#132783]: https://github.com/rust-lang/rust/issues/132783
@@ -10369,6 +13787,8 @@ The tracking issue for this feature is: [#132783]
label: "specialization",
description: r##"# `specialization`
+Allows specialization of implementations (RFC 1210).
+
The tracking issue for this feature is: [#31844]
[#31844]: https://github.com/rust-lang/rust/issues/31844
@@ -10383,6 +13803,8 @@ The tracking issue for this feature is: [#31844]
label: "split_array",
description: r##"# `split_array`
+
+
The tracking issue for this feature is: [#90091]
[#90091]: https://github.com/rust-lang/rust/issues/90091
@@ -10397,6 +13819,8 @@ The tracking issue for this feature is: [#90091]
label: "split_as_slice",
description: r##"# `split_as_slice`
+
+
The tracking issue for this feature is: [#96137]
[#96137]: https://github.com/rust-lang/rust/issues/96137
@@ -10408,12 +13832,12 @@ The tracking issue for this feature is: [#96137]
deny_since: None,
},
Lint {
- label: "sse4a_target_feature",
- description: r##"# `sse4a_target_feature`
+ label: "staged_api",
+ description: r##"# `staged_api`
-The tracking issue for this feature is: [#44839]
+Allows using the `#[stable]` and `#[unstable]` attributes.
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -10422,10 +13846,14 @@ The tracking issue for this feature is: [#44839]
deny_since: None,
},
Lint {
- label: "staged_api",
- description: r##"# `staged_api`
+ label: "static_align",
+ description: r##"# `static_align`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+Allows using `#[rustc_align_static(...)]` on static items.
+
+The tracking issue for this feature is: [#146177]
+
+[#146177]: https://github.com/rust-lang/rust/issues/146177
------------------------
"##,
@@ -10434,74 +13862,56 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "start",
- description: r##"# `start`
-
-The tracking issue for this feature is: [#29633]
-
-[#29633]: https://github.com/rust-lang/rust/issues/29633
-
-------------------------
+ label: "std_internals",
+ description: r##"# `std_internals`
-Allows you to mark a function as the entry point of the executable, which is
-necessary in `#![no_std]` environments.
-The function marked `#[start]` is passed the command line parameters in the same
-format as the C main function (aside from the integer types being used).
-It has to be non-generic and have the following signature:
-```rust,ignore (only-for-syntax-highlight)
-# let _:
-fn(isize, *const *const u8) -> isize
-# ;
-```
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
-This feature should not be confused with the `start` *lang item* which is
-defined by the `std` crate and is written `#[lang = "start"]`.
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "stdarch_aarch64_feature_detection",
+ description: r##"# `stdarch_aarch64_feature_detection`
-## Usage together with the `std` crate
-`#[start]` can be used in combination with the `std` crate, in which case the
-normal `main` function (which would get called from the `std` crate) won't be
-used as an entry point.
-The initialization code in `std` will be skipped this way.
-Example:
+The tracking issue for this feature is: [#127764]
-```rust
-#![feature(start)]
+[#127764]: https://github.com/rust-lang/rust/issues/127764
-#[start]
-fn start(_argc: isize, _argv: *const *const u8) -> isize {
- 0
-}
-```
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "stdarch_arm_feature_detection",
+ description: r##"# `stdarch_arm_feature_detection`
-Unwinding the stack past the `#[start]` function is currently considered
-Undefined Behavior (for any unwinding implementation):
-```rust,ignore (UB)
-#![feature(start)]
-#[start]
-fn start(_argc: isize, _argv: *const *const u8) -> isize {
- std::panic::catch_unwind(|| {
- panic!(); // panic safely gets caught or safely aborts execution
- });
+The tracking issue for this feature is: [#111190]
- panic!(); // UB!
+[#111190]: https://github.com/rust-lang/rust/issues/111190
- 0
-}
-```
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "std_internals",
- description: r##"# `std_internals`
+ label: "stdarch_internal",
+ description: r##"# `stdarch_internal`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
@@ -10512,12 +13922,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "stdarch_arm_feature_detection",
- description: r##"# `stdarch_arm_feature_detection`
+ label: "stdarch_loongarch_feature_detection",
+ description: r##"# `stdarch_loongarch_feature_detection`
-The tracking issue for this feature is: [#111190]
-[#111190]: https://github.com/rust-lang/rust/issues/111190
+
+The tracking issue for this feature is: [#117425]
+
+[#117425]: https://github.com/rust-lang/rust/issues/117425
------------------------
"##,
@@ -10529,6 +13941,8 @@ The tracking issue for this feature is: [#111190]
label: "stdarch_mips_feature_detection",
description: r##"# `stdarch_mips_feature_detection`
+
+
The tracking issue for this feature is: [#111188]
[#111188]: https://github.com/rust-lang/rust/issues/111188
@@ -10543,6 +13957,8 @@ The tracking issue for this feature is: [#111188]
label: "stdarch_powerpc_feature_detection",
description: r##"# `stdarch_powerpc_feature_detection`
+
+
The tracking issue for this feature is: [#111191]
[#111191]: https://github.com/rust-lang/rust/issues/111191
@@ -10554,9 +13970,43 @@ The tracking issue for this feature is: [#111191]
deny_since: None,
},
Lint {
+ label: "stdarch_riscv_feature_detection",
+ description: r##"# `stdarch_riscv_feature_detection`
+
+
+
+The tracking issue for this feature is: [#111192]
+
+[#111192]: https://github.com/rust-lang/rust/issues/111192
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "stdio_fd_consts",
+ description: r##"# `stdio_fd_consts`
+
+
+
+The tracking issue for this feature is: [#150836]
+
+[#150836]: https://github.com/rust-lang/rust/issues/150836
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "stdio_makes_pipe",
description: r##"# `stdio_makes_pipe`
+
+
The tracking issue for this feature is: [#98288]
[#98288]: https://github.com/rust-lang/rust/issues/98288
@@ -10568,9 +14018,27 @@ The tracking issue for this feature is: [#98288]
deny_since: None,
},
Lint {
+ label: "stdio_swap",
+ description: r##"# `stdio_swap`
+
+
+
+The tracking issue for this feature is: [#150667]
+
+[#150667]: https://github.com/rust-lang/rust/issues/150667
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "step_trait",
description: r##"# `step_trait`
+
+
The tracking issue for this feature is: [#42168]
[#42168]: https://github.com/rust-lang/rust/issues/42168
@@ -10585,6 +14053,8 @@ The tracking issue for this feature is: [#42168]
label: "stmt_expr_attributes",
description: r##"# `stmt_expr_attributes`
+Allows attributes on expressions and non-item statements.
+
The tracking issue for this feature is: [#15701]
[#15701]: https://github.com/rust-lang/rust/issues/15701
@@ -10599,6 +14069,8 @@ The tracking issue for this feature is: [#15701]
label: "str_as_str",
description: r##"# `str_as_str`
+
+
The tracking issue for this feature is: [#130366]
[#130366]: https://github.com/rust-lang/rust/issues/130366
@@ -10613,6 +14085,8 @@ The tracking issue for this feature is: [#130366]
label: "str_from_raw_parts",
description: r##"# `str_from_raw_parts`
+
+
The tracking issue for this feature is: [#119206]
[#119206]: https://github.com/rust-lang/rust/issues/119206
@@ -10627,6 +14101,8 @@ The tracking issue for this feature is: [#119206]
label: "str_from_utf16_endian",
description: r##"# `str_from_utf16_endian`
+
+
The tracking issue for this feature is: [#116258]
[#116258]: https://github.com/rust-lang/rust/issues/116258
@@ -10653,6 +14129,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "str_lines_remainder",
description: r##"# `str_lines_remainder`
+
+
The tracking issue for this feature is: [#77998]
[#77998]: https://github.com/rust-lang/rust/issues/77998
@@ -10667,6 +14145,8 @@ The tracking issue for this feature is: [#77998]
label: "str_split_inclusive_remainder",
description: r##"# `str_split_inclusive_remainder`
+
+
The tracking issue for this feature is: [#77998]
[#77998]: https://github.com/rust-lang/rust/issues/77998
@@ -10681,6 +14161,8 @@ The tracking issue for this feature is: [#77998]
label: "str_split_remainder",
description: r##"# `str_split_remainder`
+
+
The tracking issue for this feature is: [#77998]
[#77998]: https://github.com/rust-lang/rust/issues/77998
@@ -10695,23 +14177,11 @@ The tracking issue for this feature is: [#77998]
label: "str_split_whitespace_remainder",
description: r##"# `str_split_whitespace_remainder`
-The tracking issue for this feature is: [#77998]
-
-[#77998]: https://github.com/rust-lang/rust/issues/77998
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
- label: "strict_provenance_atomic_ptr",
- description: r##"# `strict_provenance_atomic_ptr`
-The tracking issue for this feature is: [#99108]
+The tracking issue for this feature is: [#77998]
-[#99108]: https://github.com/rust-lang/rust/issues/99108
+[#77998]: https://github.com/rust-lang/rust/issues/77998
------------------------
"##,
@@ -10748,64 +14218,46 @@ fn main() {
deny_since: None,
},
Lint {
- label: "string_deref_patterns",
- description: r##"# `string_deref_patterns`
+ label: "string_from_utf8_lossy_owned",
+ description: r##"# `string_from_utf8_lossy_owned`
-The tracking issue for this feature is: [#87121]
-[#87121]: https://github.com/rust-lang/rust/issues/87121
-------------------------
+The tracking issue for this feature is: [#129436]
-This feature permits pattern matching `String` to `&str` through [its `Deref` implementation].
+[#129436]: https://github.com/rust-lang/rust/issues/129436
-```rust
-#![feature(string_deref_patterns)]
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "string_into_chars",
+ description: r##"# `string_into_chars`
-pub enum Value {
- String(String),
- Number(u32),
-}
-pub fn is_it_the_answer(value: Value) -> bool {
- match value {
- Value::String("42") => true,
- Value::Number(42) => true,
- _ => false,
- }
-}
-```
-Without this feature other constructs such as match guards have to be used.
+The tracking issue for this feature is: [#133125]
-```rust
-# pub enum Value {
-# String(String),
-# Number(u32),
-# }
-#
-pub fn is_it_the_answer(value: Value) -> bool {
- match value {
- Value::String(s) if s == "42" => true,
- Value::Number(42) => true,
- _ => false,
- }
-}
-```
+[#133125]: https://github.com/rust-lang/rust/issues/133125
-[its `Deref` implementation]: https://doc.rust-lang.org/std/string/struct.String.html#impl-Deref-for-String
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "string_extend_from_within",
- description: r##"# `string_extend_from_within`
+ label: "string_remove_matches",
+ description: r##"# `string_remove_matches`
-The tracking issue for this feature is: [#103806]
-[#103806]: https://github.com/rust-lang/rust/issues/103806
+
+The tracking issue for this feature is: [#72826]
+
+[#72826]: https://github.com/rust-lang/rust/issues/72826
------------------------
"##,
@@ -10814,12 +14266,14 @@ The tracking issue for this feature is: [#103806]
deny_since: None,
},
Lint {
- label: "string_from_utf8_lossy_owned",
- description: r##"# `string_from_utf8_lossy_owned`
+ label: "string_replace_in_place",
+ description: r##"# `string_replace_in_place`
-The tracking issue for this feature is: [#129436]
-[#129436]: https://github.com/rust-lang/rust/issues/129436
+
+The tracking issue for this feature is: [#147949]
+
+[#147949]: https://github.com/rust-lang/rust/issues/147949
------------------------
"##,
@@ -10828,12 +14282,14 @@ The tracking issue for this feature is: [#129436]
deny_since: None,
},
Lint {
- label: "string_remove_matches",
- description: r##"# `string_remove_matches`
+ label: "strip_circumfix",
+ description: r##"# `strip_circumfix`
-The tracking issue for this feature is: [#72826]
-[#72826]: https://github.com/rust-lang/rust/issues/72826
+
+The tracking issue for this feature is: [#147946]
+
+[#147946]: https://github.com/rust-lang/rust/issues/147946
------------------------
"##,
@@ -10845,6 +14301,8 @@ The tracking issue for this feature is: [#72826]
label: "structural_match",
description: r##"# `structural_match`
+Allows using `#[structural_match]` which indicates that a type is structurally matchable. FIXME: Subsumed by trait `StructuralPartialEq`, cannot move to removed until a library feature with the same name exists.
+
The tracking issue for this feature is: [#31434]
[#31434]: https://github.com/rust-lang/rust/issues/31434
@@ -10859,6 +14317,8 @@ The tracking issue for this feature is: [#31434]
label: "substr_range",
description: r##"# `substr_range`
+
+
The tracking issue for this feature is: [#126769]
[#126769]: https://github.com/rust-lang/rust/issues/126769
@@ -10870,12 +14330,30 @@ The tracking issue for this feature is: [#126769]
deny_since: None,
},
Lint {
- label: "sync_unsafe_cell",
- description: r##"# `sync_unsafe_cell`
+ label: "super_let",
+ description: r##"# `super_let`
-The tracking issue for this feature is: [#95439]
+Allows `super let` statements.
-[#95439]: https://github.com/rust-lang/rust/issues/95439
+The tracking issue for this feature is: [#139076]
+
+[#139076]: https://github.com/rust-lang/rust/issues/139076
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "supertrait_item_shadowing",
+ description: r##"# `supertrait_item_shadowing`
+
+Allows subtrait items to shadow supertrait items.
+
+The tracking issue for this feature is: [#89151]
+
+[#89151]: https://github.com/rust-lang/rust/issues/89151
------------------------
"##,
@@ -10884,12 +14362,14 @@ The tracking issue for this feature is: [#95439]
deny_since: None,
},
Lint {
- label: "target_feature_11",
- description: r##"# `target_feature_11`
+ label: "sync_nonpoison",
+ description: r##"# `sync_nonpoison`
+
-The tracking issue for this feature is: [#69098]
-[#69098]: https://github.com/rust-lang/rust/issues/69098
+The tracking issue for this feature is: [#134645]
+
+[#134645]: https://github.com/rust-lang/rust/issues/134645
------------------------
"##,
@@ -10898,12 +14378,46 @@ The tracking issue for this feature is: [#69098]
deny_since: None,
},
Lint {
- label: "tbm_target_feature",
- description: r##"# `tbm_target_feature`
+ label: "sync_poison_mod",
+ description: r##"# `sync_poison_mod`
+
+
+
+The tracking issue for this feature is: [#134646]
+
+[#134646]: https://github.com/rust-lang/rust/issues/134646
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "sync_unsafe_cell",
+ description: r##"# `sync_unsafe_cell`
+
+
+
+The tracking issue for this feature is: [#95439]
+
+[#95439]: https://github.com/rust-lang/rust/issues/95439
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "target_feature_inline_always",
+ description: r##"# `target_feature_inline_always`
+
+Allows the use of target_feature when a function is marked inline(always).
-The tracking issue for this feature is: [#44839]
+The tracking issue for this feature is: [#145574]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+[#145574]: https://github.com/rust-lang/rust/issues/145574
------------------------
"##,
@@ -10915,6 +14429,8 @@ The tracking issue for this feature is: [#44839]
label: "tcp_deferaccept",
description: r##"# `tcp_deferaccept`
+
+
The tracking issue for this feature is: [#119639]
[#119639]: https://github.com/rust-lang/rust/issues/119639
@@ -10929,6 +14445,8 @@ The tracking issue for this feature is: [#119639]
label: "tcp_linger",
description: r##"# `tcp_linger`
+
+
The tracking issue for this feature is: [#88494]
[#88494]: https://github.com/rust-lang/rust/issues/88494
@@ -10940,12 +14458,14 @@ The tracking issue for this feature is: [#88494]
deny_since: None,
},
Lint {
- label: "tcp_quickack",
- description: r##"# `tcp_quickack`
+ label: "tcplistener_into_incoming",
+ description: r##"# `tcplistener_into_incoming`
+
+
-The tracking issue for this feature is: [#96256]
+The tracking issue for this feature is: [#88373]
-[#96256]: https://github.com/rust-lang/rust/issues/96256
+[#88373]: https://github.com/rust-lang/rust/issues/88373
------------------------
"##,
@@ -10954,12 +14474,12 @@ The tracking issue for this feature is: [#96256]
deny_since: None,
},
Lint {
- label: "tcplistener_into_incoming",
- description: r##"# `tcplistener_into_incoming`
+ label: "temporary_niche_types",
+ description: r##"# `temporary_niche_types`
-The tracking issue for this feature is: [#88373]
-[#88373]: https://github.com/rust-lang/rust/issues/88373
+
+This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
"##,
@@ -11133,9 +14653,25 @@ even when using either of the above.
deny_since: None,
},
Lint {
+ label: "test_incomplete_feature",
+ description: r##"# `test_incomplete_feature`
+
+Perma-unstable, only used to test the `incomplete_features` lint.
+
+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: "test_unstable_lint",
description: r##"# `test_unstable_lint`
+Added for testing unstable lints; perma-unstable.
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11148,6 +14684,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "thin_box",
description: r##"# `thin_box`
+
+
The tracking issue for this feature is: [#92791]
[#92791]: https://github.com/rust-lang/rust/issues/92791
@@ -11162,6 +14700,8 @@ The tracking issue for this feature is: [#92791]
label: "thread_id_value",
description: r##"# `thread_id_value`
+
+
The tracking issue for this feature is: [#67939]
[#67939]: https://github.com/rust-lang/rust/issues/67939
@@ -11176,6 +14716,8 @@ The tracking issue for this feature is: [#67939]
label: "thread_local",
description: r##"# `thread_local`
+Allows using `#[thread_local]` on `static` items.
+
The tracking issue for this feature is: [#29594]
[#29594]: https://github.com/rust-lang/rust/issues/29594
@@ -11202,6 +14744,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "thread_raw",
description: r##"# `thread_raw`
+
+
The tracking issue for this feature is: [#97523]
[#97523]: https://github.com/rust-lang/rust/issues/97523
@@ -11216,6 +14760,8 @@ The tracking issue for this feature is: [#97523]
label: "thread_sleep_until",
description: r##"# `thread_sleep_until`
+
+
The tracking issue for this feature is: [#113752]
[#113752]: https://github.com/rust-lang/rust/issues/113752
@@ -11230,6 +14776,8 @@ The tracking issue for this feature is: [#113752]
label: "thread_spawn_hook",
description: r##"# `thread_spawn_hook`
+
+
The tracking issue for this feature is: [#132951]
[#132951]: https://github.com/rust-lang/rust/issues/132951
@@ -11241,6 +14789,54 @@ The tracking issue for this feature is: [#132951]
deny_since: None,
},
Lint {
+ label: "time_saturating_systemtime",
+ description: r##"# `time_saturating_systemtime`
+
+
+
+The tracking issue for this feature is: [#151199]
+
+[#151199]: https://github.com/rust-lang/rust/issues/151199
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "time_systemtime_limits",
+ description: r##"# `time_systemtime_limits`
+
+
+
+The tracking issue for this feature is: [#149067]
+
+[#149067]: https://github.com/rust-lang/rust/issues/149067
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "titlecase",
+ description: r##"# `titlecase`
+
+
+
+The tracking issue for this feature is: [#153892]
+
+[#153892]: https://github.com/rust-lang/rust/issues/153892
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "trace_macros",
description: r##"# `trace_macros`
@@ -11287,20 +14883,6 @@ note: trace_macro
deny_since: None,
},
Lint {
- label: "track_path",
- description: r##"# `track_path`
-
-The tracking issue for this feature is: [#99515]
-
-[#99515]: https://github.com/rust-lang/rust/issues/99515
-
-------------------------
-"##,
- default_severity: Severity::Allow,
- warn_since: None,
- deny_since: None,
- },
- Lint {
label: "trait_alias",
description: r##"# `trait_alias`
@@ -11342,45 +14924,46 @@ pub fn main() {
deny_since: None,
},
Lint {
- label: "trait_upcasting",
- description: r##"# `trait_upcasting`
-
-The tracking issue for this feature is: [#65991]
+ label: "transmutability",
+ description: r##"# `transmutability`
-[#65991]: https://github.com/rust-lang/rust/issues/65991
-------------------------
-The `trait_upcasting` feature adds support for trait upcasting coercion. This allows a
-trait object of type `dyn Bar` to be cast to a trait object of type `dyn Foo`
-so long as `Bar: Foo`.
+The tracking issue for this feature is: [#99571]
-```rust,edition2018
-#![feature(trait_upcasting)]
+[#99571]: https://github.com/rust-lang/rust/issues/99571
-trait Foo {}
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "transmute_generic_consts",
+ description: r##"# `transmute_generic_consts`
-trait Bar: Foo {}
+Allows for transmuting between arrays with sizes that contain generic consts.
-impl Foo for i32 {}
+The tracking issue for this feature is: [#109929]
-impl<T: Foo + ?Sized> Bar for T {}
+[#109929]: https://github.com/rust-lang/rust/issues/109929
-let bar: &dyn Bar = &123;
-let foo: &dyn Foo = bar;
-```
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "transmutability",
- description: r##"# `transmutability`
+ label: "transmute_neo",
+ description: r##"# `transmute_neo`
-The tracking issue for this feature is: [#99571]
-[#99571]: https://github.com/rust-lang/rust/issues/99571
+
+The tracking issue for this feature is: [#155079]
+
+[#155079]: https://github.com/rust-lang/rust/issues/155079
------------------------
"##,
@@ -11389,12 +14972,14 @@ The tracking issue for this feature is: [#99571]
deny_since: None,
},
Lint {
- label: "transmute_generic_consts",
- description: r##"# `transmute_generic_consts`
+ label: "transmute_prefix",
+ description: r##"# `transmute_prefix`
-The tracking issue for this feature is: [#109929]
-[#109929]: https://github.com/rust-lang/rust/issues/109929
+
+The tracking issue for this feature is: [#155079]
+
+[#155079]: https://github.com/rust-lang/rust/issues/155079
------------------------
"##,
@@ -11493,9 +15078,27 @@ their application of these optimizations.
deny_since: None,
},
Lint {
+ label: "trim_prefix_suffix",
+ description: r##"# `trim_prefix_suffix`
+
+
+
+The tracking issue for this feature is: [#142312]
+
+[#142312]: https://github.com/rust-lang/rust/issues/142312
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "trivial_bounds",
description: r##"# `trivial_bounds`
+Allows inconsistent bounds in where clauses.
+
The tracking issue for this feature is: [#48214]
[#48214]: https://github.com/rust-lang/rust/issues/48214
@@ -11507,9 +15110,25 @@ The tracking issue for this feature is: [#48214]
deny_since: None,
},
Lint {
+ label: "trivial_clone",
+ description: r##"# `trivial_clone`
+
+
+
+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: "trusted_fused",
description: r##"# `trusted_fused`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11522,6 +15141,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "trusted_len",
description: r##"# `trusted_len`
+
+
The tracking issue for this feature is: [#37572]
[#37572]: https://github.com/rust-lang/rust/issues/37572
@@ -11536,6 +15157,8 @@ The tracking issue for this feature is: [#37572]
label: "trusted_len_next_unchecked",
description: r##"# `trusted_len_next_unchecked`
+
+
The tracking issue for this feature is: [#37572]
[#37572]: https://github.com/rust-lang/rust/issues/37572
@@ -11550,6 +15173,8 @@ The tracking issue for this feature is: [#37572]
label: "trusted_random_access",
description: r##"# `trusted_random_access`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11562,6 +15187,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "trusted_step",
description: r##"# `trusted_step`
+
+
The tracking issue for this feature is: [#85731]
[#85731]: https://github.com/rust-lang/rust/issues/85731
@@ -11573,6 +15200,22 @@ The tracking issue for this feature is: [#85731]
deny_since: None,
},
Lint {
+ label: "try_as_dyn",
+ description: r##"# `try_as_dyn`
+
+
+
+The tracking issue for this feature is: [#144361]
+
+[#144361]: https://github.com/rust-lang/rust/issues/144361
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "try_blocks",
description: r##"# `try_blocks`
@@ -11610,9 +15253,27 @@ assert!(result.is_err());
deny_since: None,
},
Lint {
+ label: "try_blocks_heterogeneous",
+ description: r##"# `try_blocks_heterogeneous`
+
+Allows using `try bikeshed TargetType {...}` expressions.
+
+The tracking issue for this feature is: [#149488]
+
+[#149488]: https://github.com/rust-lang/rust/issues/149488
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "try_find",
description: r##"# `try_find`
+
+
The tracking issue for this feature is: [#63178]
[#63178]: https://github.com/rust-lang/rust/issues/63178
@@ -11624,9 +15285,27 @@ The tracking issue for this feature is: [#63178]
deny_since: None,
},
Lint {
+ label: "try_from_int_error_kind",
+ description: r##"# `try_from_int_error_kind`
+
+
+
+The tracking issue for this feature is: [#153978]
+
+[#153978]: https://github.com/rust-lang/rust/issues/153978
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "try_reserve_kind",
description: r##"# `try_reserve_kind`
+
+
The tracking issue for this feature is: [#48043]
[#48043]: https://github.com/rust-lang/rust/issues/48043
@@ -11641,6 +15320,8 @@ The tracking issue for this feature is: [#48043]
label: "try_trait_v2",
description: r##"# `try_trait_v2`
+
+
The tracking issue for this feature is: [#84277]
[#84277]: https://github.com/rust-lang/rust/issues/84277
@@ -11655,6 +15336,8 @@ The tracking issue for this feature is: [#84277]
label: "try_trait_v2_residual",
description: r##"# `try_trait_v2_residual`
+
+
The tracking issue for this feature is: [#91285]
[#91285]: https://github.com/rust-lang/rust/issues/91285
@@ -11669,6 +15352,8 @@ The tracking issue for this feature is: [#91285]
label: "try_trait_v2_yeet",
description: r##"# `try_trait_v2_yeet`
+
+
The tracking issue for this feature is: [#96374]
[#96374]: https://github.com/rust-lang/rust/issues/96374
@@ -11683,6 +15368,8 @@ The tracking issue for this feature is: [#96374]
label: "try_with_capacity",
description: r##"# `try_with_capacity`
+
+
The tracking issue for this feature is: [#91913]
[#91913]: https://github.com/rust-lang/rust/issues/91913
@@ -11697,6 +15384,8 @@ The tracking issue for this feature is: [#91913]
label: "tuple_trait",
description: r##"# `tuple_trait`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11711,9 +15400,161 @@ This feature has no tracking issue, and is therefore likely internal to the comp
The tracking issue for this feature is: [#63063]
-[#63063]: https://github.com/rust-lang/rust/issues/63063
-
------------------------
+
+> This feature is not to be confused with [`trait_alias`] or [`impl_trait_in_assoc_type`].
+
+### What is `impl Trait`?
+
+`impl Trait` in return position is useful for declaring types that are constrained by traits, but whose concrete type should be hidden:
+
+```rust
+use std::fmt::Debug;
+
+fn new() -> impl Debug {
+ 42
+}
+
+fn main() {
+ let thing = new();
+ // What actually is a `thing`?
+ // No idea but we know it implements `Debug`, so we can debug print it
+ println!("{thing:?}");
+}
+```
+
+See the [reference] for more information about `impl Trait` in return position.
+
+### `type_alias_impl_trait`
+
+However, we might want to use an `impl Trait` in multiple locations but actually use the same concrete type everywhere while keeping it hidden.
+This can be useful in libraries where you want to hide implementation details.
+
+The `#[define_opaque]` attribute must be used to explicitly list opaque items constrained by the item it's on.
+
+```rust
+#![feature(type_alias_impl_trait)]
+# #![allow(unused_variables, dead_code)]
+trait Trait {}
+
+struct MyType;
+
+impl Trait for MyType {}
+
+type Alias = impl Trait;
+
+#[define_opaque(Alias)] // To constrain the type alias to `MyType`
+fn new() -> Alias {
+ MyType
+}
+
+#[define_opaque(Alias)] // So we can name the concrete type inside this item
+fn main() {
+ let thing: MyType = new();
+}
+
+// It can be a part of a struct too
+struct HaveAlias {
+ stuff: String,
+ thing: Alias,
+}
+```
+
+In this example, the concrete type referred to by `Alias` is guaranteed to be the same wherever `Alias` occurs.
+
+> Originally this feature included type aliases as an associated type of a trait. In [#110237] this was split off to [`impl_trait_in_assoc_type`].
+
+### `type_alias_impl_trait` in argument position.
+
+Note that using `Alias` as an argument type is *not* the same as argument-position `impl Trait`, as `Alias` refers to a unique type, whereas the concrete type for argument-position `impl Trait` is chosen by the caller.
+
+```rust
+# #![feature(type_alias_impl_trait)]
+# #![allow(unused_variables)]
+# pub mod x {
+# pub trait Trait {}
+#
+# struct MyType;
+#
+# impl Trait for MyType {}
+#
+# pub type Alias = impl Trait;
+#
+# #[define_opaque(Alias)]
+# pub fn new() -> Alias {
+# MyType
+# }
+# }
+# use x::*;
+// this...
+pub fn take_alias(x: Alias) {
+ // ...
+}
+
+// ...is *not* the same as
+pub fn take_impl(x: impl Trait) {
+ // ...
+}
+# fn main(){}
+```
+
+```rust,compile_fail,E0308
+# #![feature(type_alias_impl_trait)]
+# #![allow(unused_variables)]
+# pub mod x {
+# pub trait Trait {}
+#
+# struct MyType;
+#
+# impl Trait for MyType {}
+#
+# pub type Alias = impl Trait;
+#
+# #[define_opaque(Alias)]
+# pub fn new() -> Alias {
+# MyType
+# }
+# }
+# use x::*;
+# pub fn take_alias(x: Alias) {
+# // ...
+# }
+#
+# pub fn take_impl(x: impl Trait) {
+# // ...
+# }
+#
+// a user's crate using the trait and type alias
+struct UserType;
+impl Trait for UserType {}
+
+# fn main(){
+let x = UserType;
+take_alias(x);
+// ERROR expected opaque type, found `UserType`
+// this function *actually* takes a `MyType` as is constrained in `new`
+
+let x = UserType;
+take_impl(x);
+// OK
+
+let x = new();
+take_alias(x);
+// OK
+
+let x = new();
+take_impl(x);
+// OK
+# }
+```
+
+Note that the user cannot use `#[define_opaque(Alias)]` to reify the opaque type because only the crate where the type alias is declared may do so. But if this happened in the same crate and the opaque type was reified, they'd get a familiar error: "expected `MyType`, got `UserType`".
+
+[#63063]: https://github.com/rust-lang/rust/issues/63063
+[#110237]: https://github.com/rust-lang/rust/pull/110237
+[reference]: https://doc.rust-lang.org/stable/reference/types/impl-trait.html#abstract-return-types
+[`trait_alias`]: ./trait-alias.md
+[`impl_trait_in_assoc_type`]: ./impl-trait-in-assoc-type.md
"##,
default_severity: Severity::Allow,
warn_since: None,
@@ -11723,6 +15564,8 @@ The tracking issue for this feature is: [#63063]
label: "type_ascription",
description: r##"# `type_ascription`
+
+
The tracking issue for this feature is: [#23416]
[#23416]: https://github.com/rust-lang/rust/issues/23416
@@ -11774,9 +15617,27 @@ fn main () {
deny_since: None,
},
Lint {
+ label: "type_info",
+ description: r##"# `type_info`
+
+
+
+The tracking issue for this feature is: [#146922]
+
+[#146922]: https://github.com/rust-lang/rust/issues/146922
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "ub_checks",
description: r##"# `ub_checks`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11789,6 +15650,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "uefi_std",
description: r##"# `uefi_std`
+
+
The tracking issue for this feature is: [#100499]
[#100499]: https://github.com/rust-lang/rust/issues/100499
@@ -11800,12 +15663,30 @@ The tracking issue for this feature is: [#100499]
deny_since: None,
},
Lint {
- label: "unbounded_shifts",
- description: r##"# `unbounded_shifts`
+ label: "uint_carryless_mul",
+ description: r##"# `uint_carryless_mul`
-The tracking issue for this feature is: [#129375]
-[#129375]: https://github.com/rust-lang/rust/issues/129375
+
+The tracking issue for this feature is: [#152080]
+
+[#152080]: https://github.com/rust-lang/rust/issues/152080
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "uint_gather_scatter_bits",
+ description: r##"# `uint_gather_scatter_bits`
+
+
+
+The tracking issue for this feature is: [#149069]
+
+[#149069]: https://github.com/rust-lang/rust/issues/149069
------------------------
"##,
@@ -11849,6 +15730,8 @@ fn main() {}
label: "unicode_internals",
description: r##"# `unicode_internals`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -11861,6 +15744,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "unique_rc_arc",
description: r##"# `unique_rc_arc`
+
+
The tracking issue for this feature is: [#112566]
[#112566]: https://github.com/rust-lang/rust/issues/112566
@@ -11875,6 +15760,8 @@ The tracking issue for this feature is: [#112566]
label: "unix_file_vectored_at",
description: r##"# `unix_file_vectored_at`
+
+
The tracking issue for this feature is: [#89517]
[#89517]: https://github.com/rust-lang/rust/issues/89517
@@ -11886,9 +15773,43 @@ The tracking issue for this feature is: [#89517]
deny_since: None,
},
Lint {
+ label: "unix_mkfifo",
+ description: r##"# `unix_mkfifo`
+
+
+
+The tracking issue for this feature is: [#139324]
+
+[#139324]: https://github.com/rust-lang/rust/issues/139324
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "unix_send_signal",
+ description: r##"# `unix_send_signal`
+
+
+
+The tracking issue for this feature is: [#141975]
+
+[#141975]: https://github.com/rust-lang/rust/issues/141975
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "unix_set_mark",
description: r##"# `unix_set_mark`
+
+
The tracking issue for this feature is: [#96467]
[#96467]: https://github.com/rust-lang/rust/issues/96467
@@ -11903,6 +15824,8 @@ The tracking issue for this feature is: [#96467]
label: "unix_socket_ancillary_data",
description: r##"# `unix_socket_ancillary_data`
+
+
The tracking issue for this feature is: [#76915]
[#76915]: https://github.com/rust-lang/rust/issues/76915
@@ -11914,9 +15837,27 @@ The tracking issue for this feature is: [#76915]
deny_since: None,
},
Lint {
+ label: "unix_socket_exclbind",
+ description: r##"# `unix_socket_exclbind`
+
+
+
+The tracking issue for this feature is: [#123481]
+
+[#123481]: https://github.com/rust-lang/rust/issues/123481
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "unix_socket_peek",
description: r##"# `unix_socket_peek`
+
+
The tracking issue for this feature is: [#76923]
[#76923]: https://github.com/rust-lang/rust/issues/76923
@@ -11931,7 +15872,11 @@ The tracking issue for this feature is: [#76923]
label: "unqualified_local_imports",
description: r##"# `unqualified_local_imports`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+Helps with formatting for `group_imports = "StdExternalCrate"`.
+
+The tracking issue for this feature is: [#138299]
+
+[#138299]: https://github.com/rust-lang/rust/issues/138299
------------------------
"##,
@@ -11940,12 +15885,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "unsafe_fields",
- description: r##"# `unsafe_fields`
+ label: "unsafe_binders",
+ description: r##"# `unsafe_binders`
-The tracking issue for this feature is: [#132922]
+Allows using `unsafe<'a> &'a T` unsafe binder types.
-[#132922]: https://github.com/rust-lang/rust/issues/132922
+The tracking issue for this feature is: [#130516]
+
+[#130516]: https://github.com/rust-lang/rust/issues/130516
------------------------
"##,
@@ -11954,10 +15901,14 @@ The tracking issue for this feature is: [#132922]
deny_since: None,
},
Lint {
- label: "unsafe_pin_internals",
- description: r##"# `unsafe_pin_internals`
+ label: "unsafe_cell_access",
+ description: r##"# `unsafe_cell_access`
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
+
+
+The tracking issue for this feature is: [#136327]
+
+[#136327]: https://github.com/rust-lang/rust/issues/136327
------------------------
"##,
@@ -11966,12 +15917,14 @@ This feature has no tracking issue, and is therefore likely internal to the comp
deny_since: None,
},
Lint {
- label: "unsigned_is_multiple_of",
- description: r##"# `unsigned_is_multiple_of`
+ label: "unsafe_fields",
+ description: r##"# `unsafe_fields`
-The tracking issue for this feature is: [#128101]
+Allows declaring fields `unsafe`.
-[#128101]: https://github.com/rust-lang/rust/issues/128101
+The tracking issue for this feature is: [#132922]
+
+[#132922]: https://github.com/rust-lang/rust/issues/132922
------------------------
"##,
@@ -11980,12 +15933,14 @@ The tracking issue for this feature is: [#128101]
deny_since: None,
},
Lint {
- label: "unsigned_nonzero_div_ceil",
- description: r##"# `unsigned_nonzero_div_ceil`
+ label: "unsafe_pinned",
+ description: r##"# `unsafe_pinned`
+
-The tracking issue for this feature is: [#132968]
-[#132968]: https://github.com/rust-lang/rust/issues/132968
+The tracking issue for this feature is: [#125735]
+
+[#125735]: https://github.com/rust-lang/rust/issues/125735
------------------------
"##,
@@ -11994,12 +15949,14 @@ The tracking issue for this feature is: [#132968]
deny_since: None,
},
Lint {
- label: "unsigned_signed_diff",
- description: r##"# `unsigned_signed_diff`
+ label: "unsafe_unpin",
+ description: r##"# `unsafe_unpin`
+
-The tracking issue for this feature is: [#126041]
-[#126041]: https://github.com/rust-lang/rust/issues/126041
+The tracking issue for this feature is: [#125735]
+
+[#125735]: https://github.com/rust-lang/rust/issues/125735
------------------------
"##,
@@ -12011,6 +15968,8 @@ The tracking issue for this feature is: [#126041]
label: "unsize",
description: r##"# `unsize`
+
+
The tracking issue for this feature is: [#18598]
[#18598]: https://github.com/rust-lang/rust/issues/18598
@@ -12025,6 +15984,8 @@ The tracking issue for this feature is: [#18598]
label: "unsized_const_params",
description: r##"# `unsized_const_params`
+Allows const generic parameters to be defined with types that are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
+
The tracking issue for this feature is: [#95174]
[#95174]: https://github.com/rust-lang/rust/issues/95174
@@ -12039,6 +16000,8 @@ The tracking issue for this feature is: [#95174]
label: "unsized_fn_params",
description: r##"# `unsized_fn_params`
+Allows unsized fn parameters.
+
The tracking issue for this feature is: [#48055]
[#48055]: https://github.com/rust-lang/rust/issues/48055
@@ -12050,194 +16013,170 @@ The tracking issue for this feature is: [#48055]
deny_since: None,
},
Lint {
- label: "unsized_locals",
- description: r##"# `unsized_locals`
+ label: "unwrap_infallible",
+ description: r##"# `unwrap_infallible`
-The tracking issue for this feature is: [#48055]
-[#48055]: https://github.com/rust-lang/rust/issues/48055
+
+The tracking issue for this feature is: [#61695]
+
+[#61695]: https://github.com/rust-lang/rust/issues/61695
------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "update_panic_count",
+ description: r##"# `update_panic_count`
-This implements [RFC1909]. When turned on, you can have unsized arguments and locals:
+This feature is internal to the Rust compiler and is not intended for general use.
-[RFC1909]: https://github.com/rust-lang/rfcs/blob/master/text/1909-unsized-rvalues.md
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "used_with_arg",
+ description: r##"# `used_with_arg`
-```rust
-#![allow(incomplete_features)]
-#![feature(unsized_locals, unsized_fn_params)]
+Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
-use std::any::Any;
+The tracking issue for this feature is: [#93798]
-fn main() {
- let x: Box<dyn Any> = Box::new(42);
- let x: dyn Any = *x;
- // ^ unsized local variable
- // ^^ unsized temporary
- foo(x);
-}
+[#93798]: https://github.com/rust-lang/rust/issues/93798
-fn foo(_: dyn Any) {}
-// ^^^^^^ unsized argument
-```
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "utf16_extra",
+ description: r##"# `utf16_extra`
-The RFC still forbids the following unsized expressions:
-```rust,compile_fail
-#![feature(unsized_locals)]
-use std::any::Any;
+The tracking issue for this feature is: [#94919]
-struct MyStruct<T: ?Sized> {
- content: T,
-}
+[#94919]: https://github.com/rust-lang/rust/issues/94919
-struct MyTupleStruct<T: ?Sized>(T);
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "variant_count",
+ description: r##"# `variant_count`
-fn answer() -> Box<dyn Any> {
- Box::new(42)
-}
-fn main() {
- // You CANNOT have unsized statics.
- static X: dyn Any = *answer(); // ERROR
- const Y: dyn Any = *answer(); // ERROR
-
- // You CANNOT have struct initialized unsized.
- MyStruct { content: *answer() }; // ERROR
- MyTupleStruct(*answer()); // ERROR
- (42, *answer()); // ERROR
-
- // You CANNOT have unsized return types.
- fn my_function() -> dyn Any { *answer() } // ERROR
-
- // You CAN have unsized local variables...
- let mut x: dyn Any = *answer(); // OK
- // ...but you CANNOT reassign to them.
- x = *answer(); // ERROR
-
- // You CANNOT even initialize them separately.
- let y: dyn Any; // OK
- y = *answer(); // ERROR
-
- // Not mentioned in the RFC, but by-move captured variables are also Sized.
- let x: dyn Any = *answer();
- (move || { // ERROR
- let y = x;
- })();
-
- // You CAN create a closure with unsized arguments,
- // but you CANNOT call it.
- // This is an implementation detail and may be changed in the future.
- let f = |x: dyn Any| {};
- f(*answer()); // ERROR
-}
-```
-## By-value trait objects
+The tracking issue for this feature is: [#73662]
-With this feature, you can have by-value `self` arguments without `Self: Sized` bounds.
+[#73662]: https://github.com/rust-lang/rust/issues/73662
-```rust
-#![feature(unsized_fn_params)]
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "vec_deque_extract_if",
+ description: r##"# `vec_deque_extract_if`
-trait Foo {
- fn foo(self) {}
-}
-impl<T: ?Sized> Foo for T {}
-fn main() {
- let slice: Box<[i32]> = Box::new([1, 2, 3]);
- <[i32] as Foo>::foo(*slice);
-}
-```
+The tracking issue for this feature is: [#147750]
-And `Foo` will also be object-safe.
+[#147750]: https://github.com/rust-lang/rust/issues/147750
-```rust
-#![feature(unsized_fn_params)]
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "vec_deque_iter_as_slices",
+ description: r##"# `vec_deque_iter_as_slices`
-trait Foo {
- fn foo(self) {}
-}
-impl<T: ?Sized> Foo for T {}
-fn main () {
- let slice: Box<dyn Foo> = Box::new([1, 2, 3]);
- // doesn't compile yet
- <dyn Foo as Foo>::foo(*slice);
-}
-```
+The tracking issue for this feature is: [#123947]
-One of the objectives of this feature is to allow `Box<dyn FnOnce>`.
+[#123947]: https://github.com/rust-lang/rust/issues/123947
-## Variable length arrays
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "vec_deque_truncate_front",
+ description: r##"# `vec_deque_truncate_front`
-The RFC also describes an extension to the array literal syntax: `[e; dyn n]`. In the syntax, `n` isn't necessarily a constant expression. The array is dynamically allocated on the stack and has the type of `[T]`, instead of `[T; n]`.
-```rust,ignore (not-yet-implemented)
-#![feature(unsized_locals)]
-fn mergesort<T: Ord>(a: &mut [T]) {
- let mut tmp = [T; dyn a.len()];
- // ...
-}
+The tracking issue for this feature is: [#140667]
-fn main() {
- let mut a = [3, 1, 5, 6];
- mergesort(&mut a);
- assert_eq!(a, [1, 3, 5, 6]);
-}
-```
+[#140667]: https://github.com/rust-lang/rust/issues/140667
-VLAs are not implemented yet. The syntax isn't final, either. We may need an alternative syntax for Rust 2015 because, in Rust 2015, expressions like `[e; dyn(1)]` would be ambiguous. One possible alternative proposed in the RFC is `[e; n]`: if `n` captures one or more local variables, then it is considered as `[e; dyn n]`.
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "vec_fallible_shrink",
+ description: r##"# `vec_fallible_shrink`
-## Advisory on stack usage
-It's advised not to casually use the `#![feature(unsized_locals)]` feature. Typical use-cases are:
-- When you need a by-value trait objects.
-- When you really need a fast allocation of small temporary arrays.
+The tracking issue for this feature is: [#152350]
-Another pitfall is repetitive allocation and temporaries. Currently the compiler simply extends the stack frame every time it encounters an unsized assignment. So for example, the code
+[#152350]: https://github.com/rust-lang/rust/issues/152350
-```rust
-#![feature(unsized_locals)]
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
+ label: "vec_from_fn",
+ description: r##"# `vec_from_fn`
-fn main() {
- let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
- let _x = {{{{{{{{{{*x}}}}}}}}}};
-}
-```
-and the code
-```rust
-#![feature(unsized_locals)]
+The tracking issue for this feature is: [#149698]
-fn main() {
- for _ in 0..10 {
- let x: Box<[i32]> = Box::new([1, 2, 3, 4, 5]);
- let _x = *x;
- }
-}
-```
+[#149698]: https://github.com/rust-lang/rust/issues/149698
-will unnecessarily extend the stack frame.
+------------------------
"##,
default_severity: Severity::Allow,
warn_since: None,
deny_since: None,
},
Lint {
- label: "unwrap_infallible",
- description: r##"# `unwrap_infallible`
+ label: "vec_into_chunks",
+ description: r##"# `vec_into_chunks`
-The tracking issue for this feature is: [#61695]
-[#61695]: https://github.com/rust-lang/rust/issues/61695
+
+The tracking issue for this feature is: [#142137]
+
+[#142137]: https://github.com/rust-lang/rust/issues/142137
------------------------
"##,
@@ -12246,10 +16185,14 @@ The tracking issue for this feature is: [#61695]
deny_since: None,
},
Lint {
- label: "update_panic_count",
- description: r##"# `update_panic_count`
+ label: "vec_peek_mut",
+ description: r##"# `vec_peek_mut`
-This feature is internal to the Rust compiler and is not intended for general use.
+
+
+The tracking issue for this feature is: [#122742]
+
+[#122742]: https://github.com/rust-lang/rust/issues/122742
------------------------
"##,
@@ -12258,12 +16201,14 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
- label: "used_with_arg",
- description: r##"# `used_with_arg`
+ label: "vec_push_within_capacity",
+ description: r##"# `vec_push_within_capacity`
-The tracking issue for this feature is: [#93798]
-[#93798]: https://github.com/rust-lang/rust/issues/93798
+
+The tracking issue for this feature is: [#100486]
+
+[#100486]: https://github.com/rust-lang/rust/issues/100486
------------------------
"##,
@@ -12272,12 +16217,14 @@ The tracking issue for this feature is: [#93798]
deny_since: None,
},
Lint {
- label: "utf16_extra",
- description: r##"# `utf16_extra`
+ label: "vec_recycle",
+ description: r##"# `vec_recycle`
-The tracking issue for this feature is: [#94919]
-[#94919]: https://github.com/rust-lang/rust/issues/94919
+
+The tracking issue for this feature is: [#148227]
+
+[#148227]: https://github.com/rust-lang/rust/issues/148227
------------------------
"##,
@@ -12286,12 +16233,14 @@ The tracking issue for this feature is: [#94919]
deny_since: None,
},
Lint {
- label: "variant_count",
- description: r##"# `variant_count`
+ label: "vec_split_at_spare",
+ description: r##"# `vec_split_at_spare`
-The tracking issue for this feature is: [#73662]
-[#73662]: https://github.com/rust-lang/rust/issues/73662
+
+The tracking issue for this feature is: [#81944]
+
+[#81944]: https://github.com/rust-lang/rust/issues/81944
------------------------
"##,
@@ -12300,12 +16249,14 @@ The tracking issue for this feature is: [#73662]
deny_since: None,
},
Lint {
- label: "vec_deque_iter_as_slices",
- description: r##"# `vec_deque_iter_as_slices`
+ label: "vec_try_remove",
+ description: r##"# `vec_try_remove`
-The tracking issue for this feature is: [#123947]
-[#123947]: https://github.com/rust-lang/rust/issues/123947
+
+The tracking issue for this feature is: [#146954]
+
+[#146954]: https://github.com/rust-lang/rust/issues/146954
------------------------
"##,
@@ -12314,12 +16265,14 @@ The tracking issue for this feature is: [#123947]
deny_since: None,
},
Lint {
- label: "vec_into_raw_parts",
- description: r##"# `vec_into_raw_parts`
+ label: "waker_fn",
+ description: r##"# `waker_fn`
-The tracking issue for this feature is: [#65816]
-[#65816]: https://github.com/rust-lang/rust/issues/65816
+
+The tracking issue for this feature is: [#149580]
+
+[#149580]: https://github.com/rust-lang/rust/issues/149580
------------------------
"##,
@@ -12328,12 +16281,14 @@ The tracking issue for this feature is: [#65816]
deny_since: None,
},
Lint {
- label: "vec_pop_if",
- description: r##"# `vec_pop_if`
+ label: "waker_from_fn_ptr",
+ description: r##"# `waker_from_fn_ptr`
+
+
-The tracking issue for this feature is: [#122741]
+The tracking issue for this feature is: [#148457]
-[#122741]: https://github.com/rust-lang/rust/issues/122741
+[#148457]: https://github.com/rust-lang/rust/issues/148457
------------------------
"##,
@@ -12342,12 +16297,14 @@ The tracking issue for this feature is: [#122741]
deny_since: None,
},
Lint {
- label: "vec_push_within_capacity",
- description: r##"# `vec_push_within_capacity`
+ label: "wasi_ext",
+ description: r##"# `wasi_ext`
-The tracking issue for this feature is: [#100486]
-[#100486]: https://github.com/rust-lang/rust/issues/100486
+
+The tracking issue for this feature is: [#71213]
+
+[#71213]: https://github.com/rust-lang/rust/issues/71213
------------------------
"##,
@@ -12356,12 +16313,14 @@ The tracking issue for this feature is: [#100486]
deny_since: None,
},
Lint {
- label: "vec_split_at_spare",
- description: r##"# `vec_split_at_spare`
+ label: "wasm_target_feature",
+ description: r##"# `wasm_target_feature`
-The tracking issue for this feature is: [#81944]
+Target features on wasm.
-[#81944]: https://github.com/rust-lang/rust/issues/81944
+The tracking issue for this feature is: [#150260]
+
+[#150260]: https://github.com/rust-lang/rust/issues/150260
------------------------
"##,
@@ -12370,12 +16329,14 @@ The tracking issue for this feature is: [#81944]
deny_since: None,
},
Lint {
- label: "wasi_ext",
- description: r##"# `wasi_ext`
+ label: "where_clause_attrs",
+ description: r##"# `where_clause_attrs`
-The tracking issue for this feature is: [#71213]
+Allows use of attributes in `where` clauses.
-[#71213]: https://github.com/rust-lang/rust/issues/71213
+The tracking issue for this feature is: [#115590]
+
+[#115590]: https://github.com/rust-lang/rust/issues/115590
------------------------
"##,
@@ -12384,12 +16345,14 @@ The tracking issue for this feature is: [#71213]
deny_since: None,
},
Lint {
- label: "wasm_target_feature",
- description: r##"# `wasm_target_feature`
+ label: "widening_mul",
+ description: r##"# `widening_mul`
+
-The tracking issue for this feature is: [#44839]
-[#44839]: https://github.com/rust-lang/rust/issues/44839
+The tracking issue for this feature is: [#152016]
+
+[#152016]: https://github.com/rust-lang/rust/issues/152016
------------------------
"##,
@@ -12401,6 +16364,8 @@ The tracking issue for this feature is: [#44839]
label: "windows_by_handle",
description: r##"# `windows_by_handle`
+
+
The tracking issue for this feature is: [#63010]
[#63010]: https://github.com/rust-lang/rust/issues/63010
@@ -12427,6 +16392,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "windows_change_time",
description: r##"# `windows_change_time`
+
+
The tracking issue for this feature is: [#121478]
[#121478]: https://github.com/rust-lang/rust/issues/121478
@@ -12438,6 +16405,22 @@ The tracking issue for this feature is: [#121478]
deny_since: None,
},
Lint {
+ label: "windows_freeze_file_times",
+ description: r##"# `windows_freeze_file_times`
+
+
+
+The tracking issue for this feature is: [#149715]
+
+[#149715]: https://github.com/rust-lang/rust/issues/149715
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "windows_handle",
description: r##"# `windows_handle`
@@ -12465,6 +16448,8 @@ This feature is internal to the Rust compiler and is not intended for general us
label: "windows_process_exit_code_from",
description: r##"# `windows_process_exit_code_from`
+
+
The tracking issue for this feature is: [#111688]
[#111688]: https://github.com/rust-lang/rust/issues/111688
@@ -12479,6 +16464,8 @@ The tracking issue for this feature is: [#111688]
label: "windows_process_extensions_async_pipes",
description: r##"# `windows_process_extensions_async_pipes`
+
+
The tracking issue for this feature is: [#98289]
[#98289]: https://github.com/rust-lang/rust/issues/98289
@@ -12493,6 +16480,8 @@ The tracking issue for this feature is: [#98289]
label: "windows_process_extensions_force_quotes",
description: r##"# `windows_process_extensions_force_quotes`
+
+
The tracking issue for this feature is: [#82227]
[#82227]: https://github.com/rust-lang/rust/issues/82227
@@ -12504,9 +16493,27 @@ The tracking issue for this feature is: [#82227]
deny_since: None,
},
Lint {
+ label: "windows_process_extensions_inherit_handles",
+ description: r##"# `windows_process_extensions_inherit_handles`
+
+
+
+The tracking issue for this feature is: [#146407]
+
+[#146407]: https://github.com/rust-lang/rust/issues/146407
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "windows_process_extensions_main_thread_handle",
description: r##"# `windows_process_extensions_main_thread_handle`
+
+
The tracking issue for this feature is: [#96723]
[#96723]: https://github.com/rust-lang/rust/issues/96723
@@ -12521,6 +16528,8 @@ The tracking issue for this feature is: [#96723]
label: "windows_process_extensions_raw_attribute",
description: r##"# `windows_process_extensions_raw_attribute`
+
+
The tracking issue for this feature is: [#114854]
[#114854]: https://github.com/rust-lang/rust/issues/114854
@@ -12535,6 +16544,8 @@ The tracking issue for this feature is: [#114854]
label: "windows_process_extensions_show_window",
description: r##"# `windows_process_extensions_show_window`
+
+
The tracking issue for this feature is: [#127544]
[#127544]: https://github.com/rust-lang/rust/issues/127544
@@ -12546,6 +16557,22 @@ The tracking issue for this feature is: [#127544]
deny_since: None,
},
Lint {
+ label: "windows_process_extensions_startupinfo",
+ description: r##"# `windows_process_extensions_startupinfo`
+
+
+
+The tracking issue for this feature is: [#141010]
+
+[#141010]: https://github.com/rust-lang/rust/issues/141010
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "windows_stdio",
description: r##"# `windows_stdio`
@@ -12558,9 +16585,27 @@ This feature is internal to the Rust compiler and is not intended for general us
deny_since: None,
},
Lint {
+ label: "windows_unix_domain_sockets",
+ description: r##"# `windows_unix_domain_sockets`
+
+
+
+The tracking issue for this feature is: [#150487]
+
+[#150487]: https://github.com/rust-lang/rust/issues/150487
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "with_negative_coherence",
description: r##"# `with_negative_coherence`
+Use for stable + negative coherence and strict coherence depending on trait's rustc_strict_coherence value.
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -12573,6 +16618,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
label: "wrapping_int_impl",
description: r##"# `wrapping_int_impl`
+
+
The tracking issue for this feature is: [#32463]
[#32463]: https://github.com/rust-lang/rust/issues/32463
@@ -12587,6 +16634,8 @@ The tracking issue for this feature is: [#32463]
label: "wrapping_next_power_of_two",
description: r##"# `wrapping_next_power_of_two`
+
+
The tracking issue for this feature is: [#32463]
[#32463]: https://github.com/rust-lang/rust/issues/32463
@@ -12601,6 +16650,8 @@ The tracking issue for this feature is: [#32463]
label: "write_all_vectored",
description: r##"# `write_all_vectored`
+
+
The tracking issue for this feature is: [#70436]
[#70436]: https://github.com/rust-lang/rust/issues/70436
@@ -12612,9 +16663,25 @@ The tracking issue for this feature is: [#70436]
deny_since: None,
},
Lint {
+ label: "wtf8_internals",
+ description: r##"# `wtf8_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: "x86_amx_intrinsics",
description: r##"# `x86_amx_intrinsics`
+Allows use of x86 `AMX` target-feature attributes and intrinsics
+
The tracking issue for this feature is: [#126622]
[#126622]: https://github.com/rust-lang/rust/issues/126622
@@ -12626,9 +16693,27 @@ The tracking issue for this feature is: [#126622]
deny_since: None,
},
Lint {
+ label: "x87_target_feature",
+ description: r##"# `x87_target_feature`
+
+The x87 target feature on x86.
+
+The tracking issue for this feature is: [#150261]
+
+[#150261]: https://github.com/rust-lang/rust/issues/150261
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
+ Lint {
label: "xop_target_feature",
description: r##"# `xop_target_feature`
+Allows use of the `xop` target-feature
+
The tracking issue for this feature is: [#127208]
[#127208]: https://github.com/rust-lang/rust/issues/127208
@@ -12643,6 +16728,8 @@ The tracking issue for this feature is: [#127208]
label: "yeet_desugar_details",
description: r##"# `yeet_desugar_details`
+
+
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
------------------------
@@ -12684,6 +16771,22 @@ assert_eq!(bar(), None);
warn_since: None,
deny_since: None,
},
+ Lint {
+ label: "yield_expr",
+ description: r##"# `yield_expr`
+
+
+
+The tracking issue for this feature is: [#43122]
+
+[#43122]: https://github.com/rust-lang/rust/issues/43122
+
+------------------------
+"##,
+ default_severity: Severity::Allow,
+ warn_since: None,
+ deny_since: None,
+ },
];
pub const CLIPPY_LINTS: &[Lint] = &[
@@ -14967,7 +19070,7 @@ cannot be represented as the underlying type without loss."##,
},
Lint {
label: "clippy::manual_bits",
- description: r##"Checks for usage of `size_of::<T>() * 8` when
+ description: r##"Checks for usage of `std::mem::size_of::<T>() * 8` when
`T::BITS` is available."##,
default_severity: Severity::Allow,
warn_since: None,
@@ -17309,7 +21412,7 @@ count of elements of type `T`"##,
},
Lint {
label: "clippy::size_of_ref",
- description: r##"Checks for calls to `size_of_val()` where the argument is
+ description: r##"Checks for calls to `std::mem::size_of_val()` where the argument is
a reference to a reference."##,
default_severity: Severity::Allow,
warn_since: None,
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 1c48527027..9018552afb 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -8,8 +8,10 @@ use hir::{
SemanticsScope, Trait, Type,
};
use itertools::Itertools;
+use parser::SyntaxKind;
use rustc_hash::{FxHashMap, FxHashSet};
use smallvec::{SmallVec, smallvec};
+use stdx::never;
use syntax::{
AstNode, SyntaxNode,
ast::{self, HasName, make},
@@ -61,6 +63,105 @@ pub struct TraitImportCandidate<'db> {
pub assoc_item_name: NameToImport,
}
+#[derive(Debug)]
+struct PathDefinitionKinds {
+ modules: bool,
+ bang_macros: bool,
+ // FIXME: Distinguish between attr and derive macros.
+ attr_macros: bool,
+ value_namespace: bool,
+ type_namespace: bool,
+ /// Unions, record structs and record enum variants. Note that unions and structs
+ /// can also be enabled by `type_namespace` (either works).
+ records: bool,
+ /// Tuple structs and tuple enum variants. Both are also controlled by `value_namespace`
+ /// (either works). Structs are also covered by `type_namespace`.
+ tuple_structs: bool,
+ /// Structs, enum variants and consts.
+ structs_and_consts: bool,
+}
+
+impl PathDefinitionKinds {
+ const ALL_DISABLED: Self = Self {
+ modules: false,
+ bang_macros: false,
+ attr_macros: false,
+ value_namespace: false,
+ type_namespace: false,
+ records: false,
+ tuple_structs: false,
+ structs_and_consts: false,
+ };
+ const ALL_ENABLED: Self = Self {
+ modules: true,
+ bang_macros: true,
+ attr_macros: true,
+ value_namespace: true,
+ type_namespace: true,
+ records: true,
+ tuple_structs: true,
+ structs_and_consts: true,
+ };
+ // While a path pattern only allows unit structs/enum variants, parentheses/braces may be written later.
+ const PATH_PAT_KINDS: PathDefinitionKinds =
+ Self { structs_and_consts: true, bang_macros: true, ..Self::ALL_DISABLED };
+
+ fn deduce_from_path(path: &ast::Path, exact: bool) -> Self {
+ let Some(parent) = path.syntax().parent() else {
+ return Self::ALL_ENABLED;
+ };
+ let mut result = match parent.kind() {
+ // When there are following segments, it can be a type (with a method) or a module.
+ // Technically, a type can only have up to 2 segments following (an associated type
+ // then a method), but most paths are shorter than 3 segments anyway, and we'll also
+ // validate that the following segment resolve.
+ SyntaxKind::PATH => Self { modules: true, type_namespace: true, ..Self::ALL_DISABLED },
+ SyntaxKind::MACRO_CALL => Self { bang_macros: true, ..Self::ALL_DISABLED },
+ SyntaxKind::PATH_META | SyntaxKind::KEY_VALUE_META | SyntaxKind::TOKEN_TREE_META => {
+ Self { attr_macros: true, ..Self::ALL_DISABLED }
+ }
+ SyntaxKind::USE_TREE => {
+ if ast::UseTree::cast(parent).unwrap().use_tree_list().is_some() {
+ Self { modules: true, ..Self::ALL_DISABLED }
+ } else {
+ Self::ALL_ENABLED
+ }
+ }
+ SyntaxKind::VISIBILITY => Self { modules: true, ..Self::ALL_DISABLED },
+ SyntaxKind::ASM_SYM => Self { value_namespace: true, ..Self::ALL_DISABLED },
+ // `bang_macros = true` because you can still type the `!`.
+ // `type_namespace = true` because you can type `::method()`.
+ SyntaxKind::PATH_EXPR => Self {
+ value_namespace: true,
+ bang_macros: true,
+ type_namespace: true,
+ ..Self::ALL_DISABLED
+ },
+ SyntaxKind::PATH_PAT => Self::PATH_PAT_KINDS,
+ SyntaxKind::TUPLE_STRUCT_PAT => {
+ Self { tuple_structs: true, bang_macros: true, ..Self::ALL_DISABLED }
+ }
+ SyntaxKind::RECORD_EXPR | SyntaxKind::RECORD_PAT => {
+ Self { records: true, bang_macros: true, ..Self::ALL_DISABLED }
+ }
+ SyntaxKind::PATH_TYPE => {
+ Self { type_namespace: true, bang_macros: true, ..Self::ALL_DISABLED }
+ }
+ SyntaxKind::ERROR => Self::ALL_ENABLED,
+ _ => {
+ never!("this match should cover all possible parents of paths\nparent={parent:#?}");
+ Self::ALL_ENABLED
+ }
+ };
+ if !exact {
+ // When the path is not required to be exact, there could be additional segments to be filled.
+ result.modules = true;
+ result.type_namespace = true;
+ }
+ result
+ }
+}
+
/// Path import for a given name, qualified or not.
#[derive(Debug)]
pub struct PathImportCandidate {
@@ -70,6 +171,8 @@ pub struct PathImportCandidate {
pub name: NameToImport,
/// Potentially more segments that should resolve in the candidate.
pub after: Vec<Name>,
+ /// The kind of definitions that we can include.
+ definition_kinds: PathDefinitionKinds,
}
/// A name that will be used during item lookups.
@@ -168,13 +271,14 @@ impl<'db> ImportAssets<'db> {
pub fn for_fuzzy_path(
module_with_candidate: Module,
+ path: Option<&ast::Path>,
qualifier: Option<ast::Path>,
fuzzy_name: String,
sema: &Semantics<'db, RootDatabase>,
candidate_node: SyntaxNode,
) -> Option<Self> {
Some(Self {
- import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?,
+ import_candidate: ImportCandidate::for_fuzzy_path(path, qualifier, fuzzy_name, sema)?,
module_with_candidate,
candidate_node,
})
@@ -394,6 +498,9 @@ fn path_applicable_imports(
// see also an ignored test under FIXME comment in the qualify_path.rs module
AssocSearchMode::Exclude,
)
+ .filter(|(item, _)| {
+ filter_by_definition_kind(db, *item, &path_candidate.definition_kinds)
+ })
.filter_map(|(item, do_not_complete)| {
if !scope_filter(item) {
return None;
@@ -442,6 +549,46 @@ fn path_applicable_imports(
result
}
+fn filter_by_definition_kind(
+ db: &RootDatabase,
+ item: ItemInNs,
+ allowed: &PathDefinitionKinds,
+) -> bool {
+ let item = item.into_module_def();
+ let struct_per_kind = |struct_kind| {
+ allowed.structs_and_consts
+ || match struct_kind {
+ hir::StructKind::Record => allowed.records,
+ hir::StructKind::Tuple => allowed.value_namespace || allowed.tuple_structs,
+ hir::StructKind::Unit => allowed.value_namespace,
+ }
+ };
+ match item {
+ ModuleDef::Module(_) => allowed.modules,
+ ModuleDef::Function(_) => allowed.value_namespace,
+ ModuleDef::Adt(hir::Adt::Struct(item)) => {
+ allowed.type_namespace || struct_per_kind(item.kind(db))
+ }
+ ModuleDef::Adt(hir::Adt::Enum(_)) => allowed.type_namespace,
+ ModuleDef::Adt(hir::Adt::Union(_)) => {
+ allowed.type_namespace || allowed.records || allowed.structs_and_consts
+ }
+ ModuleDef::EnumVariant(item) => struct_per_kind(item.kind(db)),
+ ModuleDef::Const(_) => allowed.value_namespace || allowed.structs_and_consts,
+ ModuleDef::Static(_) => allowed.value_namespace,
+ ModuleDef::Trait(_) => allowed.type_namespace,
+ ModuleDef::TypeAlias(_) => allowed.type_namespace,
+ ModuleDef::BuiltinType(_) => allowed.type_namespace,
+ ModuleDef::Macro(item) => {
+ if item.is_fn_like(db) {
+ allowed.bang_macros
+ } else {
+ allowed.attr_macros
+ }
+ }
+ }
+}
+
fn filter_candidates_by_after_path(
db: &RootDatabase,
scope: &SemanticsScope<'_>,
@@ -835,6 +982,7 @@ impl<'db> ImportCandidate<'db> {
.collect::<Option<_>>()?;
path_import_candidate(
sema,
+ Some(path),
path.qualifier(),
NameToImport::exact_case_sensitive(path.segment()?.name_ref()?.to_string()),
after,
@@ -853,25 +1001,31 @@ impl<'db> ImportCandidate<'db> {
qualifier: vec![],
name: NameToImport::exact_case_sensitive(name.to_string()),
after: vec![],
+ definition_kinds: PathDefinitionKinds::PATH_PAT_KINDS,
}))
}
fn for_fuzzy_path(
+ path: Option<&ast::Path>,
qualifier: Option<ast::Path>,
fuzzy_name: String,
sema: &Semantics<'db, RootDatabase>,
) -> Option<Self> {
// Assume a fuzzy match does not want the segments after. Because... I guess why not?
- path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new())
+ path_import_candidate(sema, path, qualifier, NameToImport::fuzzy(fuzzy_name), Vec::new())
}
}
fn path_import_candidate<'db>(
sema: &Semantics<'db, RootDatabase>,
+ path: Option<&ast::Path>,
qualifier: Option<ast::Path>,
name: NameToImport,
after: Vec<Name>,
) -> Option<ImportCandidate<'db>> {
+ let definition_kinds = path.map_or(PathDefinitionKinds::ALL_ENABLED, |path| {
+ PathDefinitionKinds::deduce_from_path(path, matches!(name, NameToImport::Exact(..)))
+ });
Some(match qualifier {
Some(qualifier) => match sema.resolve_path(&qualifier) {
Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
@@ -880,7 +1034,12 @@ fn path_import_candidate<'db>(
.segments()
.map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text())))
.collect::<Option<Vec<_>>>()?;
- ImportCandidate::Path(PathImportCandidate { qualifier, name, after })
+ ImportCandidate::Path(PathImportCandidate {
+ qualifier,
+ name,
+ after,
+ definition_kinds,
+ })
} else {
return None;
}
@@ -904,7 +1063,12 @@ fn path_import_candidate<'db>(
}
Some(_) => return None,
},
- None => ImportCandidate::Path(PathImportCandidate { qualifier: vec![], name, after }),
+ None => ImportCandidate::Path(PathImportCandidate {
+ qualifier: vec![],
+ name,
+ after,
+ definition_kinds,
+ }),
})
}
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index da8525d1fb..fe30a4dc5c 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -9,7 +9,7 @@ use syntax::{
Direction, NodeOrToken, SyntaxKind, SyntaxNode, algo,
ast::{
self, AstNode, HasAttrs, HasModuleItem, HasVisibility, PathSegmentKind,
- edit_in_place::Removable, make, syntax_factory::SyntaxFactory,
+ edit_in_place::Removable, make,
},
syntax_editor::{Position, SyntaxEditor},
ted,
@@ -101,14 +101,12 @@ impl ImportScope {
{
block = b.stmt_list();
}
- if has_attrs
- .attrs()
- .any(|attr| attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg"))
+ if has_attrs.attrs().any(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))))
{
if let Some(b) = block.clone() {
- let current_cfgs = has_attrs.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
- });
+ let current_cfgs = has_attrs
+ .attrs()
+ .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_))));
let total_cfgs: Vec<_> =
required_cfgs.iter().cloned().chain(current_cfgs).collect();
@@ -118,7 +116,7 @@ impl ImportScope {
if let Some(parent) = parent {
can_merge = parent.children().filter_map(ast::Use::cast).any(|u| {
let u_attrs = u.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
+ matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))
});
crate::imports::merge_imports::eq_attrs(
u_attrs,
@@ -134,9 +132,11 @@ impl ImportScope {
});
}
}
- required_cfgs.extend(has_attrs.attrs().filter(|attr| {
- attr.as_simple_call().is_some_and(|(ident, _)| ident == "cfg")
- }));
+ required_cfgs.extend(
+ has_attrs
+ .attrs()
+ .filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))),
+ );
}
}
}
@@ -175,10 +175,9 @@ pub fn insert_use_with_editor(
scope: &ImportScope,
path: ast::Path,
cfg: &InsertUseConfig,
- syntax_editor: &mut SyntaxEditor,
- syntax_factory: &SyntaxFactory,
+ syntax_editor: &SyntaxEditor,
) {
- insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor, syntax_factory);
+ insert_use_with_alias_option_with_editor(scope, path, cfg, None, syntax_editor);
}
pub fn insert_use_as_alias(
@@ -269,9 +268,9 @@ fn insert_use_with_alias_option_with_editor(
path: ast::Path,
cfg: &InsertUseConfig,
alias: Option<ast::Rename>,
- syntax_editor: &mut SyntaxEditor,
- syntax_factory: &SyntaxFactory,
+ syntax_editor: &SyntaxEditor,
) {
+ let make = syntax_editor.make();
let _p = tracing::info_span!("insert_use_with_alias_option").entered();
let mut mb = match cfg.granularity {
ImportGranularity::Crate => Some(MergeBehavior::Crate),
@@ -301,14 +300,12 @@ fn insert_use_with_alias_option_with_editor(
};
}
- let use_tree = syntax_factory.use_tree(path, None, alias, false);
+ 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).clone_for_update();
- for attr in
- scope.required_cfgs.iter().map(|attr| attr.syntax().clone_subtree().clone_for_update())
- {
+ 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);
}
@@ -326,7 +323,7 @@ fn insert_use_with_alias_option_with_editor(
}
// 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_with_editor_(scope, use_item, cfg.group, syntax_editor, syntax_factory);
+ 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>> {
@@ -606,9 +603,9 @@ fn insert_use_with_editor_(
scope: &ImportScope,
use_item: ast::Use,
group_imports: bool,
- syntax_editor: &mut SyntaxEditor,
- syntax_factory: &SyntaxFactory,
+ syntax_editor: &SyntaxEditor,
) {
+ let make = syntax_editor.make();
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`");
@@ -658,7 +655,7 @@ fn insert_use_with_editor_(
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), syntax_factory.whitespace("\n"));
+ syntax_editor.insert(Position::after(node), make.whitespace("\n"));
}
return;
}
@@ -666,7 +663,7 @@ fn insert_use_with_editor_(
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), syntax_factory.whitespace("\n"));
+ syntax_editor.insert(Position::after(node), make.whitespace("\n"));
return;
}
} else {
@@ -705,20 +702,18 @@ fn insert_use_with_editor_(
{
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), syntax_factory.whitespace("\n"));
+ syntax_editor.insert(Position::after(last_inner_element), make.whitespace("\n"));
} else {
match l_curly {
Some(b) => {
cov_mark::hit!(insert_empty_module);
- syntax_editor.insert(Position::after(&b), syntax_factory.whitespace("\n"));
- syntax_editor.insert(Position::after(&b), use_item.syntax());
+ syntax_editor.insert(Position::after(&b), make.whitespace("\n"));
+ syntax_editor.insert_with_whitespace(Position::after(&b), use_item.syntax());
}
None => {
cov_mark::hit!(insert_empty_file);
- syntax_editor.insert(
- Position::first_child_of(scope_syntax),
- syntax_factory.whitespace("\n\n"),
- );
+ 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());
}
}
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs
index 3301719f5c..76645464dd 100644
--- a/crates/ide-db/src/imports/merge_imports.rs
+++ b/crates/ide-db/src/imports/merge_imports.rs
@@ -256,16 +256,6 @@ pub fn try_normalize_import(use_item: &ast::Use, style: NormalizationStyle) -> O
Some(use_item)
}
-/// Normalizes a use tree (see [`try_normalize_import`] doc).
-pub fn try_normalize_use_tree(
- use_tree: &ast::UseTree,
- style: NormalizationStyle,
-) -> Option<ast::UseTree> {
- let use_tree = use_tree.clone_subtree().clone_for_update();
- try_normalize_use_tree_mut(&use_tree, style)?;
- Some(use_tree)
-}
-
pub fn try_normalize_use_tree_mut(
use_tree: &ast::UseTree,
style: NormalizationStyle,
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index cde0705d8a..6b72a30339 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -60,8 +60,8 @@ use salsa::Durability;
use std::{fmt, mem::ManuallyDrop};
use base_db::{
- CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, RootQueryDb,
- SourceDatabase, SourceRoot, SourceRootId, SourceRootInput, query_group,
+ CrateGraphBuilder, CratesMap, FileSourceRootInput, FileText, Files, Nonce, SourceDatabase,
+ SourceRoot, SourceRootId, SourceRootInput, set_all_crates_with_durability,
};
use hir::{
FilePositionWrapper, FileRangeWrapper,
@@ -197,7 +197,7 @@ impl RootDatabase {
nonce: Nonce::new(),
};
// This needs to be here otherwise `CrateGraphBuilder` will panic.
- db.set_all_crates(Arc::new(Box::new([])));
+ set_all_crates_with_durability(&mut db, std::iter::empty(), Durability::HIGH);
CrateGraphBuilder::default().set_in_db(&mut db);
db.set_proc_macros_with_durability(Default::default(), Durability::MEDIUM);
_ = base_db::LibraryRoots::builder(Default::default())
@@ -252,15 +252,20 @@ impl RootDatabase {
}
}
-#[query_group::query_group]
-pub trait LineIndexDatabase: base_db::RootQueryDb {
- #[salsa::invoke_interned(line_index)]
- fn line_index(&self, file_id: FileId) -> Arc<LineIndex>;
-}
-
-fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
- let text = db.file_text(file_id).text(db);
- Arc::new(LineIndex::new(text))
+pub fn line_index(db: &dyn SourceDatabase, file_id: FileId) -> &Arc<LineIndex> {
+ #[salsa::interned]
+ pub struct InternedFileId {
+ id: FileId,
+ }
+ #[salsa::tracked(returns(ref))]
+ fn line_index<'db>(
+ db: &'db dyn SourceDatabase,
+ file_id: InternedFileId<'db>,
+ ) -> Arc<LineIndex> {
+ let text = db.file_text(file_id.id(db)).text(db);
+ Arc::new(LineIndex::new(text))
+ }
+ line_index(db, InternedFileId::new(db, file_id))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -380,7 +385,7 @@ pub enum Severity {
Allow,
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Clone, Copy)]
pub struct MiniCore<'a>(&'a str);
impl<'a> MiniCore<'a> {
@@ -395,6 +400,15 @@ 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()
+ }
+}
+
impl<'a> Default for MiniCore<'a> {
#[inline]
fn default() -> Self {
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs
index 508f841340..2d4a6b8b5b 100644
--- a/crates/ide-db/src/path_transform.rs
+++ b/crates/ide-db/src/path_transform.rs
@@ -197,7 +197,7 @@ impl<'a> PathTransform<'a> {
&& let Some(default) =
&default.display_source_code(db, source_module.into(), false).ok()
{
- type_substs.insert(k, make::ty(default).clone_for_update());
+ type_substs.insert(k, make::ty(default));
defaulted_params.push(Either::Left(k));
}
}
@@ -222,7 +222,7 @@ impl<'a> PathTransform<'a> {
k.default(db, target_module.krate(db).to_display_target(db))
&& let Some(default) = default.expr()
{
- const_substs.insert(k, default.syntax().clone_for_update());
+ const_substs.insert(k, default.syntax().clone());
defaulted_params.push(Either::Right(k));
}
}
@@ -278,12 +278,10 @@ impl Ctx<'_> {
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
// so that such operation is safe.
- let item = self.transform_path(item).clone_subtree();
- let mut editor = SyntaxEditor::new(item.clone());
+ let (editor, item) = SyntaxEditor::new(self.transform_path(item));
preorder_rev(&item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
- editor
- .replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
+ editor.replace(lifetime.syntax(), subst.clone().syntax());
}
});
@@ -331,26 +329,22 @@ impl Ctx<'_> {
result
}
- let root_path = path.clone_subtree();
-
+ let (editor, root_path) = SyntaxEditor::new(path.clone());
let result = find_child_paths_and_ident_pats(&root_path);
- let mut editor = SyntaxEditor::new(root_path.clone());
for sub_path in result {
let new = self.transform_path(sub_path.syntax());
editor.replace(sub_path.syntax(), new);
}
-
- let update_sub_item = editor.finish().new_root().clone().clone_subtree();
+ let (editor, update_sub_item) = SyntaxEditor::new(editor.finish().new_root().clone());
let item = find_child_paths_and_ident_pats(&update_sub_item);
- let mut editor = SyntaxEditor::new(update_sub_item);
for sub_path in item {
- self.transform_path_or_ident_pat(&mut editor, &sub_path);
+ self.transform_path_or_ident_pat(&editor, &sub_path);
}
editor.finish().new_root().clone()
}
fn transform_path_or_ident_pat(
&self,
- editor: &mut SyntaxEditor,
+ editor: &SyntaxEditor,
item: &Either<ast::Path, ast::IdentPat>,
) -> Option<()> {
match item {
@@ -359,7 +353,7 @@ impl Ctx<'_> {
}
}
- fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> {
+ fn transform_path_(&self, editor: &SyntaxEditor, path: &ast::Path) -> Option<()> {
if path.qualifier().is_some() {
return None;
}
@@ -411,33 +405,27 @@ impl Ctx<'_> {
let segment = make::path_segment_ty(subst.clone(), trait_ref);
let qualified = make::path_from_segments(std::iter::once(segment), false);
- editor.replace(path.syntax(), qualified.clone_for_update().syntax());
+ editor.replace(path.syntax(), qualified.clone().syntax());
} else if let Some(path_ty) = ast::PathType::cast(parent) {
let old = path_ty.syntax();
if old.parent().is_some() {
- editor.replace(old, subst.clone_subtree().clone_for_update().syntax());
+ editor.replace(old, subst.clone().syntax());
} else {
- // Some `path_ty` has no parent, especially ones made for default value
- // of type parameters.
- // In this case, `ted` cannot replace `path_ty` with `subst` directly.
- // So, just replace its children as long as the `subst` is the same type.
- let new = subst.clone_subtree().clone_for_update();
- if !matches!(new, ast::Type::PathType(..)) {
- return None;
- }
let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?;
let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?;
editor.replace_all(
start..=end,
- new.syntax().children().map(NodeOrToken::Node).collect::<Vec<_>>(),
+ subst
+ .clone()
+ .syntax()
+ .children()
+ .map(NodeOrToken::Node)
+ .collect::<Vec<_>>(),
);
}
} else {
- editor.replace(
- path.syntax(),
- subst.clone_subtree().clone_for_update().syntax(),
- );
+ editor.replace(path.syntax(), subst.clone().syntax());
}
}
}
@@ -459,18 +447,17 @@ 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).clone_for_update();
- let mut res_editor = SyntaxEditor::new(res.syntax().clone_subtree());
+ let res = mod_path_to_ast(&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()
{
if let Some(old) = segment.generic_arg_list() {
- res_editor
- .replace(old.syntax(), args.clone_subtree().syntax().clone_for_update())
+ res_editor.replace(old.syntax(), args.syntax().clone())
} else {
res_editor.insert(
syntax_editor::Position::last_child_of(segment.syntax()),
- args.clone_subtree().syntax().clone_for_update(),
+ args.syntax().clone(),
);
}
}
@@ -479,7 +466,7 @@ impl Ctx<'_> {
}
hir::PathResolution::ConstParam(cp) => {
if let Some(subst) = self.const_substs.get(&cp) {
- editor.replace(path.syntax(), subst.clone_subtree().clone_for_update());
+ editor.replace(path.syntax(), subst.clone());
}
}
hir::PathResolution::SelfType(imp) => {
@@ -496,7 +483,7 @@ impl Ctx<'_> {
true,
)
.ok()?;
- let ast_ty = make::ty(ty_str).clone_for_update();
+ let ast_ty = make::ty(ty_str);
if let Some(adt) = ty.as_adt()
&& let ast::Type::PathType(path_ty) = &ast_ty
@@ -516,8 +503,10 @@ impl Ctx<'_> {
if let Some(qual) =
mod_path_to_ast(&found_path, self.target_edition).qualifier()
{
- let res = make::path_concat(qual, path_ty.path()?).clone_for_update();
- editor.replace(path.syntax(), res.syntax());
+ editor.replace(
+ path.syntax(),
+ make::path_concat(qual, path_ty.path()?).syntax(),
+ );
return Some(());
}
}
@@ -533,11 +522,7 @@ impl Ctx<'_> {
Some(())
}
- fn transform_ident_pat(
- &self,
- editor: &mut SyntaxEditor,
- ident_pat: &ast::IdentPat,
- ) -> Option<()> {
+ fn transform_ident_pat(&self, editor: &SyntaxEditor, ident_pat: &ast::IdentPat) -> Option<()> {
let name = ident_pat.name()?;
let temp_path = make::path_from_text(&name.text());
@@ -593,8 +578,10 @@ 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).clone_for_update();
- editor.replace(ident_pat.syntax(), res.syntax());
+ editor.replace(
+ ident_pat.syntax(),
+ mod_path_to_ast(&found_path, self.target_edition).syntax(),
+ );
Some(())
}
_ => None,
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index d264428212..12a48d65ac 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -4,15 +4,12 @@
//! various caches, it's not really advanced at the moment.
use std::panic::AssertUnwindSafe;
+use base_db::all_crates;
use hir::{Symbol, import_map::ImportMap};
use rustc_hash::FxHashMap;
use salsa::{Cancelled, Database};
-use crate::{
- FxIndexMap, RootDatabase,
- base_db::{Crate, RootQueryDb},
- symbol_index::SymbolIndex,
-};
+use crate::{FxIndexMap, RootDatabase, base_db::Crate, symbol_index::SymbolIndex};
/// We're indexing many crates.
#[derive(Debug)]
@@ -56,7 +53,7 @@ pub fn parallel_prime_caches(
// to compute the symbols/import map of an already computed def map in that time.
let (reverse_deps, mut to_be_done_deps) = {
- let all_crates = db.all_crates();
+ let all_crates = all_crates(db);
let to_be_done_deps = all_crates
.iter()
.map(|&krate| (krate, krate.data(db).dependencies.len() as u32))
@@ -200,7 +197,7 @@ pub fn parallel_prime_caches(
)
};
- let crate_def_maps_total = db.all_crates().len();
+ let crate_def_maps_total = all_crates(db).len();
let mut crate_def_maps_done = 0;
let (mut crate_import_maps_total, mut crate_import_maps_done) = (0usize, 0usize);
let (mut module_symbols_total, mut module_symbols_done) = (0usize, 0usize);
diff --git a/crates/ide-db/src/ra_fixture.rs b/crates/ide-db/src/ra_fixture.rs
index c9a670b2d1..2f4d319ec8 100644
--- a/crates/ide-db/src/ra_fixture.rs
+++ b/crates/ide-db/src/ra_fixture.rs
@@ -52,6 +52,18 @@ impl RootDatabase {
}
}
+#[derive(Debug, Clone, Copy)]
+pub struct RaFixtureConfig<'a> {
+ pub minicore: MiniCore<'a>,
+ pub disable_ra_fixture: bool,
+}
+
+impl<'a> RaFixtureConfig<'a> {
+ pub const fn default() -> Self {
+ Self { minicore: MiniCore::default(), disable_ra_fixture: false }
+ }
+}
+
pub struct RaFixtureAnalysis {
pub db: RootDatabase,
tmp_file_ids: Vec<(FileId, usize)>,
@@ -69,9 +81,14 @@ impl RaFixtureAnalysis {
sema: &Semantics<'_, RootDatabase>,
literal: ast::String,
expanded: &ast::String,
- minicore: MiniCore<'_>,
+ config: &RaFixtureConfig<'_>,
on_cursor: &mut dyn FnMut(TextRange),
) -> Option<RaFixtureAnalysis> {
+ if config.disable_ra_fixture {
+ return None;
+ }
+ let minicore = config.minicore;
+
if !literal.is_raw() {
return None;
}
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 25acb47f7b..f41e293070 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -7,7 +7,7 @@
use std::mem;
use std::{cell::LazyCell, cmp::Reverse};
-use base_db::{RootQueryDb, SourceDatabase};
+use base_db::{SourceDatabase, all_crates};
use either::Either;
use hir::{
Adt, AsAssocItem, DefWithBody, EditionedFileId, ExpressionStoreOwner, FileRange,
@@ -161,7 +161,7 @@ impl SearchScope {
fn crate_graph(db: &RootDatabase) -> SearchScope {
let mut entries = FxHashMap::default();
- let all_crates = db.all_crates();
+ let all_crates = all_crates(db);
for &krate in all_crates.iter() {
let crate_data = krate.data(db);
let source_root = db.file_source_root(crate_data.root_file_id).source_root_id(db);
@@ -449,6 +449,8 @@ impl Definition {
scope: None,
include_self_kw_refs: None,
search_self_mod: false,
+ included_categories: ReferenceCategory::all(),
+ exclude_library_files: false,
}
}
}
@@ -465,6 +467,10 @@ pub struct FindUsages<'a> {
include_self_kw_refs: Option<hir::Type<'a>>,
/// whether to search for the `self` module
search_self_mod: bool,
+ /// categories to include while collecting usages
+ included_categories: ReferenceCategory,
+ /// whether to skip files from library source roots
+ exclude_library_files: bool,
}
impl<'a> FindUsages<'a> {
@@ -495,6 +501,16 @@ impl<'a> FindUsages<'a> {
self
}
+ pub fn set_included_categories(mut self, categories: ReferenceCategory) -> Self {
+ self.included_categories = categories;
+ self
+ }
+
+ pub fn set_exclude_library_files(mut self, exclude_library_files: bool) -> Self {
+ self.exclude_library_files = exclude_library_files;
+ self
+ }
+
pub fn at_least_one(&self) -> bool {
let mut found = false;
self.search(&mut |_, _| {
@@ -516,14 +532,21 @@ impl<'a> FindUsages<'a> {
fn scope_files<'b>(
db: &'b RootDatabase,
scope: &'b SearchScope,
+ exclude_library_files: bool,
) -> impl Iterator<Item = (Arc<str>, EditionedFileId, TextRange)> + 'b {
- scope.entries.iter().map(|(&file_id, &search_range)| {
- let text = db.file_text(file_id.file_id(db)).text(db);
- let search_range =
- search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
+ scope
+ .entries
+ .iter()
+ .filter(move |(file_id, _)| {
+ !exclude_library_files || !is_library_file(db, file_id.file_id(db))
+ })
+ .map(|(&file_id, &search_range)| {
+ let text = db.file_text(file_id.file_id(db)).text(db);
+ let search_range =
+ search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
- (text.clone(), file_id, search_range)
- })
+ (text.clone(), file_id, search_range)
+ })
}
fn match_indices<'b>(
@@ -649,6 +672,7 @@ impl<'a> FindUsages<'a> {
fn collect_possible_aliases(
sema: &Semantics<'_, RootDatabase>,
container: Adt,
+ exclude_library_files: bool,
) -> Option<(FxHashSet<SmolStr>, Vec<FileRangeWrapper<EditionedFileId>>)> {
fn insert_type_alias(
db: &RootDatabase,
@@ -682,9 +706,11 @@ impl<'a> FindUsages<'a> {
};
let finder = Finder::new(current_to_process.as_bytes());
- for (file_text, file_id, search_range) in
- FindUsages::scope_files(db, &current_to_process_search_scope)
- {
+ for (file_text, file_id, search_range) in FindUsages::scope_files(
+ db,
+ &current_to_process_search_scope,
+ exclude_library_files,
+ ) {
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
for offset in FindUsages::match_indices(&file_text, &finder, search_range) {
@@ -869,7 +895,7 @@ impl<'a> FindUsages<'a> {
}
let Some((container_possible_aliases, is_possibly_self)) =
- collect_possible_aliases(self.sema, container)
+ collect_possible_aliases(self.sema, container, self.exclude_library_files)
else {
return false;
};
@@ -906,7 +932,7 @@ impl<'a> FindUsages<'a> {
self,
&finder,
name,
- FindUsages::scope_files(self.sema.db, search_scope),
+ FindUsages::scope_files(self.sema.db, search_scope, self.exclude_library_files),
|path, name_position| {
has_any_name(path, |name| container_possible_aliases.contains(name))
&& !self_positions.contains(&name_position)
@@ -931,6 +957,9 @@ impl<'a> FindUsages<'a> {
Some(scope) => base.intersection(scope),
}
};
+ if search_scope.entries.is_empty() {
+ return;
+ }
let name = match (self.rename, self.def) {
(Some(rename), _) => {
@@ -982,7 +1011,9 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new(name);
let include_self_kw_refs =
self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
- for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) {
+ for (text, file_id, search_range) in
+ Self::scope_files(sema.db, &search_scope, self.exclude_library_files)
+ {
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
@@ -1039,7 +1070,9 @@ impl<'a> FindUsages<'a> {
let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
let finder = &Finder::new("super");
- for (text, file_id, search_range) in Self::scope_files(sema.db, &scope) {
+ for (text, file_id, search_range) in
+ Self::scope_files(sema.db, &scope, self.exclude_library_files)
+ {
self.sema.db.unwind_if_revision_cancelled();
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
@@ -1118,6 +1151,10 @@ impl<'a> FindUsages<'a> {
name_ref: &ast::NameRef,
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
+ if self.is_excluded_name_ref(name_ref) {
+ return false;
+ }
+
// See https://github.com/rust-lang/rust-analyzer/pull/15864/files/e0276dc5ddc38c65240edb408522bb869f15afb4#r1389848845
let ty_eq = |ty: hir::Type<'_>| match (ty.as_adt(), self_ty.as_adt()) {
(Some(ty), Some(self_ty)) => ty == self_ty,
@@ -1146,6 +1183,10 @@ impl<'a> FindUsages<'a> {
name_ref: &ast::NameRef,
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
+ if self.is_excluded_name_ref(name_ref) {
+ return false;
+ }
+
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
@@ -1210,6 +1251,10 @@ impl<'a> FindUsages<'a> {
name_ref: &ast::NameRef,
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
+ if self.is_excluded_name_ref(name_ref) {
+ return false;
+ }
+
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(def, _))
if self.def == def
@@ -1241,18 +1286,17 @@ impl<'a> FindUsages<'a> {
};
sink(file_id, reference)
}
- Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => {
- if self.include_self_kw_refs == def_to_ty(self.sema, &def) {
- let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
- let reference = FileReference {
- range,
- name: FileReferenceNode::NameRef(name_ref.clone()),
- category: ReferenceCategory::new(self.sema, &def, name_ref),
- };
- sink(file_id, reference)
- } else {
- false
- }
+ Some(NameRefClass::Definition(def, _))
+ if self.include_self_kw_refs.is_some()
+ && self.include_self_kw_refs == def_to_ty(self.sema, &def) =>
+ {
+ let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
+ let reference = FileReference {
+ range,
+ name: FileReferenceNode::NameRef(name_ref.clone()),
+ category: ReferenceCategory::new(self.sema, &def, name_ref),
+ };
+ sink(file_id, reference)
}
Some(NameRefClass::FieldShorthand {
local_ref: local,
@@ -1283,6 +1327,13 @@ impl<'a> FindUsages<'a> {
}
}
+ fn is_excluded_name_ref(&self, name_ref: &ast::NameRef) -> bool {
+ (!self.included_categories.contains(ReferenceCategory::TEST)
+ && is_name_ref_in_test(self.sema, name_ref))
+ || (!self.included_categories.contains(ReferenceCategory::IMPORT)
+ && is_name_ref_in_import(name_ref))
+ }
+
fn found_name(
&self,
name: &ast::Name,
@@ -1409,3 +1460,8 @@ fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameR
None => false,
})
}
+
+fn is_library_file(db: &RootDatabase, file_id: span::FileId) -> bool {
+ let source_root = db.file_source_root(file_id).source_root_id(db);
+ db.source_root(source_root).source_root(db).is_library
+}
diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs
index 57072bb5ba..81b679ead2 100644
--- a/crates/ide-db/src/source_change.rs
+++ b/crates/ide-db/src/source_change.rs
@@ -282,14 +282,14 @@ impl SourceChangeBuilder {
}
pub fn make_editor(&self, node: &SyntaxNode) -> SyntaxEditor {
- SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone()))
+ SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())).0
}
- pub fn add_file_edits(&mut self, file_id: impl Into<FileId>, edit: SyntaxEditor) {
+ pub fn add_file_edits(&mut self, file_id: impl Into<FileId>, editor: SyntaxEditor) {
match self.file_editors.entry(file_id.into()) {
- Entry::Occupied(mut entry) => entry.get_mut().merge(edit),
+ Entry::Occupied(mut entry) => entry.get_mut().merge(editor),
Entry::Vacant(entry) => {
- entry.insert(edit);
+ entry.insert(editor);
}
}
}
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs
index 183f6b6495..55acf5abf8 100644
--- a/crates/ide-db/src/symbol_index.rs
+++ b/crates/ide-db/src/symbol_index.rs
@@ -27,7 +27,10 @@ use std::{
ops::ControlFlow,
};
-use base_db::{CrateOrigin, LangCrateOrigin, LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
+use base_db::{
+ CrateOrigin, InternedSourceRootId, LangCrateOrigin, LibraryRoots, LocalRoots, SourceRootId,
+ source_root_crates,
+};
use fst::{Automaton, Streamer, raw::IndexedValue};
use hir::{
Crate, Module,
@@ -255,7 +258,7 @@ pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec<FileSymbol<'_>>
let mut crates = Vec::new();
for &root in LocalRoots::get(db).roots(db).iter() {
- crates.extend(db.source_root_crates(root).iter().copied())
+ crates.extend(source_root_crates(db, root).iter().copied())
}
crates
.par_iter()
@@ -322,7 +325,7 @@ fn resolve_path_to_modules(
// If not anchored to crate, also search for modules matching first segment in local crates
if !anchor_to_crate {
for &root in LocalRoots::get(db).roots(db).iter() {
- for &krate in db.source_root_crates(root).iter() {
+ for &krate in source_root_crates(db, root).iter() {
let root_module = Crate::from(krate).root_module(db);
for child in root_module.children(db) {
if let Some(name) = child.name(db)
@@ -369,11 +372,6 @@ impl<'db> SymbolIndex<'db> {
db: &'db dyn HirDatabase,
source_root_id: SourceRootId,
) -> &'db SymbolIndex<'db> {
- // FIXME:
- #[salsa::interned]
- struct InternedSourceRootId {
- id: SourceRootId,
- }
#[salsa::tracked(returns(ref))]
fn library_symbols<'db>(
db: &'db dyn HirDatabase,
@@ -385,7 +383,7 @@ impl<'db> SymbolIndex<'db> {
hir::attach_db(db, || {
let mut symbol_collector = SymbolCollector::new(db, true);
- db.source_root_crates(source_root_id.id(db))
+ source_root_crates(db, source_root_id.id(db))
.iter()
.flat_map(|&krate| Crate::from(krate).modules(db))
// we specifically avoid calling other SymbolsDatabase queries here, even though they do the same thing,
@@ -402,22 +400,16 @@ impl<'db> SymbolIndex<'db> {
/// The symbol index for a given module. These modules should only be in source roots that
/// are inside local_roots.
pub fn module_symbols(db: &dyn HirDatabase, module: Module) -> &SymbolIndex<'_> {
- // FIXME:
- #[salsa::interned]
- struct InternedModuleId {
- id: hir::ModuleId,
- }
-
#[salsa::tracked(returns(ref))]
fn module_symbols<'db>(
db: &'db dyn HirDatabase,
- module: InternedModuleId<'db>,
+ module: hir::ModuleId,
) -> SymbolIndex<'db> {
let _p = tracing::info_span!("module_symbols").entered();
// We call this without attaching because this runs in parallel, so we need to attach here.
hir::attach_db(db, || {
- let module: Module = module.id(db).into();
+ let module: Module = module.into();
SymbolIndex::new(SymbolCollector::new_module(
db,
module,
@@ -426,7 +418,7 @@ impl<'db> SymbolIndex<'db> {
})
}
- module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module)))
+ module_symbols(db, hir::ModuleId::from(module))
}
/// The symbol index for all extern prelude crates.
diff --git a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
index 8f25833fff..6cc3334196 100644
--- a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
+++ b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -125,7 +125,7 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
// if the expression consists of a single number, like "0" or "12", it can refer to
// format args in the order they are specified.
// see: https://doc.rust-lang.org/std/fmt/#positional-parameters
- if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) {
+ if trimmed.chars().all(|c| c.is_ascii_digit()) {
output.push_str(trimmed);
} else if matches!(state, State::Expr) {
extracted_expressions.push(Arg::Expr(trimmed.into()));
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs
index e30b21c139..11ba815dab 100644
--- a/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -216,7 +216,12 @@ pub fn walk_ty(ty: &ast::Type, cb: &mut dyn FnMut(ast::Type) -> bool) {
preorder.skip_subtree();
cb(ty);
}
- Some(ty) => {
+ Some(ty) =>
+ {
+ #[expect(
+ clippy::collapsible_match,
+ reason = "it won't compile due to exhaustiveness"
+ )]
if cb(ty) {
preorder.skip_subtree();
}
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index 3a785fbe80..09e6115320 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -123,6 +123,20 @@ impl NameGenerator {
generator
}
+ pub fn new_from_scope_non_locals(scope: Option<SemanticsScope<'_>>) -> Self {
+ let mut generator = Self::default();
+ if let Some(scope) = scope {
+ scope.process_all_names(&mut |name, scope| {
+ if let hir::ScopeDef::Local(_) = scope {
+ return;
+ }
+ generator.insert(name.as_str());
+ });
+ }
+
+ generator
+ }
+
/// Suggest a name without conflicts. If the name conflicts with existing names,
/// it will try to resolve the conflict by adding a numeric suffix.
pub fn suggest_name(&mut self, name: &str) -> SmolStr {
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt
index fc98ebb069..17d002e8bf 100644
--- a/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -2,7 +2,7 @@
(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3400),
+ [salsa id]: Id(3000),
},
},
[
@@ -12,7 +12,7 @@
Struct(
Struct {
id: StructId(
- 3c01,
+ 3801,
),
},
),
@@ -20,7 +20,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -49,7 +49,7 @@
Struct(
Struct {
id: StructId(
- 3c00,
+ 3800,
),
},
),
@@ -57,7 +57,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -86,7 +86,7 @@
Struct(
Struct {
id: StructId(
- 3c00,
+ 3800,
),
},
),
@@ -94,7 +94,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -123,7 +123,7 @@
Struct(
Struct {
id: StructId(
- 3c00,
+ 3800,
),
},
),
@@ -131,7 +131,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -160,7 +160,7 @@
Struct(
Struct {
id: StructId(
- 3c00,
+ 3800,
),
},
),
@@ -168,7 +168,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -197,7 +197,7 @@
Struct(
Struct {
id: StructId(
- 3c01,
+ 3801,
),
},
),
@@ -205,7 +205,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -234,7 +234,7 @@
Struct(
Struct {
id: StructId(
- 3c00,
+ 3800,
),
},
),
@@ -242,7 +242,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 02a023038a..1b20a574bd 100644
--- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,7 +2,7 @@
(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3400),
+ [salsa id]: Id(3000),
},
},
[
@@ -11,14 +11,14 @@
def: EnumVariant(
EnumVariant {
id: EnumVariantId(
- 7c00,
+ 7800,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -48,14 +48,14 @@
def: TypeAlias(
TypeAlias {
id: TypeAliasId(
- 7000,
+ 6c00,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -83,14 +83,14 @@
def: EnumVariant(
EnumVariant {
id: EnumVariantId(
- 7c01,
+ 7801,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -120,14 +120,14 @@
def: Const(
Const {
id: ConstId(
- 6800,
+ 6400,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -155,14 +155,14 @@
def: Const(
Const {
id: ConstId(
- 6802,
+ 6402,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -191,7 +191,7 @@
Enum(
Enum {
id: EnumId(
- 5400,
+ 5000,
),
},
),
@@ -199,7 +199,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -228,7 +228,7 @@
Macro {
id: Macro2Id(
Macro2Id(
- 5000,
+ 4c00,
),
),
},
@@ -236,7 +236,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -265,7 +265,7 @@
Macro {
id: Macro2Id(
Macro2Id(
- 5000,
+ 4c00,
),
),
},
@@ -273,7 +273,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -301,14 +301,14 @@
def: Static(
Static {
id: StaticId(
- 6c00,
+ 6800,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -337,7 +337,7 @@
Struct(
Struct {
id: StructId(
- 4c01,
+ 4801,
),
},
),
@@ -345,7 +345,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -374,7 +374,7 @@
Struct(
Struct {
id: StructId(
- 4c00,
+ 4800,
),
},
),
@@ -382,7 +382,7 @@
loc: DeclarationLocation {
hir_file_id: MacroFile(
MacroCallId(
- Id(4400),
+ Id(4000),
),
),
ptr: SyntaxNodePtr {
@@ -411,7 +411,7 @@
Struct(
Struct {
id: StructId(
- 4c05,
+ 4805,
),
},
),
@@ -419,7 +419,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -450,7 +450,7 @@
Struct(
Struct {
id: StructId(
- 4c06,
+ 4806,
),
},
),
@@ -458,7 +458,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -489,7 +489,7 @@
Struct(
Struct {
id: StructId(
- 4c07,
+ 4807,
),
},
),
@@ -497,7 +497,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -526,7 +526,7 @@
Struct(
Struct {
id: StructId(
- 4c02,
+ 4802,
),
},
),
@@ -534,7 +534,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -562,14 +562,14 @@
def: Trait(
Trait {
id: TraitId(
- 6000,
+ 5c00,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -598,7 +598,7 @@
Macro {
id: Macro2Id(
Macro2Id(
- 5000,
+ 4c00,
),
),
},
@@ -606,7 +606,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -635,7 +635,7 @@
Union(
Union {
id: UnionId(
- 5800,
+ 5400,
),
},
),
@@ -643,7 +643,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -671,14 +671,14 @@
def: Module(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3401),
+ [salsa id]: Id(3001),
},
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -706,14 +706,14 @@
def: Module(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3402),
+ [salsa id]: Id(3002),
},
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -742,7 +742,7 @@
Macro {
id: MacroRulesId(
MacroRulesId(
- 4001,
+ 3c01,
),
),
},
@@ -750,7 +750,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -778,14 +778,14 @@
def: Function(
FunctionId(
FunctionId(
- 6402,
+ 6002,
),
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -815,14 +815,14 @@
def: Function(
FunctionId(
FunctionId(
- 6401,
+ 6001,
),
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -853,7 +853,7 @@
Macro {
id: MacroRulesId(
MacroRulesId(
- 4000,
+ 3c00,
),
),
},
@@ -861,7 +861,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -889,14 +889,14 @@
def: Function(
FunctionId(
FunctionId(
- 6400,
+ 6000,
),
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -925,7 +925,7 @@
Macro {
id: MacroRulesId(
MacroRulesId(
- 4001,
+ 3c01,
),
),
},
@@ -933,7 +933,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -961,14 +961,14 @@
def: Function(
FunctionId(
FunctionId(
- 6403,
+ 6003,
),
),
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -998,7 +998,7 @@
(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3401),
+ [salsa id]: Id(3001),
},
},
[
@@ -1008,7 +1008,7 @@
Struct(
Struct {
id: StructId(
- 4c03,
+ 4803,
),
},
),
@@ -1016,7 +1016,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
@@ -1044,7 +1044,7 @@
(
Module {
id: ModuleIdLt {
- [salsa id]: Id(3402),
+ [salsa id]: Id(3002),
},
},
[
@@ -1053,14 +1053,14 @@
def: Trait(
Trait {
id: TraitId(
- 6000,
+ 5c00,
),
},
),
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
@@ -1089,7 +1089,7 @@
Macro {
id: Macro2Id(
Macro2Id(
- 5000,
+ 4c00,
),
),
},
@@ -1097,7 +1097,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
@@ -1126,7 +1126,7 @@
Struct(
Struct {
id: StructId(
- 4c04,
+ 4804,
),
},
),
@@ -1134,7 +1134,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
@@ -1163,7 +1163,7 @@
Macro {
id: Macro2Id(
Macro2Id(
- 5000,
+ 4c00,
),
),
},
@@ -1171,7 +1171,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
@@ -1200,7 +1200,7 @@
Struct(
Struct {
id: StructId(
- 4c04,
+ 4804,
),
},
),
@@ -1208,7 +1208,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
index aff1d56c56..f8ae687b78 100644
--- a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -5,7 +5,7 @@
Struct(
Struct {
id: StructId(
- 4000,
+ 3c00,
),
},
),
@@ -13,7 +13,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
index bf5d81cfb1..2282815a61 100644
--- a/crates/ide-db/src/test_data/test_symbols_with_imports.txt
+++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -5,7 +5,7 @@
Struct(
Struct {
id: StructId(
- 4000,
+ 3c00,
),
},
),
@@ -13,7 +13,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3001),
+ Id(2c01),
),
),
ptr: SyntaxNodePtr {
@@ -42,7 +42,7 @@
Struct(
Struct {
id: StructId(
- 4000,
+ 3c00,
),
},
),
@@ -50,7 +50,7 @@
loc: DeclarationLocation {
hir_file_id: FileId(
EditionedFileId(
- Id(3000),
+ Id(2c00),
),
),
ptr: SyntaxNodePtr {
diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs
index dfa9639f6e..be4fe763a0 100644
--- a/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -40,7 +40,10 @@ pub(crate) fn inactive_code(
#[cfg(test)]
mod tests {
- use crate::{DiagnosticsConfig, tests::check_diagnostics_with_config};
+ use ide_db::RootDatabase;
+ use test_fixture::WithFixture;
+
+ use crate::{DiagnosticCode, DiagnosticsConfig, tests::check_diagnostics_with_config};
#[track_caller]
pub(crate) fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
@@ -206,10 +209,47 @@ union FooBar {
#[cfg(true)] fn active() {}
- #[cfg(any(not(true)), false)] fn inactive2() {}
-//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled
+ #[cfg(any(not(true), false))] fn inactive2() {}
+//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: true is enabled and false is disabled
"#,
);
}
+
+ #[test]
+ fn inactive_crate() {
+ let db = RootDatabase::with_files(
+ r#"
+#![cfg(false)]
+
+fn foo() {}
+ "#,
+ );
+ let file_id = db.test_crate().root_file_id(&db);
+ let diagnostics = hir::attach_db(&db, || {
+ crate::full_diagnostics(
+ &db,
+ &DiagnosticsConfig::test_sample(),
+ &ide_db::assists::AssistResolveStrategy::All,
+ file_id.file_id(&db),
+ )
+ });
+ let [inactive_code] = &*diagnostics else {
+ panic!("expected one inactive_code diagnostic, found {diagnostics:#?}");
+ };
+ assert_eq!(
+ inactive_code.code,
+ DiagnosticCode::Ra("inactive-code", ide_db::Severity::WeakWarning)
+ );
+ assert_eq!(
+ inactive_code.message,
+ "code is inactive due to #[cfg] directives: false is disabled",
+ );
+ assert!(inactive_code.fixes.is_none());
+ let full_file_range = file_id.parse(&db).syntax_node().text_range();
+ assert_eq!(
+ inactive_code.range,
+ ide_db::FileRange { file_id: file_id.file_id(&db), range: full_file_range },
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 4ed71f0d3f..4c0985c7ae 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -357,6 +357,7 @@ fn f() {
fn arg_count_lambda() {
check_diagnostics(
r#"
+//- minicore: fn
fn main() {
let f = |()| ();
f();
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 050d5477f6..85368cc09f 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -1,6 +1,6 @@
use either::Either;
use hir::{
- AssocItem, FindPathConfig, HirDisplay, InFile, Type,
+ AssocItem, FindPathConfig, HasVisibility, HirDisplay, InFile, Type,
db::{ExpandDatabase, HirDatabase},
sym,
};
@@ -35,7 +35,7 @@ use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
// ```
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 {
+ for (field, _) in &d.missed_fields {
format_to!(message, "- {}\n", field.display(ctx.sema.db, ctx.edition));
}
@@ -57,7 +57,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
// `struct A(usize);`
// `let a = A { 0: () }`
// but it is uncommon usage and it should not be encouraged.
- if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) {
+ if d.missed_fields.iter().any(|(name, _)| name.as_tuple_index().is_some()) {
return None;
}
@@ -68,6 +68,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let range = InFile::new(d.file, d.field_list_parent.text_range())
.original_node_file_range_rooted_opt(ctx.sema.db)?;
+ if let Some(current_module) = current_module
+ && d.missed_fields.iter().any(|(_, field)| !field.is_visible_from(ctx.db(), current_module))
+ {
+ return None;
+ }
+
let build_text_edit = |new_syntax: &SyntaxNode, old_syntax| {
let edit = {
let old_range = ctx.sema.original_range_opt(old_syntax)?;
@@ -120,7 +126,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let field_expr = if let Some(local_candidate) = locals.get(&f.name(ctx.sema.db)) {
cov_mark::hit!(field_shorthand);
let candidate_ty = local_candidate.ty(ctx.sema.db);
- if ty.could_unify_with(ctx.sema.db, &candidate_ty) {
+ if candidate_ty.could_coerce_to(ctx.sema.db, ty) {
None
} else {
Some(generate_fill_expr(ty))
@@ -254,7 +260,7 @@ fn get_default_constructor(
#[cfg(test)]
mod tests {
- use crate::tests::{check_diagnostics, check_fix};
+ use crate::tests::{check_diagnostics, check_fix, check_no_fix};
#[test]
fn missing_record_pat_field_diagnostic() {
@@ -934,4 +940,45 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn coerce_existing_local() {
+ check_fix(
+ r#"
+struct A {
+ v: f64,
+}
+
+fn f() -> A {
+ let v = loop {};
+ A {$0}
+}
+ "#,
+ r#"
+struct A {
+ v: f64,
+}
+
+fn f() -> A {
+ let v = loop {};
+ A { v }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn inaccessible_fields() {
+ check_no_fix(
+ r#"
+mod foo {
+ pub struct Bar { baz: i32 }
+}
+
+fn qux() {
+ foo::Bar {$0};
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 1abb50144d..6a37702fc5 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -845,6 +845,7 @@ fn foo(v: &Union) {
fn union_destructuring() {
check_diagnostics(
r#"
+//- minicore: fn
union Union { field: u8 }
fn foo(v @ Union { field: _field }: &Union) {
// ^^^^^^ error: access to union field is unsafe and requires an unsafe function or block
diff --git a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
index dec7be8b74..2ec41d0528 100644
--- a/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
+++ b/crates/ide-diagnostics/src/handlers/remove_trailing_return.rs
@@ -95,6 +95,7 @@ fn foo() -> u8 {
fn remove_trailing_return_closure() {
check_diagnostics(
r#"
+//- minicore: fn
fn foo() -> u8 {
let bar = || return 2;
bar() //^^^^^^^^ 💡 weak: replace return <expr>; with <expr>
@@ -103,6 +104,7 @@ fn foo() -> u8 {
);
check_diagnostics(
r#"
+//- minicore: fn
fn foo() -> u8 {
let bar = || {
return 2;
@@ -276,6 +278,7 @@ fn foo() -> u8 {
fn replace_in_closure() {
check_fix(
r#"
+//- minicore: fn
fn foo() -> u8 {
let bar = || return$0 2;
bar()
@@ -290,6 +293,7 @@ fn foo() -> u8 {
);
check_fix(
r#"
+//- minicore: fn
fn foo() -> u8 {
let bar = || {
return$0 2;
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 f4054610f2..6a380481d4 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
@@ -5,8 +5,10 @@ use ide_db::{
label::Label,
source_change::SourceChangeBuilder,
};
-use syntax::ToSmolStr;
-use syntax::ast::edit::AstNodeEdit;
+use syntax::{
+ AstNode, ToSmolStr,
+ ast::{HasName, edit::AstNodeEdit},
+};
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
@@ -82,16 +84,18 @@ fn quickfix_for_redundant_assoc_item(
let db = ctx.sema.db;
let root = db.parse_or_expand(d.file_id);
// don't modify trait def in outer crate
- let current_crate = ctx.sema.scope(&d.impl_.syntax_node_ptr().to_node(&root))?.krate();
+ let impl_def = d.impl_.to_node(&root);
+ let current_crate = ctx.sema.scope(impl_def.syntax())?.krate();
let trait_def_crate = d.trait_.module(db).krate(db);
if trait_def_crate != current_crate {
return None;
}
let trait_def = d.trait_.source(db)?.value;
- let l_curly = trait_def.assoc_item_list()?.l_curly_token()?.text_range();
+ let insert_after = find_insert_after(range, &impl_def, &trait_def)?;
+
let where_to_insert =
- hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted_opt(db)?;
+ hir::InFile::new(d.file_id, insert_after).original_node_file_range_rooted_opt(db)?;
if where_to_insert.file_id != file_id {
return None;
}
@@ -112,6 +116,41 @@ fn quickfix_for_redundant_assoc_item(
}])
}
+fn find_insert_after(
+ redundant_range: TextRange,
+ impl_def: &syntax::ast::Impl,
+ trait_def: &syntax::ast::Trait,
+) -> Option<TextRange> {
+ let impl_items_before_redundant = impl_def
+ .assoc_item_list()?
+ .assoc_items()
+ .take_while(|it| it.syntax().text_range().start() < redundant_range.start())
+ .filter_map(|it| name_of(&it))
+ .collect::<Vec<_>>();
+
+ let after_item = trait_def
+ .assoc_item_list()?
+ .assoc_items()
+ .filter(|it| {
+ name_of(it).is_some_and(|name| {
+ impl_items_before_redundant.iter().any(|it| it.text() == name.text())
+ })
+ })
+ .last()
+ .map(|it| it.syntax().text_range());
+
+ return after_item.or_else(|| Some(trait_def.assoc_item_list()?.l_curly_token()?.text_range()));
+
+ fn name_of(it: &syntax::ast::AssocItem) -> Option<syntax::ast::Name> {
+ match it {
+ syntax::ast::AssocItem::Const(it) => it.name(),
+ syntax::ast::AssocItem::Fn(it) => it.name(),
+ syntax::ast::AssocItem::TypeAlias(it) => it.name(),
+ syntax::ast::AssocItem::MacroCall(_) => None,
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_diagnostics, check_fix, check_no_fix};
@@ -275,6 +314,69 @@ mod indent {
}
#[test]
+ fn quickfix_order() {
+ check_fix(
+ r#"
+trait Marker {
+ fn foo();
+ fn baz();
+}
+struct Foo;
+impl Marker for Foo {
+ fn foo() {}
+ fn missing() {}$0
+ fn baz() {}
+}
+ "#,
+ r#"
+trait Marker {
+ fn foo();
+ fn missing();
+ fn baz();
+}
+struct Foo;
+impl Marker for Foo {
+ fn foo() {}
+ fn missing() {}
+ fn baz() {}
+}
+ "#,
+ );
+
+ check_fix(
+ r#"
+trait Marker {
+ type Item;
+ fn bar();
+ fn baz();
+}
+struct Foo;
+impl Marker for Foo {
+ type Item = Foo;
+ fn missing() {}$0
+ fn bar() {}
+ fn baz() {}
+}
+ "#,
+ r#"
+trait Marker {
+ type Item;
+ fn missing();
+ fn bar();
+ fn baz();
+}
+struct Foo;
+impl Marker for Foo {
+ type Item = Foo;
+ fn missing() {}
+ fn bar() {}
+ fn baz() {}
+}
+ "#,
+ );
+ }
+
+ #[test]
fn quickfix_dont_work() {
check_no_fix(
r#"
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index f443dc08f5..98a4474ef1 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -10,7 +10,6 @@ use syntax::{
ast::{
self, BlockExpr, Expr, ExprStmt, HasArgList,
edit::{AstNodeEdit, IndentLevel},
- syntax_factory::SyntaxFactory,
},
};
@@ -20,7 +19,14 @@ 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<'_>, d: &hir::TypeMismatch<'_>) -> Diagnostic {
+pub(crate) fn type_mismatch(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::TypeMismatch<'_>,
+) -> Option<Diagnostic> {
+ if d.expected.is_unknown() || d.actual.is_unknown() {
+ return None;
+ }
+
let display_range = adjusted_display_range(ctx, d.expr_or_pat, &|node| {
let Either::Left(expr) = node else { return None };
let salient_token_range = match expr {
@@ -39,21 +45,23 @@ pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<
cov_mark::hit!(type_mismatch_range_adjustment);
Some(salient_token_range)
});
- Diagnostic::new(
- DiagnosticCode::RustcHardError("E0308"),
- format!(
- "expected {}, found {}",
- d.expected
- .display(ctx.sema.db, ctx.display_target)
- .with_closure_style(ClosureStyle::ClosureWithId),
- d.actual
- .display(ctx.sema.db, ctx.display_target)
- .with_closure_style(ClosureStyle::ClosureWithId),
- ),
- display_range,
+ Some(
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0308"),
+ format!(
+ "expected {}, found {}",
+ d.expected
+ .display(ctx.sema.db, ctx.display_target)
+ .with_closure_style(ClosureStyle::ClosureWithId),
+ d.actual
+ .display(ctx.sema.db, ctx.display_target)
+ .with_closure_style(ClosureStyle::ClosureWithId),
+ ),
+ display_range,
+ )
+ .stable()
+ .with_fixes(fixes(ctx, d)),
)
- .stable()
- .with_fixes(fixes(ctx, d))
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
@@ -101,7 +109,8 @@ fn add_missing_ok_or_some(
) -> Option<()> {
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
- let expr_range = expr.syntax().text_range();
+ let hir::FileRange { file_id, range: expr_range } =
+ ctx.sema.original_range_opt(expr.syntax())?;
let scope = ctx.sema.scope(expr.syntax())?;
let expected_adt = d.expected.as_adt()?;
@@ -124,6 +133,8 @@ fn add_missing_ok_or_some(
return None;
}
+ let file_id = file_id.file_id(ctx.sema.db);
+
if d.actual.is_unit() {
if let Expr::BlockExpr(block) = &expr {
if block.tail_expr().is_none() {
@@ -135,21 +146,18 @@ fn add_missing_ok_or_some(
// Empty block
let indent = block_indent + 1;
builder.insert(
- block.syntax().text_range().start() + TextSize::from(1),
+ expr_range.start() + TextSize::from(1),
format!("\n{indent}{variant_name}(())\n{block_indent}"),
);
} else {
let indent = IndentLevel::from(1);
builder.insert(
- block.syntax().text_range().end() - TextSize::from(1),
+ expr_range.end() - TextSize::from(1),
format!("{indent}{variant_name}(())\n{block_indent}"),
);
}
- let source_change = SourceChange::from_text_edit(
- expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
- builder.finish(),
- );
+ let source_change = SourceChange::from_text_edit(file_id, builder.finish());
let name = format!("Insert {variant_name}(()) as the tail of this block");
acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
}
@@ -158,26 +166,31 @@ fn add_missing_ok_or_some(
// Fix for forms like `fn foo() -> Result<(), String> { return; }`
if ret_expr.expr().is_none() {
let mut builder = TextEdit::builder();
- builder
- .insert(ret_expr.syntax().text_range().end(), format!(" {variant_name}(())"));
- let source_change = SourceChange::from_text_edit(
- expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
- builder.finish(),
- );
+ builder.insert(expr_range.end(), format!(" {variant_name}(())"));
+ let source_change = SourceChange::from_text_edit(file_id, builder.finish());
let name = format!("Insert {variant_name}(()) as the return value");
acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
}
return Some(());
+ } else if expr.is_block_like()
+ && expr.syntax().parent().and_then(ast::StmtList::cast).is_some()
+ {
+ // Fix for forms like `fn foo() -> Result<(), String> { for _ in 0..8 {} }`
+ let mut builder = TextEdit::builder();
+ let indent = expr.indent_level();
+ builder.insert(expr_range.end(), format!("\n{indent}{variant_name}(())"));
+
+ let source_change = SourceChange::from_text_edit(file_id, builder.finish());
+ let name = format!("Insert {variant_name}(()) as the tail of this block");
+ acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
+ return Some(());
}
}
let mut builder = TextEdit::builder();
- builder.insert(expr.syntax().text_range().start(), format!("{variant_name}("));
- builder.insert(expr.syntax().text_range().end(), ")".to_owned());
- let source_change = SourceChange::from_text_edit(
- expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
- builder.finish(),
- );
+ builder.insert(expr_range.start(), format!("{variant_name}("));
+ builder.insert(expr_range.end(), ")".to_owned());
+ let source_change = SourceChange::from_text_edit(file_id, builder.finish());
let name = format!("Wrap in {variant_name}");
acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
Some(())
@@ -192,6 +205,7 @@ fn remove_unnecessary_wrapper(
let db = ctx.sema.db;
let root = db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
+ // FIXME: support inside MacroCall?
let expr = ctx.sema.original_ast_node(expr)?;
let Expr::CallExpr(call_expr) = expr else {
@@ -220,7 +234,7 @@ fn remove_unnecessary_wrapper(
let file_id = expr_ptr.file_id.original_file(db);
let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db));
- let mut editor;
+ let editor;
match inner_arg {
// We're returning `()`
Expr::TupleExpr(tup) if tup.fields().next().is_none() => {
@@ -230,7 +244,7 @@ fn remove_unnecessary_wrapper(
.and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)?;
editor = builder.make_editor(parent.syntax());
- let make = SyntaxFactory::with_mappings();
+ let make = editor.make();
match parent {
Either::Left(ret_expr) => {
@@ -246,8 +260,6 @@ fn remove_unnecessary_wrapper(
editor.replace(stmt_list.syntax().parent()?, new_block.syntax());
}
}
-
- editor.add_mappings(make.finish_with_mappings());
}
_ => {
editor = builder.make_editor(call_expr.syntax());
@@ -278,6 +290,7 @@ fn remove_semicolon(
return None;
}
let block = BlockExpr::cast(expr.syntax().clone())?;
+ // FIXME: support inside MacroCall?
let expr_before_semi =
block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
@@ -311,16 +324,13 @@ fn str_ref_to_owned(
let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
let expr = expr_ptr.value.to_node(&root);
- let expr_range = expr.syntax().text_range();
+ let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?;
let to_owned = ".to_owned()".to_owned();
- let edit = TextEdit::insert(expr.syntax().text_range().end(), to_owned);
- let source_change = SourceChange::from_text_edit(
- expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
- edit,
- );
- acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, expr_range));
+ let edit = TextEdit::insert(range.end(), to_owned);
+ let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit);
+ acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range));
Some(())
}
@@ -568,6 +578,32 @@ fn div(x: i32, y: i32) -> Result<i32, ()> {
}
"#,
);
+
+ check_fix(
+ r#"
+//- minicore: option, result
+macro_rules! identity { ($($t:tt)*) => ($($t)*) }
+identity! {
+ fn div(x: i32, y: i32) -> Result<i32, ()> {
+ if y == 0 {
+ return Err(());
+ }
+ x / y$0
+ }
+}
+"#,
+ r#"
+macro_rules! identity { ($($t:tt)*) => ($($t)*) }
+identity! {
+ fn div(x: i32, y: i32) -> Result<i32, ()> {
+ if y == 0 {
+ return Err(());
+ }
+ Ok(x / y)
+ }
+}
+"#,
+ );
}
#[test]
@@ -700,6 +736,21 @@ fn foo() -> Result<(), ()> {
}
"#,
);
+
+ check_fix(
+ r#"
+//- minicore: result
+fn foo() -> Result<(), ()> {
+ for _ in 0..5 {}$0
+}
+ "#,
+ r#"
+fn foo() -> Result<(), ()> {
+ for _ in 0..5 {}
+ Ok(())
+}
+ "#,
+ );
}
#[test]
@@ -1040,6 +1091,29 @@ fn test() -> String {
}
"#,
);
+
+ check_fix(
+ r#"
+macro_rules! identity { ($($t:tt)*) => ($($t)*) }
+struct String;
+
+identity! {
+ fn test() -> String {
+ "a"$0
+ }
+}
+ "#,
+ r#"
+macro_rules! identity { ($($t:tt)*) => ($($t)*) }
+struct String;
+
+identity! {
+ fn test() -> String {
+ "a".to_owned()
+ }
+}
+ "#,
+ );
}
#[test]
@@ -1171,7 +1245,7 @@ trait B {}
fn test(a: &dyn A) -> &dyn B {
a
- //^ error: expected &(dyn B + 'static), found &(dyn A + 'static)
+ //^💡 error: expected &(dyn B + 'static), found &(dyn A + 'static)
}
"#,
);
@@ -1253,4 +1327,23 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn test_ignore_unknown_mismatch() {
+ check_diagnostics(
+ r#"
+pub trait Foo {
+ type Out;
+}
+impl Foo for [i32; 1] {
+ type Out = ();
+}
+pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } }
+
+fn main() {
+ let _x = foo(2);
+}
+"#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 1283a11700..570319c347 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -4,11 +4,9 @@ use std::iter;
use hir::crate_def_map;
use hir::{InFile, ModuleSource};
-use ide_db::base_db::RootQueryDb;
use ide_db::text_edit::TextEdit;
-use ide_db::{
- FileId, FileRange, LineIndexDatabase, base_db::SourceDatabase, source_change::SourceChange,
-};
+use ide_db::{FileId, FileRange, base_db::SourceDatabase, source_change::SourceChange};
+use ide_db::{base_db, line_index};
use paths::Utf8Component;
use syntax::{
AstNode, TextRange,
@@ -26,7 +24,7 @@ pub(crate) fn unlinked_file(
acc: &mut Vec<Diagnostic>,
file_id: FileId,
) {
- let mut range = TextRange::up_to(ctx.sema.db.line_index(file_id).len());
+ let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len());
let fixes = fixes(ctx, file_id, range);
// FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
// This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523
@@ -101,8 +99,8 @@ fn fixes(
};
// check crate roots, i.e. main.rs, lib.rs, ...
- let relevant_crates = db.relevant_crates(file_id);
- 'crates: for &krate in &*relevant_crates {
+ let relevant_crates = base_db::relevant_crates(db, file_id);
+ 'crates: for &krate in relevant_crates {
// FIXME: This shouldnt need to access the crate def map directly
let crate_def_map = crate_def_map(ctx.sema.db, krate);
@@ -157,7 +155,7 @@ fn fixes(
paths.into_iter().find_map(|path| source_root.file_for_path(&path))
})?;
stack.pop();
- let relevant_crates = db.relevant_crates(parent_id);
+ let relevant_crates = base_db::relevant_crates(db, parent_id);
'crates: for &krate in relevant_crates.iter() {
let crate_def_map = crate_def_map(ctx.sema.db, krate);
let Some((_, module)) = crate_def_map.modules().find(|(_, module)| {
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 0c6953419f..09c9f8eab0 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -96,7 +96,7 @@ use hir::{
use ide_db::{
FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, Severity, SnippetCap,
assists::{Assist, AssistId, AssistResolveStrategy, ExprFillDefaultMode},
- base_db::{ReleaseChannel, RootQueryDb as _},
+ base_db::{ReleaseChannel, all_crates, toolchain_channel},
generated::lints::{CLIPPY_LINT_GROUPS, DEFAULT_LINT_GROUPS, DEFAULT_LINTS, Lint, LintGroup},
imports::insert_use::InsertUseConfig,
label::Label,
@@ -285,6 +285,12 @@ struct DiagnosticsContext<'a> {
is_nightly: bool,
}
+impl<'a> DiagnosticsContext<'a> {
+ fn db(&self) -> &'a RootDatabase {
+ self.sema.db
+ }
+}
+
/// Request parser level diagnostics for the given [`FileId`].
pub fn syntax_diagnostics(
db: &RootDatabase,
@@ -303,7 +309,8 @@ pub fn syntax_diagnostics(
let (file_id, _) = editioned_file_id.unpack(db);
// [#3434] Only take first 128 errors to prevent slowing down editor/ide, the number 128 is chosen arbitrarily.
- db.parse_errors(editioned_file_id)
+ editioned_file_id
+ .parse_errors(db)
.into_iter()
.flatten()
.take(128)
@@ -353,14 +360,14 @@ pub fn semantic_diagnostics(
let module = sema.file_to_module_def(file_id);
let is_nightly = matches!(
- module.and_then(|m| db.toolchain_channel(m.krate(db).into())),
+ module.and_then(|m| toolchain_channel(db, m.krate(db).into())),
Some(ReleaseChannel::Nightly) | None
);
let krate = match module {
Some(module) => module.krate(db),
None => {
- match db.all_crates().last() {
+ match all_crates(db).last() {
Some(last) => (*last).into(),
// short-circuit, return an empty vec of diagnostics
None => return vec![],
@@ -375,7 +382,7 @@ pub fn semantic_diagnostics(
// A bunch of parse errors in a file indicate some bigger structural parse changes in the
// file, so we skip semantic diagnostics so we can show these faster.
Some(m) => {
- if db.parse_errors(editioned_file_id).is_none_or(|es| es.len() < 16) {
+ if editioned_file_id.parse_errors(db).is_none_or(|es| es.len() < 16) {
m.diagnostics(db, &mut diags, config.style_lints);
}
}
@@ -430,7 +437,10 @@ pub fn semantic_diagnostics(
AnyDiagnostic::TraitImplRedundantAssocItems(d) => handlers::trait_impl_redundant_assoc_item::trait_impl_redundant_assoc_item(&ctx, &d),
AnyDiagnostic::TraitImplOrphan(d) => handlers::trait_impl_orphan::trait_impl_orphan(&ctx, &d),
AnyDiagnostic::TypedHole(d) => handlers::typed_hole::typed_hole(&ctx, &d),
- AnyDiagnostic::TypeMismatch(d) => handlers::type_mismatch::type_mismatch(&ctx, &d),
+ AnyDiagnostic::TypeMismatch(d) => match handlers::type_mismatch::type_mismatch(&ctx, &d) {
+ Some(diag) => diag,
+ None => continue,
+ },
AnyDiagnostic::UndeclaredLabel(d) => handlers::undeclared_label::undeclared_label(&ctx, &d),
AnyDiagnostic::UnimplementedBuiltinMacro(d) => handlers::unimplemented_builtin_macro::unimplemented_builtin_macro(&ctx, &d),
AnyDiagnostic::UnreachableLabel(d) => handlers::unreachable_label::unreachable_label(&ctx, &d),
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 3dc155efe9..fc49542e3c 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -4,9 +4,10 @@ mod overly_long_real_world_cases;
use hir::setup_tracing;
use ide_db::{
- LineIndexDatabase, RootDatabase,
+ RootDatabase,
assists::{AssistResolveStrategy, ExprFillDefaultMode},
base_db::SourceDatabase,
+ line_index,
};
use itertools::Itertools;
use stdx::trim_indent;
@@ -242,7 +243,7 @@ pub(crate) fn check_diagnostics_with_config(
.into_group_map();
for file_id in files {
let file_id = file_id.file_id(&db);
- let line_index = db.line_index(file_id);
+ let line_index = line_index(&db, file_id);
let mut actual = annotations.remove(&file_id).unwrap_or_default();
let mut expected = extract_annotations(db.file_text(file_id).text(&db));
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 c6831d818a..9883bcc84f 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
@@ -6,6 +6,7 @@ use crate::tests::check_diagnostics_with_disabled;
fn tracing_infinite_repeat() {
check_diagnostics_with_disabled(
r#"
+//- minicore: fn
//- /core.rs crate:core
#[rustc_builtin_macro]
#[macro_export]
diff --git a/crates/ide-ssr/src/from_comment.rs b/crates/ide-ssr/src/from_comment.rs
index 181cc74a51..83b8c3dc81 100644
--- a/crates/ide-ssr/src/from_comment.rs
+++ b/crates/ide-ssr/src/from_comment.rs
@@ -1,7 +1,7 @@
//! This module allows building an SSR MatchFinder by parsing the SSR rule
//! from a comment.
-use ide_db::{EditionedFileId, FilePosition, FileRange, RootDatabase, base_db::RootQueryDb};
+use ide_db::{EditionedFileId, FilePosition, FileRange, RootDatabase};
use syntax::{
TextRange,
ast::{self, AstNode, AstToken},
@@ -19,7 +19,7 @@ pub fn ssr_from_comment(
let comment = {
let file_id = EditionedFileId::current_edition(db, frange.file_id);
- let file = db.parse(file_id);
+ let file = file_id.parse(db);
file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast)
}?;
let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap();
diff --git a/crates/ide-ssr/src/matching.rs b/crates/ide-ssr/src/matching.rs
index 264f0660d7..ab5a0f70f5 100644
--- a/crates/ide-ssr/src/matching.rs
+++ b/crates/ide-ssr/src/matching.rs
@@ -7,7 +7,7 @@ use crate::{
resolving::{ResolvedPattern, ResolvedRule, UfcsCallInfo},
};
use hir::{FileRange, FindPathConfig, Semantics};
-use ide_db::{FxHashMap, base_db::RootQueryDb};
+use ide_db::{FxHashMap, base_db::all_crates};
use std::{cell::Cell, iter::Peekable};
use syntax::{
SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxKind, SyntaxNode, SyntaxToken,
@@ -621,7 +621,7 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
})?
.original;
let krate = self.sema.scope(expr.syntax()).map(|it| it.krate()).unwrap_or_else(|| {
- hir::Crate::from(*self.sema.db.all_crates().last().expect("no crate graph present"))
+ hir::Crate::from(*all_crates(self.sema.db).last().expect("no crate graph present"))
});
code_type
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 6fb8dedea4..f716f94d71 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -1,7 +1,7 @@
use hir::{HasSource, InFile, InRealFile, Semantics};
use ide_db::{
- FileId, FilePosition, FileRange, FxIndexSet, MiniCore, RootDatabase, defs::Definition,
- helpers::visit_file_defs,
+ FileId, FilePosition, FileRange, FxIndexSet, RootDatabase, defs::Definition,
+ helpers::visit_file_defs, ra_fixture::RaFixtureConfig,
};
use itertools::Itertools;
use syntax::{AstNode, TextRange, ast::HasName};
@@ -45,7 +45,7 @@ pub struct AnnotationConfig<'a> {
pub annotate_enum_variant_references: bool,
pub location: AnnotationLocation,
pub filter_adjacent_derive_implementations: bool,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
pub enum AnnotationLocation {
@@ -216,7 +216,12 @@ pub(crate) fn resolve_annotation(
*data = find_all_refs(
&Semantics::new(db),
pos,
- &FindAllRefsConfig { search_scope: None, minicore: config.minicore },
+ &FindAllRefsConfig {
+ search_scope: None,
+ ra_fixture: config.ra_fixture,
+ exclude_imports: false,
+ exclude_tests: false,
+ },
)
.map(|result| {
result
@@ -244,7 +249,7 @@ fn should_skip_runnable(kind: &RunnableKind, binary_target: bool) -> bool {
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
- use ide_db::MiniCore;
+ use ide_db::ra_fixture::RaFixtureConfig;
use crate::{Annotation, AnnotationConfig, fixture};
@@ -258,7 +263,7 @@ mod tests {
annotate_method_references: true,
annotate_enum_variant_references: true,
location: AnnotationLocation::AboveName,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
filter_adjacent_derive_implementations: false,
};
@@ -898,9 +903,6 @@ mod tests {
test_id: Path(
"tests::my_cool_test",
),
- attr: TestAttr {
- ignore: false,
- },
},
cfg: None,
update_test: UpdateTest {
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index aded911a8d..402764f112 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -4,9 +4,10 @@ use std::iter;
use hir::Semantics;
use ide_db::{
- FileRange, FxIndexMap, MiniCore, RootDatabase,
+ FileRange, FxIndexMap, RootDatabase,
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
+ ra_fixture::RaFixtureConfig,
search::FileReference,
};
use syntax::{AstNode, SyntaxKind::IDENT, ast};
@@ -25,7 +26,7 @@ pub struct CallItem {
pub struct CallHierarchyConfig<'a> {
/// Whether to exclude tests from the call hierarchy
pub exclude_tests: bool,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
pub(crate) fn call_hierarchy(
@@ -36,7 +37,7 @@ pub(crate) fn call_hierarchy(
goto_definition::goto_definition(
db,
position,
- &GotoDefinitionConfig { minicore: config.minicore },
+ &GotoDefinitionConfig { ra_fixture: config.ra_fixture },
)
}
@@ -174,7 +175,7 @@ impl CallLocations {
#[cfg(test)]
mod tests {
use expect_test::{Expect, expect};
- use ide_db::{FilePosition, MiniCore};
+ use ide_db::{FilePosition, ra_fixture::RaFixtureConfig};
use itertools::Itertools;
use crate::fixture;
@@ -197,7 +198,8 @@ mod tests {
)
}
- let config = crate::CallHierarchyConfig { exclude_tests, minicore: MiniCore::default() };
+ let config =
+ crate::CallHierarchyConfig { exclude_tests, ra_fixture: RaFixtureConfig::default() };
let (analysis, pos) = fixture::position(ra_fixture);
let mut navs = analysis.call_hierarchy(pos, &config).unwrap().unwrap().info;
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 33bed9501a..fd462d003d 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -17,7 +17,7 @@ use hir::{
};
use ide_db::{
RootDatabase,
- base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, RootQueryDb},
+ base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, toolchain_channel},
defs::{Definition, NameClass, NameRefClass},
documentation::{Documentation, HasDocs},
helpers::pick_best_token,
@@ -552,7 +552,7 @@ fn get_doc_base_urls(
.and_then(|it| Url::parse(&it).ok());
let krate = def.krate(db);
let channel = krate
- .and_then(|krate| db.toolchain_channel(krate.into()))
+ .and_then(|krate| toolchain_channel(db, krate.into()))
.unwrap_or(ReleaseChannel::Nightly)
.as_str();
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs
index 44285d9315..fb885c2ad1 100644
--- a/crates/ide/src/expand_macro.rs
+++ b/crates/ide/src/expand_macro.rs
@@ -54,8 +54,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
let InFile { file_id, value: tokens } =
hir::InMacroFile::new(macro_file, descended).upmap_once(db);
let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
- let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+ let attr = token.parent_ancestors().find_map(ast::Meta::cast)?;
let expansions = sema.expand_derive_macro(&attr)?;
+ let ast::Meta::TokenTreeMeta(attr) = attr else { return None };
let idx = attr
.token_tree()?
.token_trees_and_tokens()
@@ -235,7 +236,7 @@ fn _format(
file_id: FileId,
expansion: &str,
) -> Option<String> {
- use ide_db::base_db::RootQueryDb;
+ use ide_db::base_db::relevant_crates;
// hack until we get hygiene working (same character amount to preserve formatting as much as possible)
const DOLLAR_CRATE_REPLACE: &str = "__r_a_";
@@ -250,7 +251,7 @@ fn _format(
};
let expansion = format!("{prefix}{expansion}{suffix}");
- let &crate_id = db.relevant_crates(file_id).iter().next()?;
+ let &crate_id = relevant_crates(db, file_id).iter().next()?;
let edition = crate_id.data(db).edition;
#[allow(clippy::disallowed_methods)]
diff --git a/crates/ide/src/fetch_crates.rs b/crates/ide/src/fetch_crates.rs
index 956379e722..ad5af8bfe1 100644
--- a/crates/ide/src/fetch_crates.rs
+++ b/crates/ide/src/fetch_crates.rs
@@ -1,6 +1,6 @@
use ide_db::{
FileId, FxIndexSet, RootDatabase,
- base_db::{CrateOrigin, RootQueryDb},
+ base_db::{CrateOrigin, all_crates},
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -20,7 +20,7 @@ pub struct CrateInfo {
//
// ![Show Dependency Tree](https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png)
pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> {
- db.all_crates()
+ all_crates(db)
.iter()
.copied()
.map(|crate_id| (crate_id.data(db), crate_id.extra_data(db)))
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
index 3969490e8d..965190b27d 100644
--- a/crates/ide/src/folding_ranges.rs
+++ b/crates/ide/src/folding_ranges.rs
@@ -1,10 +1,11 @@
use ide_db::{FxHashSet, syntax_helpers::node_ext::vis_eq};
use syntax::{
- Direction, NodeOrToken, SourceFile,
- SyntaxKind::{self, *},
+ Direction, NodeOrToken, SourceFile, SyntaxElement,
+ SyntaxKind::*,
SyntaxNode, TextRange, TextSize,
ast::{self, AstNode, AstToken},
match_ast,
+ syntax_editor::Element,
};
use std::hash::Hash;
@@ -31,19 +32,33 @@ pub enum FoldKind {
TypeAliases,
ExternCrates,
// endregion: item runs
+ Stmt(ast::Stmt),
+ TailExpr(ast::Expr),
}
#[derive(Debug)]
pub struct Fold {
pub range: TextRange,
pub kind: FoldKind,
+ pub collapsed_text: Option<String>,
+}
+
+impl Fold {
+ pub fn new(range: TextRange, kind: FoldKind) -> Self {
+ Self { range, kind, collapsed_text: None }
+ }
+
+ pub fn with_text(mut self, text: Option<String>) -> Self {
+ self.collapsed_text = text;
+ self
+ }
}
// Feature: Folding
//
// Defines folding regions for curly braced blocks, runs of consecutive use, mod, const or static
// items, and `region` / `endregion` comment markers.
-pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
+pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec<Fold> {
let mut res = vec![];
let mut visited_comments = FxHashSet::default();
let mut visited_nodes = FxHashSet::default();
@@ -53,39 +68,41 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
for element in file.syntax().descendants_with_tokens() {
// Fold items that span multiple lines
- if let Some(kind) = fold_kind(element.kind()) {
+ if let Some(kind) = fold_kind(element.clone()) {
let is_multiline = match &element {
NodeOrToken::Node(node) => node.text().contains_char('\n'),
NodeOrToken::Token(token) => token.text().contains('\n'),
};
+
if is_multiline {
- // for the func with multiline param list
- if matches!(element.kind(), FN)
- && let NodeOrToken::Node(node) = &element
- && let Some(fn_node) = ast::Fn::cast(node.clone())
+ if let NodeOrToken::Node(node) = &element
+ && let Some(fn_) = ast::Fn::cast(node.clone())
{
- if !fn_node
+ if !fn_
.param_list()
.map(|param_list| param_list.syntax().text().contains_char('\n'))
- .unwrap_or(false)
+ .unwrap_or_default()
{
continue;
}
- if fn_node.body().is_some() {
+ if let Some(body) = fn_.body() {
// Get the actual start of the function (excluding doc comments)
- let fn_start = fn_node
+ let fn_start = fn_
.fn_token()
.map(|token| token.text_range().start())
.unwrap_or(node.text_range().start());
- res.push(Fold {
- range: TextRange::new(fn_start, node.text_range().end()),
- kind: FoldKind::Function,
- });
+ res.push(Fold::new(
+ TextRange::new(fn_start, body.syntax().text_range().end()),
+ FoldKind::Function,
+ ));
continue;
}
}
- res.push(Fold { range: element.text_range(), kind });
+
+ let collapsed_text = if add_collapsed_text { collapsed_text(&kind) } else { None };
+ let fold = Fold::new(element.text_range(), kind).with_text(collapsed_text);
+ res.push(fold);
continue;
}
}
@@ -102,15 +119,15 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
region_starts.push(comment.syntax().text_range().start());
} else if text.starts_with(REGION_END) {
if let Some(region) = region_starts.pop() {
- res.push(Fold {
- range: TextRange::new(region, comment.syntax().text_range().end()),
- kind: FoldKind::Region,
- })
+ res.push(Fold::new(
+ TextRange::new(region, comment.syntax().text_range().end()),
+ FoldKind::Region,
+ ));
}
} else if let Some(range) =
contiguous_range_for_comment(comment, &mut visited_comments)
{
- res.push(Fold { range, kind: FoldKind::Comment })
+ res.push(Fold::new(range, FoldKind::Comment));
}
}
}
@@ -123,37 +140,37 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
module,
&mut visited_nodes,
) {
- res.push(Fold { range, kind: FoldKind::Modules })
+ res.push(Fold::new(range, FoldKind::Modules));
}
},
ast::Use(use_) => {
if let Some(range) = contiguous_range_for_item_group(use_, &mut visited_nodes) {
- res.push(Fold { range, kind: FoldKind::Imports })
+ res.push(Fold::new(range, FoldKind::Imports));
}
},
ast::Const(konst) => {
if let Some(range) = contiguous_range_for_item_group(konst, &mut visited_nodes) {
- res.push(Fold { range, kind: FoldKind::Consts })
+ res.push(Fold::new(range, FoldKind::Consts));
}
},
ast::Static(statik) => {
if let Some(range) = contiguous_range_for_item_group(statik, &mut visited_nodes) {
- res.push(Fold { range, kind: FoldKind::Statics })
+ res.push(Fold::new(range, FoldKind::Statics));
}
},
ast::TypeAlias(alias) => {
if let Some(range) = contiguous_range_for_item_group(alias, &mut visited_nodes) {
- res.push(Fold { range, kind: FoldKind::TypeAliases })
+ res.push(Fold::new(range, FoldKind::TypeAliases));
}
},
ast::ExternCrate(extern_crate) => {
if let Some(range) = contiguous_range_for_item_group(extern_crate, &mut visited_nodes) {
- res.push(Fold { range, kind: FoldKind::ExternCrates })
+ res.push(Fold::new(range, FoldKind::ExternCrates));
}
},
ast::MatchArm(match_arm) => {
if let Some(range) = fold_range_for_multiline_match_arm(match_arm) {
- res.push(Fold {range, kind: FoldKind::MatchArm})
+ res.push(Fold::new(range, FoldKind::MatchArm));
}
},
_ => (),
@@ -166,8 +183,66 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
res
}
-fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
+fn collapsed_text(kind: &FoldKind) -> Option<String> {
match kind {
+ FoldKind::TailExpr(expr) => collapse_expr(expr.clone()),
+ FoldKind::Stmt(stmt) => {
+ match stmt {
+ ast::Stmt::ExprStmt(expr_stmt) => {
+ expr_stmt.expr().and_then(collapse_expr).map(|text| format!("{text};"))
+ }
+ ast::Stmt::LetStmt(let_stmt) => 'blk: {
+ if let_stmt.let_else().is_some() {
+ break 'blk None;
+ }
+
+ let Some(expr) = let_stmt.initializer() else {
+ break 'blk None;
+ };
+
+ // If the `let` statement spans multiple lines, we do not collapse it.
+ // We use the `eq_token` to check whether the `let` statement is a single line,
+ // as the formatter may place the initializer on a new line for better readability.
+ //
+ // Example:
+ // ```rust
+ // let complex_pat =
+ // complex_expr;
+ // ```
+ //
+ // In this case, we should generate the collapsed text.
+ let Some(eq_token) = let_stmt.eq_token() else {
+ break 'blk None;
+ };
+ let eq_token_offset =
+ eq_token.text_range().end() - let_stmt.syntax().text_range().start();
+ let text_until_eq_token = let_stmt.syntax().text().slice(..eq_token_offset);
+ if text_until_eq_token.contains_char('\n') {
+ break 'blk None;
+ }
+
+ collapse_expr(expr).map(|text| format!("{text_until_eq_token} {text};"))
+ }
+ // handling `items` in external matches.
+ ast::Stmt::Item(_) => None,
+ }
+ }
+ _ => None,
+ }
+}
+
+fn fold_kind(element: SyntaxElement) -> Option<FoldKind> {
+ // handle tail_expr
+ if let Some(node) = element.as_node()
+ // tail_expr -> stmt_list -> block
+ && let Some(block) = node.parent().and_then(|it| it.parent()).and_then(ast::BlockExpr::cast)
+ && let Some(tail_expr) = block.tail_expr()
+ && tail_expr.syntax() == node
+ {
+ return Some(FoldKind::TailExpr(tail_expr));
+ }
+
+ match element.kind() {
COMMENT => Some(FoldKind::Comment),
ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList),
ARRAY_EXPR => Some(FoldKind::Array),
@@ -185,10 +260,73 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
| MATCH_ARM_LIST
| VARIANT_LIST
| TOKEN_TREE => Some(FoldKind::Block),
+ EXPR_STMT | LET_STMT => Some(FoldKind::Stmt(ast::Stmt::cast(element.as_node()?.clone())?)),
_ => None,
}
}
+const COLLAPSE_EXPR_MAX_LEN: usize = 100;
+
+fn collapse_expr(expr: ast::Expr) -> Option<String> {
+ let mut text = String::with_capacity(COLLAPSE_EXPR_MAX_LEN * 2);
+
+ let mut preorder = expr.syntax().preorder_with_tokens();
+ while let Some(element) = preorder.next() {
+ match element {
+ syntax::WalkEvent::Enter(NodeOrToken::Node(node)) => {
+ if let Some(arg_list) = ast::ArgList::cast(node.clone()) {
+ let content = if arg_list.args().next().is_some() { "(…)" } else { "()" };
+ text.push_str(content);
+ preorder.skip_subtree();
+ } else if let Some(expr) = ast::Expr::cast(node) {
+ match expr {
+ ast::Expr::AwaitExpr(_)
+ | ast::Expr::BecomeExpr(_)
+ | ast::Expr::BinExpr(_)
+ | ast::Expr::BreakExpr(_)
+ | ast::Expr::CallExpr(_)
+ | ast::Expr::CastExpr(_)
+ | ast::Expr::ContinueExpr(_)
+ | ast::Expr::FieldExpr(_)
+ | ast::Expr::IndexExpr(_)
+ | ast::Expr::LetExpr(_)
+ | ast::Expr::Literal(_)
+ | ast::Expr::MethodCallExpr(_)
+ | ast::Expr::OffsetOfExpr(_)
+ | ast::Expr::ParenExpr(_)
+ | ast::Expr::PathExpr(_)
+ | ast::Expr::PrefixExpr(_)
+ | ast::Expr::RangeExpr(_)
+ | ast::Expr::RefExpr(_)
+ | ast::Expr::ReturnExpr(_)
+ | ast::Expr::TryExpr(_)
+ | ast::Expr::UnderscoreExpr(_)
+ | ast::Expr::YeetExpr(_)
+ | ast::Expr::YieldExpr(_) => {}
+
+ // Some other exprs (e.g. `while` loop) are too complex to have a collapsed text
+ _ => return None,
+ }
+ }
+ }
+ syntax::WalkEvent::Enter(NodeOrToken::Token(token)) => {
+ if !token.kind().is_trivia() {
+ text.push_str(token.text());
+ }
+ }
+ syntax::WalkEvent::Leave(_) => {}
+ }
+
+ if text.len() > COLLAPSE_EXPR_MAX_LEN {
+ return None;
+ }
+ }
+
+ text.shrink_to_fit();
+
+ Some(text)
+}
+
fn contiguous_range_for_item_group<N>(
first: N,
visited: &mut FxHashSet<SyntaxNode>,
@@ -297,7 +435,7 @@ fn contiguous_range_for_comment(
}
fn fold_range_for_multiline_match_arm(match_arm: ast::MatchArm) -> Option<TextRange> {
- if fold_kind(match_arm.expr()?.syntax().kind()).is_some() {
+ if fold_kind(match_arm.expr()?.syntax().syntax_element()).is_some() {
None
} else if match_arm.expr()?.syntax().text().contains_char('\n') {
Some(match_arm.expr()?.syntax().text_range())
@@ -314,10 +452,33 @@ mod tests {
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+ check_inner(ra_fixture, true);
+ }
+
+ fn check_without_collapsed_text(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+ check_inner(ra_fixture, false);
+ }
+
+ fn check_inner(ra_fixture: &str, enable_collapsed_text: bool) {
let (ranges, text) = extract_tags(ra_fixture, "fold");
+ let ranges: Vec<_> = ranges
+ .into_iter()
+ .map(|(range, text)| {
+ let (attr, collapsed_text) = match text {
+ Some(text) => match text.split_once(':') {
+ Some((attr, collapsed_text)) => {
+ (Some(attr.to_owned()), Some(collapsed_text.to_owned()))
+ }
+ None => (Some(text), None),
+ },
+ None => (None, None),
+ };
+ (range, attr, collapsed_text)
+ })
+ .collect();
let parse = SourceFile::parse(&text, span::Edition::CURRENT);
- let mut folds = folding_ranges(&parse.tree());
+ let mut folds = folding_ranges(&parse.tree(), enable_collapsed_text);
folds.sort_by_key(|fold| (fold.range.start(), fold.range.end()));
assert_eq!(
@@ -326,7 +487,7 @@ mod tests {
"The amount of folds is different than the expected amount"
);
- for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
+ for (fold, (range, attr, collapsed_text)) in folds.iter().zip(ranges) {
assert_eq!(fold.range.start(), range.start(), "mismatched start of folding ranges");
assert_eq!(fold.range.end(), range.end(), "mismatched end of folding ranges");
@@ -346,8 +507,15 @@ mod tests {
FoldKind::MatchArm => "matcharm",
FoldKind::Function => "function",
FoldKind::ExternCrates => "externcrates",
+ FoldKind::Stmt(_) => "stmt",
+ FoldKind::TailExpr(_) => "tailexpr",
};
assert_eq!(kind, &attr.unwrap());
+ if enable_collapsed_text {
+ assert_eq!(fold.collapsed_text, collapsed_text);
+ } else {
+ assert_eq!(fold.collapsed_text, None);
+ }
}
}
@@ -511,10 +679,10 @@ macro_rules! foo <fold block>{
check(
r#"
fn main() <fold block>{
- match 0 <fold block>{
+ <fold tailexpr>match 0 <fold block>{
0 => 0,
_ => 1,
- }</fold>
+ }</fold></fold>
}</fold>
"#,
);
@@ -525,7 +693,7 @@ fn main() <fold block>{
check(
r#"
fn main() <fold block>{
- match foo <fold block>{
+ <fold tailexpr>match foo <fold block>{
block => <fold block>{
}</fold>,
matcharm => <fold matcharm>some.
@@ -544,7 +712,7 @@ fn main() <fold block>{
structS => <fold matcharm>StructS <fold block>{
a: 31,
}</fold></fold>,
- }</fold>
+ }</fold></fold>
}</fold>
"#,
)
@@ -555,11 +723,11 @@ fn main() <fold block>{
check(
r#"
fn main() <fold block>{
- frobnicate<fold arglist>(
+ <fold tailexpr:frobnicate(…)>frobnicate<fold arglist>(
1,
2,
3,
- )</fold>
+ )</fold></fold>
}</fold>
"#,
)
@@ -698,4 +866,51 @@ type Foo<T, U> = foo<fold arglist><
"#,
);
}
+
+ #[test]
+ fn test_fold_tail_expr() {
+ check(
+ r#"
+fn f() <fold block>{
+ let x = 1;
+
+ <fold tailexpr:some_function().chain().method()>some_function()
+ .chain()
+ .method()</fold>
+}</fold>
+"#,
+ )
+ }
+
+ #[test]
+ fn test_fold_let_stmt_with_chained_methods() {
+ check(
+ r#"
+fn main() <fold block>{
+ <fold stmt:let result = some_value.method1().method2()?.method3();>let result = some_value
+ .method1()
+ .method2()?
+ .method3();</fold>
+
+ println!("{}", result);
+}</fold>
+"#,
+ )
+ }
+
+ #[test]
+ fn test_fold_let_stmt_with_chained_methods_without_collapsed_text() {
+ check_without_collapsed_text(
+ r#"
+fn main() <fold block>{
+ <fold stmt>let result = some_value
+ .method1()
+ .method2()?
+ .method3();</fold>
+
+ println!("{}", result);
+}</fold>
+"#,
+ )
+ }
}
diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs
index 375ce94bf6..d2b47a37c7 100644
--- a/crates/ide/src/goto_declaration.rs
+++ b/crates/ide/src/goto_declaration.rs
@@ -79,13 +79,13 @@ pub(crate) fn goto_declaration(
#[cfg(test)]
mod tests {
- use ide_db::{FileRange, MiniCore};
+ use ide_db::{FileRange, ra_fixture::RaFixtureConfig};
use itertools::Itertools;
use crate::{GotoDefinitionConfig, fixture};
const TEST_CONFIG: GotoDefinitionConfig<'_> =
- GotoDefinitionConfig { minicore: MiniCore::default() };
+ GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() };
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 3890bcad7f..4cdf0eac75 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -9,7 +9,7 @@ use crate::{
use hir::{
AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, ModuleDef, Semantics, sym,
};
-use ide_db::{MiniCore, ra_fixture::UpmapFromRaFixture};
+use ide_db::ra_fixture::{RaFixtureConfig, UpmapFromRaFixture};
use ide_db::{
RootDatabase, SymbolKind,
base_db::{AnchoredPath, SourceDatabase},
@@ -26,7 +26,7 @@ use syntax::{
#[derive(Debug)]
pub struct GotoDefinitionConfig<'a> {
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
// Feature: Go to Definition
@@ -105,7 +105,7 @@ pub(crate) fn goto_definition(
if let Some(token) = ast::String::cast(token.value.clone())
&& let Some(original_token) = ast::String::cast(original_token.clone())
&& let Some((analysis, fixture_analysis)) =
- Analysis::from_ra_fixture(sema, original_token, &token, config.minicore)
+ Analysis::from_ra_fixture(sema, original_token, &token, &config.ra_fixture)
&& let Some((virtual_file_id, file_offset)) = fixture_analysis.map_offset_down(offset)
{
return hir::attach_db_allow_change(&analysis.db, || {
@@ -605,11 +605,11 @@ fn expr_to_nav(
#[cfg(test)]
mod tests {
use crate::{GotoDefinitionConfig, fixture};
- use ide_db::{FileRange, MiniCore};
+ use ide_db::{FileRange, ra_fixture::RaFixtureConfig};
use itertools::Itertools;
const TEST_CONFIG: GotoDefinitionConfig<'_> =
- GotoDefinitionConfig { minicore: MiniCore::default() };
+ GotoDefinitionConfig { ra_fixture: RaFixtureConfig::default() };
#[track_caller]
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index c8e01e21ec..e6ef7b8949 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -2083,6 +2083,7 @@ fn test() {
fn return_in_macros() {
check(
r#"
+//- minicore: fn
macro_rules! N {
($i:ident, $x:expr, $blk:expr) => {
for $i in 0..$x {
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 958de8930d..df1fcecc99 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -8,11 +8,11 @@ use std::{iter, ops::Not};
use either::Either;
use hir::{DisplayTarget, GenericDef, GenericSubstitution, HasCrate, HasSource, Semantics};
use ide_db::{
- FileRange, FxIndexSet, MiniCore, Ranker, RootDatabase,
+ FileRange, FxIndexSet, Ranker, RootDatabase,
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
famous_defs::FamousDefs,
helpers::pick_best_token,
- ra_fixture::UpmapFromRaFixture,
+ ra_fixture::{RaFixtureConfig, UpmapFromRaFixture},
};
use itertools::{Itertools, multizip};
use macros::UpmapFromRaFixture;
@@ -44,7 +44,7 @@ pub struct HoverConfig<'a> {
pub max_enum_variants_count: Option<usize>,
pub max_subst_ty_len: SubstTyLen,
pub show_drop_glue: bool,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -221,7 +221,7 @@ fn hover_offset(
if let Some(literal) = ast::String::cast(original_token.clone())
&& let Some((analysis, fixture_analysis)) =
- Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture)
{
let (virtual_file_id, virtual_offset) = fixture_analysis.map_offset_down(offset)?;
return analysis
@@ -422,7 +422,7 @@ fn hover_ranged(
Either::Left(ast::Expr::Literal(literal)) => {
if let Some(literal) = ast::String::cast(literal.token())
&& let Some((analysis, fixture_analysis)) =
- Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture)
{
let (virtual_file_id, virtual_range) = fixture_analysis.map_range_down(range)?;
return analysis
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index af78e9a40c..4d712bf0f0 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -1009,8 +1009,9 @@ fn closure_ty(
display_target: DisplayTarget,
) -> Option<HoverResult> {
let c = original.as_closure()?;
- let mut captures_rendered = c.captured_items(sema.db)
- .into_iter()
+ let captures = c.captured_items(sema.db);
+ let mut captures_rendered = captures
+ .iter()
.map(|it| {
let borrow_kind = match it.kind() {
CaptureKind::SharedRef => "immutable borrow",
@@ -1018,7 +1019,7 @@ fn closure_ty(
CaptureKind::MutableRef => "mutable borrow",
CaptureKind::Move => "move",
};
- format!("* `{}` by {}", it.display_place(sema.db), borrow_kind)
+ format!("* `{}` by {}", it.display_place_source_code(sema.db, display_target.edition), borrow_kind)
})
.join("\n");
if captures_rendered.trim().is_empty() {
@@ -1031,8 +1032,8 @@ fn closure_ty(
}
};
walk_and_push_ty(sema.db, original, &mut push_new_def);
- c.capture_types(sema.db).into_iter().for_each(|ty| {
- walk_and_push_ty(sema.db, &ty, &mut push_new_def);
+ captures.iter().for_each(|capture| {
+ walk_and_push_ty(sema.db, &capture.ty(sema.db), &mut push_new_def);
});
let adjusted = if let Some(adjusted_ty) = adjusted {
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 7fbbc576dd..491471428f 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -1,5 +1,5 @@
use expect_test::{Expect, expect};
-use ide_db::{FileRange, MiniCore, base_db::SourceDatabase};
+use ide_db::{FileRange, base_db::SourceDatabase, ra_fixture::RaFixtureConfig};
use syntax::TextRange;
use crate::{
@@ -25,7 +25,7 @@ const HOVER_BASE_CONFIG: HoverConfig<'_> = HoverConfig {
max_enum_variants_count: Some(5),
max_subst_ty_len: super::SubstTyLen::Unlimited,
show_drop_glue: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
fn check_hover_no_result(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
@@ -423,7 +423,7 @@ fn main() {
## Captures
* `x.f1` by move
- * `(*x.f2.0.0).f` by mutable borrow
+ * `x.f2.0.0.f` by mutable borrow
"#]],
);
check(
@@ -3526,9 +3526,6 @@ fn foo_$0test() {}
test_id: Path(
"foo_test",
),
- attr: TestAttr {
- ignore: false,
- },
},
cfg: None,
update_test: UpdateTest {
@@ -10707,9 +10704,6 @@ macro_rules! str {
test_id: Path(
"test",
),
- attr: TestAttr {
- ignore: false,
- },
},
cfg: None,
update_test: UpdateTest {
@@ -10778,9 +10772,6 @@ pub use expect_test;
test_id: Path(
"test",
),
- attr: TestAttr {
- ignore: false,
- },
},
cfg: None,
update_test: UpdateTest {
@@ -11088,6 +11079,58 @@ impl PublicFlags for NoteDialects {
}
#[test]
+fn hover_recursive_const_fn() {
+ check(
+ r#"
+//- minicore: option
+enum Child {
+ Static { child: &'static MyEnum },
+}
+
+enum MyEnum {
+ Unit,
+ Array(Child),
+}
+
+impl MyEnum {
+ pub const fn static_array(child: &'static MyEnum) -> Self {
+ MyEnum::Array(Child::Static { child })
+ }
+}
+
+pub trait MyTrait {
+ const MY_CONST: &'static MyEnum;
+}
+
+impl<T> MyTrait for Option<T> where T: MyTrait {
+ const MY_CONST: &'static MyEnum = &MyEnum::static_array(T::MY_CONST);
+}
+
+impl MyTrait for () {
+ const MY_CONST: &'static MyEnum = &MyEnum::Unit;
+}
+
+pub struct Address;
+
+impl MyTrait for Address {
+ const MY_CONST$0: &'static MyEnum = (<Option<()> as MyTrait>::MY_CONST);
+}
+ "#,
+ expect![[r#"
+ *MY_CONST*
+
+ ```rust
+ ra_test_fixture::Address
+ ```
+
+ ```rust
+ const MY_CONST: &'static MyEnum = &Array(Static { child: &Unit })
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn bounds_from_container_do_not_panic() {
check(
r#"
@@ -11404,3 +11447,299 @@ pub trait MyTrait {
"#]],
);
}
+
+#[test]
+fn test_hover_doc_attr_macro_generated_method_stringify_self_ty() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[rustc_builtin_macro]
+macro_rules! stringify {}
+
+macro_rules! bar {
+ ($SelfT:ident) => {
+ struct $SelfT;
+ impl $SelfT {
+ #[doc = concat!("Do the foo for ", stringify!($SelfT))]
+ fn foo(&self) {}
+ }
+ }
+}
+
+bar!(Bar);
+
+fn foo() { let bar = Bar; bar.fo$0o(); }
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ ra_test_fixture::Bar
+ ```
+
+ ```rust
+ fn foo(&self)
+ ```
+
+ ---
+
+ Do the foo for Bar
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_macro_argument_expr_issue_7688() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+macro_rules! doc_comment {
+ ($x:expr, $($tt:tt)*) => {
+ #[doc = $x]
+ $($tt)*
+ };
+}
+
+doc_comment! {
+ concat!("Hello", " world"),
+ struct Ba$0r;
+}
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Bar
+ ```
+
+ ---
+
+ size = 0, align = 1, no Drop
+
+ ---
+
+ Hello world
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_concat_macro() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+#[doc = concat!("Hello", " ", "World")]
+struct Ba$0r;
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Bar
+ ```
+
+ ---
+
+ size = 0, align = 1, no Drop
+
+ ---
+
+ Hello World
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_include_str_macro() {
+ check(
+ r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include_str {}
+
+#[doc = include_str!("docs.md")]
+struct Ba$0r;
+
+//- /docs.md
+Included docs from file.
+Multiple lines of docs.
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Bar
+ ```
+
+ ---
+
+ size = 0, align = 1, no Drop
+
+ ---
+
+ Included docs from file.
+ Multiple lines of docs.
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_user_macro_returning_string() {
+ check(
+ r#"
+macro_rules! doc_str {
+ () => { "Documentation from macro" };
+}
+
+#[doc = doc_str!()]
+struct Ba$0r;
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Bar
+ ```
+
+ ---
+
+ size = 0, align = 1, no Drop
+
+ ---
+
+ Documentation from macro
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_mixed_literal_and_macro() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+/// First line
+#[doc = concat!("Second", " line")]
+struct Ba$0r;
+"#,
+ expect![[r#"
+ *Bar*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ struct Bar
+ ```
+
+ ---
+
+ size = 0, align = 1, no Drop
+
+ ---
+
+ First line
+ Second line
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_field_with_macro() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+struct Bar {
+ #[doc = concat!("field", " docs")]
+ ba$0z: i32,
+}
+"#,
+ expect![[r#"
+ *baz*
+
+ ```rust
+ ra_test_fixture::Bar
+ ```
+
+ ```rust
+ baz: i32
+ ```
+
+ ---
+
+ size = 4, align = 4, offset = 0, no Drop
+
+ ---
+
+ field docs
+ "#]],
+ );
+}
+
+#[test]
+fn test_hover_doc_attr_macro_on_outlined_mod() {
+ // Outer doc-macro on `mod foo;` resolves from inside the module's scope
+ // (matching rustc behavior), and combines with inner `//!` docs from the module file.
+ check(
+ r#"
+//- /main.rs
+mod mac {
+ macro_rules! doc_str {
+ () => { "expanded from macro" };
+ }
+ pub(crate) use doc_str;
+}
+
+/// plain outer doc
+#[doc = super::mac::doc_str!()]
+mod foo$0;
+
+//- /foo.rs
+//! inner module docs
+pub struct Bar;
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ ra_test_fixture
+ ```
+
+ ```rust
+ mod foo
+ ```
+
+ ---
+
+ plain outer doc
+ expanded from macro
+ inner module docs
+ "#]],
+ );
+}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index a58dc6f030..0d2239c71f 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -9,7 +9,8 @@ use hir::{
HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
};
use ide_db::{
- FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder,
+ FileRange, RootDatabase, famous_defs::FamousDefs, ra_fixture::RaFixtureConfig,
+ text_edit::TextEditBuilder,
};
use ide_db::{FxHashSet, text_edit::TextEdit};
use itertools::Itertools;
@@ -234,7 +235,7 @@ fn hints(
param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
}
ast::Expr::ClosureExpr(it) => {
- closure_captures::hints(hints, famous_defs, config, it.clone());
+ closure_captures::hints(hints, famous_defs, config, it.clone(), file_id.edition(sema.db));
closure_ret::hints(hints, famous_defs, config, display_target, it)
},
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, it),
@@ -302,6 +303,7 @@ fn hints(
pub struct InlayHintsConfig<'a> {
pub render_colons: bool,
pub type_hints: bool,
+ pub type_hints_placement: TypeHintsPlacement,
pub sized_bound: bool,
pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool,
@@ -328,7 +330,13 @@ pub struct InlayHintsConfig<'a> {
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum TypeHintsPlacement {
+ Inline,
+ EndOfLine,
}
impl InlayHintsConfig<'_> {
@@ -899,7 +907,7 @@ mod tests {
use expect_test::Expect;
use hir::ClosureStyle;
- use ide_db::MiniCore;
+ use ide_db::ra_fixture::RaFixtureConfig;
use itertools::Itertools;
use test_utils::extract_annotations;
@@ -907,12 +915,15 @@ mod tests {
use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode};
use crate::{LifetimeElisionHints, fixture, inlay_hints::InlayHintsConfig};
- use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve};
+ use super::{
+ ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve, TypeHintsPlacement,
+ };
pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never,
render_colons: false,
type_hints: false,
+ type_hints_placement: TypeHintsPlacement::Inline,
parameter_hints: false,
parameter_hints_for_missing_arguments: false,
sized_bound: false,
@@ -942,10 +953,11 @@ mod tests {
implicit_drop_hints: false,
implied_dyn_trait_hints: false,
range_exclusive_hints: false,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig {
type_hints: true,
+ type_hints_placement: TypeHintsPlacement::Inline,
parameter_hints: true,
chaining_hints: true,
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
@@ -1073,9 +1085,10 @@ fn foo() {
fn closure_dependency_cycle_no_panic() {
check(
r#"
+//- minicore: fn
fn foo() {
let closure;
- // ^^^^^^^ impl Fn()
+ // ^^^^^^^ impl FnOnce()
closure = || {
closure();
};
@@ -1083,9 +1096,9 @@ fn foo() {
fn bar() {
let closure1;
- // ^^^^^^^^ impl Fn()
+ // ^^^^^^^^ impl FnOnce()
let closure2;
- // ^^^^^^^^ impl Fn()
+ // ^^^^^^^^ impl FnOnce()
closure1 = || {
closure2();
};
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index caf7cc714d..f194bb183e 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -8,10 +8,12 @@ use ide_db::{RootDatabase, famous_defs::FamousDefs};
use itertools::Itertools;
use syntax::{
+ TextRange,
ast::{self, AstNode, HasGenericArgs, HasName},
match_ast,
};
+use super::TypeHintsPlacement;
use crate::{
InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind,
inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit},
@@ -29,6 +31,7 @@ pub(super) fn hints(
}
let parent = pat.syntax().parent()?;
+ let mut enclosing_let_stmt = None;
let type_ascriptable = match_ast! {
match parent {
ast::Param(it) => {
@@ -41,6 +44,7 @@ pub(super) fn hints(
Some(it.colon_token())
},
ast::LetStmt(it) => {
+ enclosing_let_stmt = Some(it.clone());
if config.hide_closure_initialization_hints
&& let Some(ast::Expr::ClosureExpr(closure)) = it.initializer()
&& closure_has_block_body(&closure) {
@@ -101,16 +105,26 @@ pub(super) fn hints(
Some(name) => name.syntax().text_range(),
None => pat.syntax().text_range(),
};
+ let mut range = match type_ascriptable {
+ Some(Some(t)) => text_range.cover(t.text_range()),
+ _ => text_range,
+ };
+
+ let mut pad_left = !render_colons;
+ if matches!(config.type_hints_placement, TypeHintsPlacement::EndOfLine)
+ && let Some(let_stmt) = enclosing_let_stmt
+ {
+ let stmt_range = let_stmt.syntax().text_range();
+ range = TextRange::new(range.start(), stmt_range.end());
+ pad_left = true;
+ }
acc.push(InlayHint {
- range: match type_ascriptable {
- Some(Some(t)) => text_range.cover(t.text_range()),
- _ => text_range,
- },
+ range,
kind: InlayKind::Type,
label,
text_edit,
position: InlayHintPosition::After,
- pad_left: !render_colons,
+ pad_left,
pad_right: false,
resolve_parent: Some(pat.syntax().text_range()),
});
@@ -182,6 +196,7 @@ mod tests {
use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig};
+ use super::TypeHintsPlacement;
use crate::inlay_hints::tests::{
DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit,
check_with_config,
@@ -205,6 +220,76 @@ fn main() {
}
#[test]
+ fn type_hints_end_of_line_placement() {
+ let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG };
+ config.type_hints_placement = TypeHintsPlacement::EndOfLine;
+ check_expect(
+ config,
+ r#"
+fn main() {
+ let foo = 92_i32;
+}
+ "#,
+ expect![[r#"
+ [
+ (
+ 20..33,
+ [
+ "i32",
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
+ fn type_hints_end_of_line_placement_chain_expr() {
+ let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG };
+ config.type_hints_placement = TypeHintsPlacement::EndOfLine;
+ check_expect(
+ config,
+ r#"
+fn main() {
+ struct Builder;
+ impl Builder {
+ fn iter(self) -> Builder { Builder }
+ fn map(self) -> Builder { Builder }
+ }
+ fn make() -> Builder { Builder }
+
+ let foo = make()
+ .iter()
+ .map();
+}
+"#,
+ expect![[r#"
+ [
+ (
+ 192..236,
+ [
+ InlayHintLabelPart {
+ text: "Builder",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 23..30,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
fn type_hints_bindings_after_at() {
check_types(
r#"
@@ -821,7 +906,7 @@ fn fallible() -> ControlFlow<()> {
check_with_config(
InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
-//- minicore: fn
+//- minicore: fn, add, builtin_impls
fn main() {
let x = || 2;
//^ impl Fn() -> i32
@@ -843,7 +928,7 @@ fn main() {
..DISABLED_CONFIG
},
r#"
-//- minicore: fn
+//- minicore: fn, add, builtin_impls
fn main() {
let x = || 2;
//^ || -> i32
@@ -865,7 +950,7 @@ fn main() {
..DISABLED_CONFIG
},
r#"
-//- minicore: fn
+//- minicore: fn, add, builtin_impls
fn main() {
let x = || 2;
//^ …
@@ -1009,6 +1094,7 @@ fn test<F>(v: S<(S<i32>, S<()>)>, f: F) {
check_edit(
TEST_CONFIG,
r#"
+//- minicore: fn
fn test<T>(t: T) {
let f = |a, b, c| {};
let result = f(42, "", t);
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index cf3149c946..4b06f83971 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -2,13 +2,13 @@
use hir::DisplayTarget;
use ide_db::famous_defs::FamousDefs;
use syntax::{
- Direction, NodeOrToken, SyntaxKind, T,
+ Direction, NodeOrToken, SyntaxKind, T, TextRange,
ast::{self, AstNode},
};
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
-use super::label_of_ty;
+use super::{TypeHintsPlacement, label_of_ty};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -40,13 +40,14 @@ pub(super) fn hints(
// Chaining can be defined as an expression whose next sibling tokens are newline and dot
// Ignoring extra whitespace and comments
- let next = tokens.next()?.kind();
- if next == SyntaxKind::WHITESPACE {
- let mut next_next = tokens.next()?.kind();
- while next_next == SyntaxKind::WHITESPACE {
- next_next = tokens.next()?.kind();
+ let next_token = tokens.next()?;
+ if next_token.kind() == SyntaxKind::WHITESPACE {
+ let newline_token = next_token;
+ let mut next_next = tokens.next()?;
+ while next_next.kind() == SyntaxKind::WHITESPACE {
+ next_next = tokens.next()?;
}
- if next_next == T![.] {
+ if next_next.kind() == T![.] {
let ty = sema.type_of_expr(desc_expr)?.original;
if ty.is_unknown() {
return None;
@@ -58,8 +59,18 @@ pub(super) fn hints(
return None;
}
let label = label_of_ty(famous_defs, config, &ty, display_target)?;
+ let range = {
+ let mut range = expr.syntax().text_range();
+ if config.type_hints_placement == TypeHintsPlacement::EndOfLine {
+ range = TextRange::new(
+ range.start(),
+ newline_token.text_range().start().max(range.end()),
+ );
+ }
+ range
+ };
acc.push(InlayHint {
- range: expr.syntax().text_range(),
+ range,
kind: InlayKind::Chaining,
label,
text_edit: None,
@@ -79,7 +90,7 @@ mod tests {
use ide_db::text_edit::{TextRange, TextSize};
use crate::{
- InlayHintsConfig, fixture,
+ InlayHintsConfig, TypeHintsPlacement, fixture,
inlay_hints::{
LazyProperty,
tests::{DISABLED_CONFIG, TEST_CONFIG, check_expect, check_with_config},
@@ -686,4 +697,80 @@ fn main() {
"#]],
);
}
+
+ #[test]
+ fn chaining_hints_end_of_line_placement() {
+ check_expect(
+ InlayHintsConfig {
+ chaining_hints: true,
+ type_hints_placement: TypeHintsPlacement::EndOfLine,
+ ..DISABLED_CONFIG
+ },
+ r#"
+fn main() {
+ let baz = make()
+ .into_bar()
+ .into_baz();
+}
+
+struct Foo;
+struct Bar;
+struct Baz;
+
+impl Foo {
+ fn into_bar(self) -> Bar { Bar }
+}
+
+impl Bar {
+ fn into_baz(self) -> Baz { Baz }
+}
+
+fn make() -> Foo {
+ Foo
+}
+"#,
+ expect![[r#"
+ [
+ (
+ 26..52,
+ [
+ InlayHintLabelPart {
+ text: "Bar",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 96..99,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ (
+ 26..32,
+ [
+ InlayHintLabelPart {
+ text: "Foo",
+ linked_location: Some(
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 84..87,
+ },
+ ),
+ ),
+ tooltip: "",
+ },
+ ],
+ ),
+ ]
+ "#]],
+ );
+ }
}
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index f8d4ddc6eb..f4ac9c42f4 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -3,6 +3,7 @@
//! 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};
@@ -15,6 +16,7 @@ pub(super) fn hints(
FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig<'_>,
closure: ast::ClosureExpr,
+ edition: Edition,
) -> Option<()> {
if !config.closure_capture_hints {
return None;
@@ -60,7 +62,7 @@ pub(super) fn hints(
hir::CaptureKind::MutableRef => "&mut ",
hir::CaptureKind::Move => "",
},
- capture.display_place(sema.db)
+ capture.display_place_source_code(sema.db, edition)
);
if never!(label.is_empty()) {
continue;
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index 4982b60f1d..7a8a6eb84a 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -31,7 +31,6 @@ pub(super) fn fn_hints(
let param_list = func.param_list()?;
let generic_param_list = func.generic_param_list();
let ret_type = func.ret_type();
- let self_param = param_list.self_param().filter(|it| it.amp_token().is_some());
let gpl_append_range = func.name()?.syntax().text_range();
hints_(
acc,
@@ -49,7 +48,7 @@ pub(super) fn fn_hints(
}),
generic_param_list,
ret_type,
- self_param,
+ param_list.self_param(),
|acc, allocated_lifetimes| {
acc.push(InlayHint {
range: gpl_append_range,
@@ -208,6 +207,20 @@ fn hints_(
Some(lt) => matches!(lt.text().as_str(), "'_"),
None => true,
};
+ let self_param = self_param.and_then(|it| {
+ if it.colon_token().is_none() {
+ return Some((it.amp_token(), it.lifetime()));
+ }
+ it.ty().map(|ty| {
+ let ref_type = ty.syntax().descendants().find_map(ast::RefType::cast);
+ let lifetime = ref_type
+ .as_ref()
+ .and_then(|it| it.lifetime())
+ .or_else(|| ty.syntax().descendants().find_map(ast::Lifetime::cast));
+ (ref_type.and_then(|it| it.amp_token()), lifetime)
+ })
+ });
+ let self_param = self_param.filter(|(amp, lt)| amp.is_some() || lt.is_some());
let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
range: t.text_range(),
@@ -222,10 +235,9 @@ fn hints_(
let potential_lt_refs = {
let mut acc: Vec<_> = vec![];
- if let Some(self_param) = &self_param {
- let lifetime = self_param.lifetime();
+ if let Some((amp_token, lifetime)) = self_param.clone() {
let is_elided = is_elided(&lifetime);
- acc.push((None, self_param.amp_token(), lifetime, is_elided));
+ acc.push((None, amp_token, lifetime, is_elided));
}
params.for_each(|(name, ty)| {
// FIXME: check path types
@@ -240,17 +252,14 @@ fn hints_(
is_trivial = false;
true
}
- ast::Type::PathType(t) => {
+ ast::Type::PathType(t)
if t.path()
.and_then(|it| it.segment())
.and_then(|it| it.parenthesized_arg_list())
- .is_some()
- {
- is_trivial = false;
- true
- } else {
- false
- }
+ .is_some() =>
+ {
+ is_trivial = false;
+ true
}
_ => false,
})
@@ -339,17 +348,14 @@ fn hints_(
is_trivial = false;
true
}
- ast::Type::PathType(t) => {
+ ast::Type::PathType(t)
if t.path()
.and_then(|it| it.segment())
.and_then(|it| it.parenthesized_arg_list())
- .is_some()
- {
- is_trivial = false;
- true
- } else {
- false
- }
+ .is_some() =>
+ {
+ is_trivial = false;
+ true
}
_ => false,
})
@@ -439,6 +445,9 @@ fn nested_out(a: &()) -> & &X< &()>{}
//^'0 ^'0 ^'0 ^'0
impl () {
+ fn foo(self, x: &()) -> &() {}
+ // ^^^<'0>
+ // ^'0 ^'0
fn foo(&self) {}
// ^^^<'0>
// ^'0
@@ -448,6 +457,10 @@ impl () {
fn foo(&self, a: &()) -> &() {}
// ^^^<'0, '1>
// ^'0 ^'1 ^'0
+ fn foo(self: &Self, a: &()) -> &() {}
+ // ^^^<'0, '1>
+ // ^'0 ^'1 ^'0
+
}
"#,
);
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 08588bbed0..c780ce5864 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -37,8 +37,9 @@ pub(super) fn hints(
let hints = callable
.params()
.into_iter()
- .zip(arg_list.args())
+ .zip(arg_list.args_maybe_empty())
.filter_map(|(p, arg)| {
+ let arg = arg?;
// Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?;
if range.file_id != file_id {
@@ -426,6 +427,7 @@ fn main() {
fn param_hints_on_closure() {
check_params(
r#"
+//- minicore: fn
fn main() {
let clo = |a: u8, b: u8| a + b;
clo(
@@ -562,6 +564,19 @@ fn main() {
}
#[test]
+ fn param_name_hints_show_after_empty_arg() {
+ check_params(
+ r#"pub fn test(a: i32, b: i32, c: i32) {}
+fn main() {
+ test(, 2,);
+ //^ b
+ test(, , 3);
+ //^ c
+}"#,
+ )
+ }
+
+ #[test]
fn function_call_parameter_hint() {
check_params(
r#"
diff --git a/crates/ide/src/inlay_hints/ra_fixture.rs b/crates/ide/src/inlay_hints/ra_fixture.rs
index bee1841642..701c8a8612 100644
--- a/crates/ide/src/inlay_hints/ra_fixture.rs
+++ b/crates/ide/src/inlay_hints/ra_fixture.rs
@@ -16,7 +16,7 @@ pub(super) fn hints(
let file_id = file_id.file_id(sema.db);
let literal = ast::String::cast(literal.token())?;
let (analysis, fixture_analysis) =
- Analysis::from_ra_fixture(sema, literal.clone(), &literal, config.minicore)?;
+ Analysis::from_ra_fixture(sema, literal.clone(), &literal, &config.ra_fixture)?;
for virtual_file_id in fixture_analysis.files() {
acc.extend(
analysis
diff --git a/crates/ide/src/interpret.rs b/crates/ide/src/interpret.rs
index 3741822547..994d325ccc 100644
--- a/crates/ide/src/interpret.rs
+++ b/crates/ide/src/interpret.rs
@@ -1,5 +1,5 @@
use hir::{ConstEvalError, DefWithBody, DisplayTarget, Semantics};
-use ide_db::{FilePosition, LineIndexDatabase, RootDatabase, base_db::SourceDatabase};
+use ide_db::{FilePosition, RootDatabase, base_db::SourceDatabase, line_index};
use std::time::{Duration, Instant};
use stdx::format_to;
use syntax::{AstNode, TextRange, algo::ancestors_at_offset, ast};
@@ -40,7 +40,7 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<(Dura
let path = source_root.path_for_file(&file_id).map(|x| x.to_string());
let path = path.as_deref().unwrap_or("<unknown file>");
- match db.line_index(file_id).try_line_col(text_range.start()) {
+ match line_index(db, file_id).try_line_col(text_range.start()) {
Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col),
None => format!("file://{path} range {text_range:?}"),
}
@@ -68,7 +68,7 @@ pub(crate) fn render_const_eval_error(
let source_root = db.source_root(source_root).source_root(db);
let path = source_root.path_for_file(&file_id).map(|x| x.to_string());
let path = path.as_deref().unwrap_or("<unknown file>");
- match db.line_index(file_id).try_line_col(text_range.start()) {
+ match line_index(db, file_id).try_line_col(text_range.start()) {
Some(line_col) => format!("file://{path}:{}:{}", line_col.line + 1, line_col.col),
None => format!("file://{path} range {text_range:?}"),
}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 81a771fec8..0af2a1f820 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -63,15 +63,18 @@ use std::panic::{AssertUnwindSafe, UnwindSafe};
use cfg::CfgOptions;
use fetch_crates::CrateInfo;
use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym};
+use ide_db::base_db::relevant_crates;
+use ide_db::base_db::salsa::Durability;
+use ide_db::line_index;
+use ide_db::ra_fixture::RaFixtureAnalysis;
use ide_db::{
- FxHashMap, FxIndexSet, LineIndexDatabase,
+ FxHashMap, FxIndexSet,
base_db::{
- CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath,
+ CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath,
salsa::{Cancelled, Database},
},
prime_caches, symbol_index,
};
-use ide_db::{MiniCore, ra_fixture::RaFixtureAnalysis};
use macros::UpmapFromRaFixture;
use syntax::{AstNode, SourceFile, ast};
use triomphe::Arc;
@@ -96,7 +99,7 @@ pub use crate::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
- LifetimeElisionHints,
+ LifetimeElisionHints, TypeHintsPlacement,
},
join_lines::JoinLinesConfig,
markup::Markup,
@@ -125,7 +128,8 @@ pub use ide_assists::{
};
pub use ide_completion::{
CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
- CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet, SnippetScope,
+ CompletionItemImport, CompletionItemKind, CompletionItemRefMode, CompletionRelevance, Snippet,
+ SnippetScope,
};
pub use ide_db::{
FileId, FilePosition, FileRange, RootDatabase, Severity, SymbolKind,
@@ -135,6 +139,7 @@ pub use ide_db::{
label::Label,
line_index::{LineCol, LineIndex},
prime_caches::ParallelPrimeCachesProgress,
+ ra_fixture::RaFixtureConfig,
search::{ReferenceCategory, SearchScope},
source_change::{FileSystemEdit, SnippetEdit, SourceChange},
symbol_index::Query,
@@ -200,10 +205,18 @@ impl AnalysisHost {
self.db.per_query_memory_usage()
}
pub fn trigger_cancellation(&mut self) {
- self.db.trigger_cancellation();
+ // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
+ // the revision bump there is a reset marker for clearing fixpoint poisoning.
+ // That is `trigger_cancellation` is currently bugged wrt to cancellation.
+ // self.db.trigger_cancellation();
+ self.db.synthetic_write(Durability::LOW);
}
pub fn trigger_garbage_collection(&mut self) {
- self.db.trigger_lru_eviction();
+ // We need to do a synthetic write right now due to how fixpoint cycles handle cancellation
+ // the revision bump there is a reset marker for clearing fixpoint poisoning.
+ // That is `trigger_lru_eviction` is currently bugged wrt to cancellation.
+ // self.db.trigger_lru_eviction();
+ self.db.synthetic_write(Durability::LOW);
// SAFETY: `trigger_lru_eviction` triggers cancellation, so all running queries were canceled.
unsafe { hir::collect_ty_garbage() };
}
@@ -289,9 +302,9 @@ impl Analysis {
sema: &Semantics<'_, RootDatabase>,
literal: ast::String,
expanded: &ast::String,
- minicore: MiniCore<'_>,
+ config: &RaFixtureConfig<'_>,
) -> Option<(Analysis, RaFixtureAnalysis)> {
- Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, minicore, &mut |_| {})
+ Self::from_ra_fixture_with_on_cursor(sema, literal, expanded, config, &mut |_| {})
}
/// Like [`Analysis::from_ra_fixture()`], but also calls `on_cursor` with the cursor position.
@@ -299,11 +312,11 @@ impl Analysis {
sema: &Semantics<'_, RootDatabase>,
literal: ast::String,
expanded: &ast::String,
- minicore: MiniCore<'_>,
+ config: &RaFixtureConfig<'_>,
on_cursor: &mut dyn FnMut(TextRange),
) -> Option<(Analysis, RaFixtureAnalysis)> {
let analysis =
- RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, minicore, on_cursor)?;
+ RaFixtureAnalysis::analyze_ra_fixture(sema, literal, expanded, config, on_cursor)?;
Some((Analysis { db: analysis.db.clone() }, analysis))
}
@@ -341,7 +354,7 @@ impl Analysis {
self.with_db(|db| {
let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
- db.parse(editioned_file_id_wrapper).tree()
+ editioned_file_id_wrapper.parse(db).tree()
})
}
@@ -356,7 +369,7 @@ impl Analysis {
/// Gets the file's `LineIndex`: data structure to convert between absolute
/// offsets and line/column representation.
pub fn file_line_index(&self, file_id: FileId) -> Cancellable<Arc<LineIndex>> {
- self.with_db(|db| db.line_index(file_id))
+ self.with_db(|db| line_index(db, file_id).clone())
}
/// Selects the next syntactic nodes encompassing the range.
@@ -369,7 +382,7 @@ impl Analysis {
pub fn matching_brace(&self, position: FilePosition) -> Cancellable<Option<TextSize>> {
self.with_db(|db| {
let file_id = EditionedFileId::current_edition(&self.db, position.file_id);
- let parse = db.parse(file_id);
+ let parse = file_id.parse(db);
let file = parse.tree();
matching_brace::matching_brace(&file, position.offset)
})
@@ -412,7 +425,7 @@ impl Analysis {
}
/// Renders the crate graph to GraphViz "dot" syntax.
- pub fn view_crate_graph(&self, full: bool) -> Cancellable<Result<String, String>> {
+ pub fn view_crate_graph(&self, full: bool) -> Cancellable<String> {
self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
}
@@ -430,7 +443,7 @@ impl Analysis {
self.with_db(|db| {
let editioned_file_id_wrapper =
EditionedFileId::current_edition(&self.db, frange.file_id);
- let parse = db.parse(editioned_file_id_wrapper);
+ let parse = editioned_file_id_wrapper.parse(db);
join_lines::join_lines(config, &parse.tree(), frange.range)
})
}
@@ -471,7 +484,7 @@ impl Analysis {
// FIXME: Edition
self.with_db(|db| {
let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
- let source_file = db.parse(editioned_file_id_wrapper).tree();
+ let source_file = editioned_file_id_wrapper.parse(db).tree();
file_structure::file_structure(&source_file, config)
})
}
@@ -499,11 +512,14 @@ impl Analysis {
}
/// Returns the set of folding ranges.
- pub fn folding_ranges(&self, file_id: FileId) -> Cancellable<Vec<Fold>> {
+ pub fn folding_ranges(&self, file_id: FileId, collapsed_text: bool) -> Cancellable<Vec<Fold>> {
self.with_db(|db| {
let editioned_file_id_wrapper = EditionedFileId::current_edition(&self.db, file_id);
- folding_ranges::folding_ranges(&db.parse(editioned_file_id_wrapper).tree())
+ folding_ranges::folding_ranges(
+ &editioned_file_id_wrapper.parse(db).tree(),
+ collapsed_text,
+ )
})
}
@@ -654,7 +670,7 @@ impl Analysis {
/// Returns crates that this file *might* belong to.
pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<Crate>> {
- self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
+ self.with_db(|db| relevant_crates(db, file_id).to_vec())
}
/// Returns the edition of the given crate.
@@ -763,7 +779,7 @@ impl Analysis {
&self,
config: &CompletionConfig<'_>,
position: FilePosition,
- imports: impl IntoIterator<Item = String> + std::panic::UnwindSafe,
+ imports: impl IntoIterator<Item = CompletionItemImport> + std::panic::UnwindSafe,
) -> Cancellable<Vec<TextEdit>> {
Ok(self
.with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))?
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index b2b91d6e3c..5079b0c4f9 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -17,25 +17,37 @@ use syntax::{
pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> {
const BRACES: &[SyntaxKind] =
&[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]];
- let (brace_token, brace_idx) = file
- .syntax()
- .token_at_offset(offset)
+ let current = file.syntax().token_at_offset(offset);
+ if let Some((brace_token, brace_idx)) = current
+ .clone()
.filter_map(|node| {
let idx = BRACES.iter().position(|&brace| brace == node.kind())?;
Some((node, idx))
})
- .last()?;
- let parent = brace_token.parent()?;
- if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
- cov_mark::hit!(pipes_not_braces);
- return None;
+ .last()
+ {
+ let parent = brace_token.parent()?;
+ if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
+ cov_mark::hit!(pipes_not_braces);
+ return None;
+ }
+ let matching_kind = BRACES[brace_idx ^ 1];
+ let matching_node = parent
+ .children_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|node| node.kind() == matching_kind && node != &brace_token)?;
+ Some(matching_node.text_range().start())
+ } else {
+ // when the offset is not at a brace, find first parent
+ current.last()?.parent_ancestors().find_map(|x| {
+ x.children_with_tokens()
+ .filter_map(|it| it.into_token())
+ // with ending brace
+ .filter(|node| BRACES.contains(&node.kind()))
+ .last()
+ .map(|x| x.text_range().start())
+ })
}
- let matching_kind = BRACES[brace_idx ^ 1];
- let matching_node = parent
- .children_with_tokens()
- .filter_map(|it| it.into_token())
- .find(|node| node.kind() == matching_kind && node != &brace_token)?;
- Some(matching_node.text_range().start())
}
#[cfg(test)]
@@ -64,6 +76,14 @@ mod tests {
"fn func(x) { return (2 * (x + 3)$0) + 5;}",
"fn func(x) { return $0(2 * (x + 3)) + 5;}",
);
+ do_check(
+ "fn func(x) { return (2 * (x $0+ 3)) + 5;}",
+ "fn func(x) { return (2 * (x + 3$0)) + 5;}",
+ );
+ do_check(
+ "fn func(x) { re$0turn (2 * (x + 3)) + 5;}",
+ "fn func(x) { return (2 * (x + 3)) + 5;$0}",
+ );
{
cov_mark::check!(pipes_not_braces);
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs
index 92020321f4..99f8634bcb 100644
--- a/crates/ide/src/navigation_target.rs
+++ b/crates/ide/src/navigation_target.rs
@@ -11,7 +11,7 @@ use hir::{
};
use ide_db::{
FileId, FileRange, RootDatabase, SymbolKind,
- base_db::{CrateOrigin, LangCrateOrigin, RootQueryDb},
+ base_db::{CrateOrigin, LangCrateOrigin, all_crates},
defs::{Definition, find_std_module},
documentation::{Documentation, HasDocs},
famous_defs::FamousDefs,
@@ -861,8 +861,7 @@ impl TryToNav for hir::BuiltinType {
sema: &Semantics<'_, RootDatabase>,
) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db;
- let krate = db
- .all_crates()
+ let krate = all_crates(db)
.iter()
.copied()
.find(|&krate| matches!(krate.data(db).origin, CrateOrigin::Lang(LangCrateOrigin::Std)))
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 96d829d126..509ec2ab40 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -1,7 +1,7 @@
use hir::{Semantics, crate_def_map};
use ide_db::{
FileId, FilePosition, RootDatabase,
- base_db::{Crate, RootQueryDb},
+ base_db::{Crate, relevant_crates},
};
use itertools::Itertools;
use syntax::{
@@ -53,7 +53,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
/// This returns `Vec` because a module may be included from several places.
pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<Crate> {
- db.relevant_crates(file_id)
+ relevant_crates(db, file_id)
.iter()
.copied()
.filter(|&crate_id| {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 9392651c17..4ed3d1c7d7 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -19,10 +19,11 @@
use hir::{PathResolution, Semantics};
use ide_db::{
- FileId, MiniCore, RootDatabase,
+ FileId, RootDatabase,
+ base_db::SourceDatabase,
defs::{Definition, NameClass, NameRefClass},
helpers::pick_best_token,
- ra_fixture::UpmapFromRaFixture,
+ ra_fixture::{RaFixtureConfig, UpmapFromRaFixture},
search::{ReferenceCategory, SearchScope, UsageSearchResult},
};
use itertools::Itertools;
@@ -90,7 +91,9 @@ pub struct Declaration {
#[derive(Debug)]
pub struct FindAllRefsConfig<'a> {
pub search_scope: Option<SearchScope>,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
+ pub exclude_imports: bool,
+ pub exclude_tests: bool,
}
/// Find all references to the item at the given position.
@@ -125,10 +128,23 @@ pub(crate) fn find_all_refs(
) -> Option<Vec<ReferenceSearchResult>> {
let _p = tracing::info_span!("find_all_refs").entered();
let syntax = sema.parse_guess_edition(position.file_id).syntax().clone();
+ let exclude_library_refs = !is_library_file(sema.db, position.file_id);
let make_searcher = |literal_search: bool| {
move |def: Definition| {
- let mut usages =
- def.usages(sema).set_scope(config.search_scope.as_ref()).include_self_refs().all();
+ let mut included_categories = ReferenceCategory::all();
+ if config.exclude_imports {
+ included_categories.remove(ReferenceCategory::IMPORT);
+ }
+ if config.exclude_tests {
+ included_categories.remove(ReferenceCategory::TEST);
+ }
+ let mut usages = def
+ .usages(sema)
+ .set_scope(config.search_scope.as_ref())
+ .set_included_categories(included_categories)
+ .set_exclude_library_files(exclude_library_refs)
+ .include_self_refs()
+ .all();
if literal_search {
retain_adt_literal_usages(&mut usages, def, sema);
}
@@ -179,7 +195,7 @@ pub(crate) fn find_all_refs(
if let Some(token) = syntax.token_at_offset(position.offset).left_biased()
&& let Some(token) = ast::String::cast(token.clone())
&& let Some((analysis, fixture_analysis)) =
- Analysis::from_ra_fixture(sema, token.clone(), &token, config.minicore)
+ Analysis::from_ra_fixture(sema, token.clone(), &token, &config.ra_fixture)
&& let Some((virtual_file_id, file_offset)) =
fixture_analysis.map_offset_down(position.offset)
{
@@ -207,6 +223,11 @@ pub(crate) fn find_all_refs(
}
}
+fn is_library_file(db: &RootDatabase, file_id: FileId) -> bool {
+ let source_root = db.file_source_root(file_id).source_root_id(db);
+ db.source_root(source_root).source_root(db).is_library
+}
+
pub(crate) fn find_defs(
sema: &Semantics<'_, RootDatabase>,
syntax: &SyntaxNode,
@@ -462,14 +483,14 @@ fn handle_control_flow_keywords(
mod tests {
use expect_test::{Expect, expect};
use hir::EditionedFileId;
- use ide_db::{FileId, MiniCore, RootDatabase};
+ use ide_db::{FileId, RootDatabase, ra_fixture::RaFixtureConfig};
use stdx::format_to;
use crate::{SearchScope, fixture, references::FindAllRefsConfig};
#[test]
fn exclude_tests() {
- check(
+ check_with_filters(
r#"
fn test_func() {}
@@ -482,6 +503,8 @@ fn test() {
test_func();
}
"#,
+ false,
+ false,
expect![[r#"
test_func Function FileId(0) 0..17 3..12
@@ -490,7 +513,7 @@ fn test() {
"#]],
);
- check(
+ check_with_filters(
r#"
fn test_func() {}
@@ -503,6 +526,8 @@ fn test() {
test_func();
}
"#,
+ false,
+ false,
expect![[r#"
test_func Function FileId(0) 0..17 3..12
@@ -510,6 +535,133 @@ fn test() {
FileId(0) 96..105 test
"#]],
);
+
+ check_with_filters(
+ r#"
+fn test_func() {}
+
+fn func() {
+ test_func$0();
+}
+
+#[test]
+fn test() {
+ test_func();
+}
+"#,
+ false,
+ true,
+ expect![[r#"
+ test_func Function FileId(0) 0..17 3..12
+
+ FileId(0) 35..44
+ "#]],
+ );
+ }
+
+ #[test]
+ fn exclude_library_refs_filtering() {
+ // exclude refs in 3rd party lib
+ check_with_filters(
+ r#"
+//- /main.rs crate:main deps:dep
+use dep::foo;
+
+fn main() {
+ foo$0();
+}
+
+//- /dep/lib.rs crate:dep new_source_root:library
+pub fn foo() {}
+
+pub fn also_calls_foo() {
+ foo();
+}
+"#,
+ false,
+ false,
+ expect![[r#"
+ foo Function FileId(1) 0..15 7..10
+
+ FileId(0) 9..12 import
+ FileId(0) 31..34
+ "#]],
+ );
+
+ // exclude refs in stdlib
+ check_with_filters(
+ r#"
+//- minicore: option
+fn main() {
+ let _ = core::option::Option::Some$0(0);
+}
+"#,
+ false,
+ false,
+ expect![[r#"
+ Some Variant FileId(1) 5999..6031 6024..6028
+
+ FileId(0) 46..50
+ "#]],
+ );
+
+ // keep refs in local lib
+ check_with_filters(
+ r#"
+//- /main.rs crate:main deps:dep
+use dep::foo;
+
+fn main() {
+ foo$0();
+}
+
+//- /dep/lib.rs crate:dep
+pub fn foo() {}
+
+pub fn also_calls_foo() {
+ foo();
+}
+"#,
+ false,
+ false,
+ expect![[r#"
+ foo Function FileId(1) 0..15 7..10
+
+ FileId(0) 9..12 import
+ FileId(0) 31..34
+ FileId(1) 47..50
+ "#]],
+ );
+ }
+
+ #[test]
+ fn find_refs_from_library_source_keeps_library_refs() {
+ check_with_filters(
+ r#"
+//- /main.rs crate:main deps:dep
+use dep::foo;
+
+fn main() {
+ foo();
+}
+
+//- /dep/lib.rs crate:dep new_source_root:library
+pub fn foo$0() {}
+
+pub fn also_calls_foo() {
+ foo();
+}
+"#,
+ false,
+ false,
+ expect![[r#"
+ foo Function FileId(1) 0..15 7..10
+
+ FileId(0) 9..12 import
+ FileId(0) 31..34
+ FileId(1) 47..50
+ "#]],
+ );
}
#[test]
@@ -1556,7 +1708,16 @@ fn main() {
}
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
- check_with_scope(ra_fixture, None, expect)
+ check_with_filters(ra_fixture, false, false, expect)
+ }
+
+ fn check_with_filters(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ exclude_imports: bool,
+ exclude_tests: bool,
+ expect: Expect,
+ ) {
+ check_with_scope_and_filters(ra_fixture, None, exclude_imports, exclude_tests, expect)
}
fn check_with_scope(
@@ -1564,10 +1725,22 @@ fn main() {
search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>,
expect: Expect,
) {
+ check_with_scope_and_filters(ra_fixture, search_scope, false, false, expect)
+ }
+
+ fn check_with_scope_and_filters(
+ #[rust_analyzer::rust_fixture] ra_fixture: &str,
+ search_scope: Option<&mut dyn FnMut(&RootDatabase) -> SearchScope>,
+ exclude_imports: bool,
+ exclude_tests: bool,
+ expect: Expect,
+ ) {
let (analysis, pos) = fixture::position(ra_fixture);
let config = FindAllRefsConfig {
search_scope: search_scope.map(|it| it(&analysis.db)),
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
+ exclude_imports,
+ exclude_tests,
};
let refs = analysis.find_all_refs(pos, &config).unwrap().unwrap();
@@ -2567,6 +2740,7 @@ fn test() {
fn goto_ref_fn_kw() {
check(
r#"
+//- minicore: fn
macro_rules! N {
($i:ident, $x:expr, $blk:expr) => {
for $i in 0..$x {
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index a0a6a24559..60750608a5 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -3,12 +3,12 @@ use std::{fmt, sync::OnceLock};
use arrayvec::ArrayVec;
use ast::HasName;
use cfg::{CfgAtom, CfgExpr};
-use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, db::HirDatabase, sym};
+use hir::{AsAssocItem, HasAttrs, HasCrate, HasSource, Semantics, Symbol, sym};
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
+use ide_db::base_db::all_crates;
use ide_db::impl_empty_upmap_from_ra_fixture;
use ide_db::{
FilePosition, FxHashMap, FxIndexMap, FxIndexSet, RootDatabase, SymbolKind,
- base_db::RootQueryDb,
defs::Definition,
helpers::visit_file_defs,
search::{FileReferenceNode, SearchScope},
@@ -55,7 +55,7 @@ impl fmt::Display for TestId {
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum RunnableKind {
TestMod { path: String },
- Test { test_id: TestId, attr: TestAttr },
+ Test { test_id: TestId },
Bench { test_id: TestId },
DocTest { test_id: TestId },
Bin,
@@ -334,8 +334,7 @@ pub(crate) fn runnable_fn(
};
if def.is_test(sema.db) {
- let attr = TestAttr::from_fn(sema.db, def);
- RunnableKind::Test { test_id: test_id(), attr }
+ RunnableKind::Test { test_id: test_id() }
} else if def.is_bench(sema.db) {
RunnableKind::Bench { test_id: test_id() }
} else {
@@ -506,7 +505,7 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op
let krate = def.krate(db);
let edition = krate.map(|it| it.edition(db)).unwrap_or(Edition::CURRENT);
let display_target = krate
- .unwrap_or_else(|| (*db.all_crates().last().expect("no crate graph present")).into())
+ .unwrap_or_else(|| (*all_crates(db).last().expect("no crate graph present")).into())
.to_display_target(db);
if !has_runnable_doc_test(db, &attrs) {
return None;
@@ -558,17 +557,6 @@ fn module_def_doctest(sema: &Semantics<'_, RootDatabase>, def: Definition) -> Op
Some(res)
}
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub struct TestAttr {
- pub ignore: bool,
-}
-
-impl TestAttr {
- fn from_fn(db: &dyn HirDatabase, fn_def: hir::Function) -> TestAttr {
- TestAttr { ignore: fn_def.is_ignore(db) }
- }
-}
-
fn has_runnable_doc_test(db: &RootDatabase, attrs: &hir::AttrsWithOwner) -> bool {
const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
@@ -618,14 +606,14 @@ fn has_test_function_or_multiple_test_submodules(
return true;
}
}
- hir::ModuleDef::Module(submodule) => {
+ hir::ModuleDef::Module(submodule)
if has_test_function_or_multiple_test_submodules(
sema,
&submodule,
consider_exported_main,
- ) {
- number_of_test_submodules += 1;
- }
+ ) =>
+ {
+ number_of_test_submodules += 1;
}
_ => (),
}
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 9eb01b12f2..cf796b2715 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -1975,8 +1975,8 @@ trait Sub: Super + Super {
fn f() -> impl Sub<$0
"#,
expect![[r#"
- trait Sub<SuperTy = …, SubTy = …>
- ^^^^^^^^^^^ ---------
+ trait Sub<SubTy = …, SuperTy = …>
+ ^^^^^^^^^ -----------
"#]],
);
}
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index aba6b64f97..4b2c9ceef9 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -4,11 +4,12 @@
use arrayvec::ArrayVec;
use hir::{Crate, Module, Semantics, db::HirDatabase};
use ide_db::{
- FileId, FileRange, FxHashMap, FxHashSet, MiniCore, RootDatabase,
- base_db::{RootQueryDb, SourceDatabase, VfsPath},
+ FileId, FileRange, FxHashMap, FxHashSet, RootDatabase,
+ base_db::{SourceDatabase, VfsPath},
defs::{Definition, IdentClass},
documentation::Documentation,
famous_defs::FamousDefs,
+ ra_fixture::RaFixtureConfig,
};
use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange};
@@ -16,7 +17,7 @@ use crate::navigation_target::UpmappingResult;
use crate::{
Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
hover::{SubstTyLen, hover_for_definition},
- inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve},
+ inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve, TypeHintsPlacement},
moniker::{MonikerResult, SymbolInformationKind, def_to_kind, def_to_moniker},
parent_module::crates_for,
};
@@ -123,16 +124,8 @@ fn documentation_for_definition(
_ => None,
};
- def.docs(
- sema.db,
- famous_defs.as_ref(),
- def.krate(sema.db)
- .unwrap_or_else(|| {
- (*sema.db.all_crates().last().expect("no crate graph present")).into()
- })
- .to_display_target(sema.db),
- )
- .map(Documentation::into_owned)
+ def.docs(sema.db, famous_defs.as_ref(), def.krate(sema.db)?.to_display_target(sema.db))
+ .map(Documentation::into_owned)
}
// FIXME: This is a weird function
@@ -159,7 +152,7 @@ pub enum VendoredLibrariesConfig<'a> {
impl StaticIndex<'_> {
fn add_file(&mut self, file_id: FileId) {
let current_crate = crates_for(self.db, file_id).pop().map(Into::into);
- let folds = self.analysis.folding_ranges(file_id).unwrap();
+ let folds = self.analysis.folding_ranges(file_id, true).unwrap();
let inlay_hints = self
.analysis
.inlay_hints(
@@ -167,6 +160,7 @@ impl StaticIndex<'_> {
render_colons: true,
discriminant_hints: crate::DiscriminantHints::Fieldless,
type_hints: true,
+ type_hints_placement: TypeHintsPlacement::Inline,
sized_bound: false,
parameter_hints: true,
parameter_hints_for_missing_arguments: false,
@@ -196,7 +190,7 @@ impl StaticIndex<'_> {
closing_brace_hints_min_lines: Some(25),
fields_to_resolve: InlayFieldsToResolve::empty(),
range_exclusive_hints: false,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
},
file_id,
None,
@@ -225,7 +219,7 @@ impl StaticIndex<'_> {
max_enum_variants_count: Some(5),
max_subst_ty_len: SubstTyLen::Unlimited,
show_drop_glue: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] };
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 217b13b4ef..9fd3f005ec 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -17,7 +17,7 @@ use either::Either;
use hir::{
DefWithBody, EditionedFileId, ExpressionStoreOwner, InFile, InRealFile, MacroKind, Semantics,
};
-use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind};
+use ide_db::{FxHashMap, FxHashSet, Ranker, RootDatabase, SymbolKind, ra_fixture::RaFixtureConfig};
use syntax::{
AstNode, AstToken, NodeOrToken,
SyntaxKind::*,
@@ -65,7 +65,7 @@ pub struct HighlightConfig<'a> {
pub macro_bang: bool,
/// Whether to highlight unresolved things be their syntax
pub syntactic_name_ref_highlighting: bool,
- pub minicore: MiniCore<'a>,
+ pub ra_fixture: RaFixtureConfig<'a>,
}
// Feature: Semantic Syntax Highlighting
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 0e101ab235..6823736d12 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -307,12 +307,12 @@ fn highlight_name_ref(
h |= HlMod::Consuming;
}
// highlight unsafe traits as unsafe only in their implementations
- Definition::Trait(trait_) if trait_.is_unsafe(db) => {
- if ast::Impl::for_trait_name_ref(&name_ref)
- .is_some_and(|impl_| impl_.unsafe_token().is_some())
- {
- h |= HlMod::Unsafe;
- }
+ Definition::Trait(trait_)
+ if trait_.is_unsafe(db)
+ && ast::Impl::for_trait_name_ref(&name_ref)
+ .is_some_and(|impl_| impl_.unsafe_token().is_some()) =>
+ {
+ h |= HlMod::Unsafe;
}
Definition::Function(_) => {
let is_unsafe = name_ref
diff --git a/crates/ide/src/syntax_highlighting/html.rs b/crates/ide/src/syntax_highlighting/html.rs
index 74567e8213..423c0c349c 100644
--- a/crates/ide/src/syntax_highlighting/html.rs
+++ b/crates/ide/src/syntax_highlighting/html.rs
@@ -1,7 +1,7 @@
//! Renders a bit of code as HTML.
use hir::Semantics;
-use ide_db::MiniCore;
+use ide_db::ra_fixture::RaFixtureConfig;
use oorandom::Rand32;
use stdx::format_to;
use syntax::AstNode;
@@ -69,7 +69,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
},
file_id,
rainbow,
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs
index 74a8d93dfe..6afe5681a9 100644
--- a/crates/ide/src/syntax_highlighting/inject.rs
+++ b/crates/ide/src/syntax_highlighting/inject.rs
@@ -27,7 +27,7 @@ pub(super) fn ra_fixture(
sema,
literal.clone(),
expanded,
- config.minicore,
+ &config.ra_fixture,
&mut |range| {
hl.add(HlRange {
range,
@@ -56,7 +56,7 @@ pub(super) fn ra_fixture(
macro_bang: config.macro_bang,
// What if there is a fixture inside a fixture? It's fixtures all the way down.
// (In fact, we have a fixture inside a fixture in our test suite!)
- minicore: config.minicore,
+ ra_fixture: config.ra_fixture,
},
tmp_file_id,
)
@@ -186,7 +186,7 @@ pub(super) fn doc_comment(
specialize_operator: config.operator,
inject_doc_comment: config.inject_doc_comment,
macro_bang: config.macro_bang,
- minicore: config.minicore,
+ ra_fixture: config.ra_fixture,
},
tmp_file_id,
None,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index ce9ec7431a..dcfe4dd41e 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -166,12 +166,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">///</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</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="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"false"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"</span><span class="keyword control injected">loop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="brace injected">}</span><span class="string_literal attribute">"</span><span class="attribute_bracket attribute">]</span>
<span class="comment documentation">/// ```</span>
<span class="comment documentation">///</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">cfg_attr</span><span class="parenthesis attribute">(</span><span class="none attribute">not</span><span class="parenthesis attribute">(</span><span class="none attribute">feature</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="none attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```rust"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
+<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="keyword attribute">cfg_attr</span><span class="parenthesis attribute">(</span>not<span class="parenthesis attribute">(</span>feature <span class="operator attribute">=</span> <span class="string_literal attribute">"alloc"</span><span class="parenthesis attribute">)</span><span class="comma attribute">,</span> <span class="builtin_attr attribute">doc</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"```ignore"</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="comment documentation">///</span><span class="none injected"> </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="punctuation injected">_</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="function injected">example</span><span class="parenthesis injected">(</span><span class="operator injected">&</span><span class="module injected">alloc</span><span class="operator injected">::</span><span class="macro injected">vec</span><span class="macro_bang injected">!</span><span class="bracket injected">[</span><span class="numeric_literal injected">1</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">2</span><span class="comma injected">,</span><span class="none injected"> </span><span class="numeric_literal injected">3</span><span class="bracket injected">]</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
<span class="comment documentation">/// ```</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration public">mix_and_match</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index aecd1d3fdb..d687cb40a9 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -1,7 +1,7 @@
use std::time::Instant;
use expect_test::{ExpectFile, expect_file};
-use ide_db::{MiniCore, SymbolKind};
+use ide_db::{SymbolKind, ra_fixture::RaFixtureConfig};
use span::Edition;
use test_utils::{AssertLinear, bench, bench_fixture, skip_slow_tests};
@@ -17,7 +17,7 @@ const HL_CONFIG: HighlightConfig<'_> = HighlightConfig {
inject_doc_comment: true,
macro_bang: true,
syntactic_name_ref_highlighting: false,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
#[test]
@@ -1348,7 +1348,7 @@ fn benchmark_syntax_highlighting_parser() {
})
.count()
};
- assert_eq!(hash, 1606);
+ assert_eq!(hash, 1631);
}
#[test]
diff --git a/crates/ide/src/test_explorer.rs b/crates/ide/src/test_explorer.rs
index 4792566f5f..02040ef138 100644
--- a/crates/ide/src/test_explorer.rs
+++ b/crates/ide/src/test_explorer.rs
@@ -1,8 +1,8 @@
//! Discovers tests
use hir::{Crate, Module, ModuleDef, Semantics};
-use ide_db::base_db;
-use ide_db::{FileId, RootDatabase, base_db::RootQueryDb};
+use ide_db::base_db::{self, all_crates};
+use ide_db::{FileId, RootDatabase};
use syntax::TextRange;
use crate::{NavigationTarget, Runnable, TryToNav, runnables::runnable_fn};
@@ -26,7 +26,7 @@ pub struct TestItem {
}
pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
- db.all_crates()
+ all_crates(db)
.iter()
.copied()
.filter(|&id| id.data(db).origin.is_local())
@@ -48,7 +48,7 @@ pub(crate) fn discover_test_roots(db: &RootDatabase) -> Vec<TestItem> {
fn find_crate_by_id(db: &RootDatabase, crate_id: &str) -> Option<base_db::Crate> {
// here, we use display_name as the crate id. This is not super ideal, but it works since we
// only show tests for the local crates.
- db.all_crates().iter().copied().find(|&id| {
+ all_crates(db).iter().copied().find(|&id| {
id.data(db).origin.is_local()
&& id.extra_data(db).display_name.as_ref().is_some_and(|x| x.to_string() == crate_id)
})
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index e8b0c92dcb..a49a85fe78 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -17,7 +17,7 @@ mod on_enter;
use either::Either;
use hir::EditionedFileId;
-use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb};
+use ide_db::{FilePosition, RootDatabase, base_db::relevant_crates};
use span::Edition;
use std::iter;
@@ -70,13 +70,12 @@ pub(crate) fn on_char_typed(
if !TRIGGER_CHARS.contains(&char_typed) {
return None;
}
- let edition = db
- .relevant_crates(position.file_id)
+ let edition = relevant_crates(db, position.file_id)
.first()
.copied()
.map_or(Edition::CURRENT, |krate| krate.data(db).edition);
let editioned_file_id_wrapper = EditionedFileId::new(db, position.file_id, edition);
- let file = &db.parse(editioned_file_id_wrapper);
+ let file = &editioned_file_id_wrapper.parse(db);
let char_matches_position =
file.tree().syntax().text().char_at(position.offset) == Some(char_typed);
if !stdx::always!(char_matches_position) {
@@ -1240,12 +1239,6 @@ sdasdasdasdasd
#[test]
fn parenthesis_noop_in_item_position_with_macro() {
type_char_noop('(', r#"$0println!();"#);
- type_char_noop(
- '(',
- r#"
-fn main() $0println!("hello");
-}"#,
- );
}
#[test]
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index fdc583a15c..7d04594a5b 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -1,13 +1,11 @@
-//! Handles the `Enter` key press. At the momently, this only continues
-//! comments, but should handle indent some time in the future as well.
+//! Handles the `Enter` key press, including comment continuation and
+//! indentation in brace-delimited constructs.
-use ide_db::base_db::RootQueryDb;
use ide_db::{FilePosition, RootDatabase};
use syntax::{
AstNode, SmolStr, SourceFile,
SyntaxKind::*,
- SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset,
- algo::find_node_at_offset,
+ SyntaxToken, TextRange, TextSize, TokenAtOffset,
ast::{self, AstToken, edit::IndentLevel},
};
@@ -20,7 +18,8 @@ use ide_db::text_edit::TextEdit;
// - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///`
// - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//`
// - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!`
-// - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block
+// - <kbd>Enter</kbd> after `{` reformats single-line brace-delimited contents by
+// moving the text between `{` and the matching `}` onto an indented line
//
// This action needs to be assigned to shortcut explicitly.
//
@@ -52,7 +51,7 @@ use ide_db::text_edit::TextEdit;
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> {
let editioned_file_id_wrapper =
ide_db::base_db::EditionedFileId::current_edition(db, position.file_id);
- let parse = db.parse(editioned_file_id_wrapper);
+ let parse = editioned_file_id_wrapper.parse(db);
let file = parse.tree();
let token = file.syntax().token_at_offset(position.offset).left_biased()?;
@@ -60,22 +59,11 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
return on_enter_in_comment(&comment, &file, position.offset);
}
- if token.kind() == L_CURLY {
- // Typing enter after the `{` of a block expression, where the `}` is on the same line
- if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
- .and_then(|block| on_enter_in_block(block, position))
- {
- cov_mark::hit!(indent_block_contents);
- return Some(edit);
- }
-
- // Typing enter after the `{` of a use tree list.
- if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
- .and_then(|list| on_enter_in_use_tree_list(list, position))
- {
- cov_mark::hit!(indent_block_contents);
- return Some(edit);
- }
+ if token.kind() == L_CURLY
+ && let Some(edit) = on_enter_in_braces(token, position)
+ {
+ cov_mark::hit!(indent_block_contents);
+ return Some(edit);
}
None
@@ -120,44 +108,54 @@ fn on_enter_in_comment(
Some(edit)
}
-fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<TextEdit> {
- let contents = block_contents(&block)?;
-
- if block.syntax().text().contains_char('\n') {
- return None;
- }
-
- let indent = IndentLevel::from_node(block.syntax());
- let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
- edit.union(TextEdit::insert(contents.text_range().end(), format!("\n{indent}"))).ok()?;
- Some(edit)
-}
-
-fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> {
- if list.syntax().text().contains_char('\n') {
+fn on_enter_in_braces(l_curly: SyntaxToken, position: FilePosition) -> Option<TextEdit> {
+ if l_curly.text_range().end() != position.offset {
return None;
}
- let indent = IndentLevel::from_node(list.syntax());
- let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
- edit.union(TextEdit::insert(list.r_curly_token()?.text_range().start(), format!("\n{indent}")))
- .ok()?;
- Some(edit)
+ let (r_curly, content) = brace_contents_on_same_line(&l_curly)?;
+ let indent = IndentLevel::from_token(&l_curly);
+ Some(TextEdit::replace(
+ TextRange::new(position.offset, r_curly.text_range().start()),
+ format!("\n{}$0{}\n{indent}", indent + 1, content),
+ ))
}
-fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
- let mut node = block.tail_expr().map(|e| e.syntax().clone());
+fn brace_contents_on_same_line(l_curly: &SyntaxToken) -> Option<(SyntaxToken, String)> {
+ let mut depth = 0_u32;
+ let mut tokens = Vec::new();
+ let mut token = l_curly.next_token()?;
- for stmt in block.statements() {
- if node.is_some() {
- // More than 1 node in the block
+ loop {
+ if token.kind() == WHITESPACE && token.text().contains('\n') {
return None;
}
- node = Some(stmt.syntax().clone());
- }
+ match token.kind() {
+ L_CURLY => {
+ depth += 1;
+ tokens.push(token.clone());
+ }
+ R_CURLY if depth == 0 => {
+ let first = tokens.iter().position(|it| it.kind() != WHITESPACE);
+ let last = tokens.iter().rposition(|it| it.kind() != WHITESPACE);
+ let content = match first.zip(last) {
+ Some((first, last)) => {
+ tokens[first..=last].iter().map(|it| it.text()).collect()
+ }
+ None => String::new(),
+ };
+ return Some((token, content));
+ }
+ R_CURLY => {
+ depth -= 1;
+ tokens.push(token.clone());
+ }
+ _ => tokens.push(token.clone()),
+ }
- node
+ token = token.next_token()?;
+ }
}
fn followed_by_comment(comment: &ast::Comment) -> bool {
@@ -383,10 +381,58 @@ fn main() {
}
#[test]
- fn indents_fn_body_block() {
+ fn indents_empty_brace_pairs() {
cov_mark::check!(indent_block_contents);
do_check(
r#"
+fn f() {$0}
+ "#,
+ r#"
+fn f() {
+ $0
+}
+ "#,
+ );
+ do_check(
+ r#"
+fn f() {
+ let x = {$0};
+}
+ "#,
+ r#"
+fn f() {
+ let x = {
+ $0
+ };
+}
+ "#,
+ );
+ do_check(
+ r#"
+use crate::{$0};
+ "#,
+ r#"
+use crate::{
+ $0
+};
+ "#,
+ );
+ do_check(
+ r#"
+mod m {$0}
+ "#,
+ r#"
+mod m {
+ $0
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn indents_fn_body_block() {
+ do_check(
+ r#"
fn f() {$0()}
"#,
r#"
@@ -478,29 +524,39 @@ fn f() {
}
#[test]
- fn does_not_indent_empty_block() {
- do_check_noop(
+ fn indents_block_with_multiple_statements() {
+ do_check(
r#"
-fn f() {$0}
+fn f() {$0 a = b; ()}
+ "#,
+ r#"
+fn f() {
+ $0a = b; ()
+}
"#,
);
- do_check_noop(
+ do_check(
r#"
-fn f() {{$0}}
+fn f() {$0 a = b; a = b; }
+ "#,
+ r#"
+fn f() {
+ $0a = b; a = b;
+}
"#,
);
}
#[test]
- fn does_not_indent_block_with_too_much_content() {
- do_check_noop(
+ fn trims_spaces_around_brace_contents() {
+ do_check(
r#"
-fn f() {$0 a = b; ()}
+fn f() {$0 () }
"#,
- );
- do_check_noop(
r#"
-fn f() {$0 a = b; a = b; }
+fn f() {
+ $0()
+}
"#,
);
}
@@ -571,6 +627,20 @@ use {
}
#[test]
+ fn indents_item_lists() {
+ do_check(
+ r#"
+mod m {$0}
+ "#,
+ r#"
+mod m {
+ $0
+}
+ "#,
+ );
+ }
+
+ #[test]
fn does_not_indent_use_tree_list_when_not_at_curly_brace() {
do_check_noop(
r#"
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs
index 25deffe10e..ecfdd09b24 100644
--- a/crates/ide/src/view_crate_graph.rs
+++ b/crates/ide/src/view_crate_graph.rs
@@ -1,10 +1,9 @@
use dot::{Id, LabelText};
+use ide_db::base_db::all_crates;
use ide_db::base_db::salsa::plumbing::AsId;
use ide_db::{
FxHashMap, RootDatabase,
- base_db::{
- BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, RootQueryDb, SourceDatabase,
- },
+ base_db::{BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase},
};
// Feature: View Crate Graph
@@ -17,8 +16,8 @@ use ide_db::{
// | Editor | Action Name |
// |---------|-------------|
// | VS Code | **rust-analyzer: View Crate Graph** |
-pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> {
- let all_crates = db.all_crates();
+pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> String {
+ let all_crates = all_crates(db);
let crates_to_render = all_crates
.iter()
.copied()
@@ -37,7 +36,7 @@ pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String,
let mut dot = Vec::new();
dot::render(&graph, &mut dot).unwrap();
- Ok(String::from_utf8(dot).unwrap())
+ String::from_utf8(dot).unwrap()
}
struct DotCrateGraph<'db> {
diff --git a/crates/ide/src/view_syntax_tree.rs b/crates/ide/src/view_syntax_tree.rs
index ecd93e8b28..7732b18082 100644
--- a/crates/ide/src/view_syntax_tree.rs
+++ b/crates/ide/src/view_syntax_tree.rs
@@ -1,6 +1,6 @@
use hir::Semantics;
use ide_db::{
- FileId, LineIndexDatabase, RootDatabase,
+ FileId, RootDatabase, line_index,
line_index::{LineCol, LineIndex},
};
use span::{TextRange, TextSize};
@@ -20,7 +20,7 @@ use triomphe::Arc;
// | VS Code | **Rust Syntax Tree** |
pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String {
let sema = Semantics::new(db);
- let line_index = db.line_index(file_id);
+ let line_index = line_index(db, file_id).clone();
let parse = sema.parse_guess_edition(file_id);
let ctx = SyntaxTreeCtx { line_index, in_string: None };
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index cc09a1aae7..614411598b 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -123,7 +123,6 @@ define_symbols! {
all,
alloc_layout,
alloc,
- allow_internal_unsafe,
allow,
any,
as_str,
@@ -178,6 +177,7 @@ define_symbols! {
Continue,
convert,
copy,
+ use_cloned,
Copy,
core_panic,
core,
@@ -200,7 +200,9 @@ define_symbols! {
derive,
discriminant_kind,
discriminant_type,
- dispatch_from_dyn,destruct,
+ dispatch_from_dyn,
+ destruct,
+ bikeshed_guaranteed_no_drop,
div_assign,
div,
doc,
@@ -233,6 +235,8 @@ define_symbols! {
async_fn_once_output,
async_fn_mut,
async_fn,
+ async_fn_kind_helper,
+ async_fn_kind_upvars,
call_ref_future,
call_once_future,
fn_ptr_addr,
@@ -298,6 +302,8 @@ define_symbols! {
iter,
Iterator,
iterator,
+ fused_iterator,
+ async_iterator,
keyword,
lang,
lang_items,
@@ -541,4 +547,35 @@ define_symbols! {
DispatchFromDyn,
define_opaque,
marker,
+ abi_unadjusted,
+ allocator_internals,
+ allow_internal_unsafe,
+ allow_internal_unstable,
+ cfg_emscripten_wasm_eh,
+ cfg_target_has_reliable_f16_f128,
+ compiler_builtins,
+ custom_mir,
+ eii_internals,
+ field_representing_type_raw,
+ intrinsics,
+ link_cfg,
+ more_maybe_bounds,
+ negative_bounds,
+ pattern_complexity_limit,
+ profiler_runtime,
+ rustc_attrs,
+ staged_api,
+ test_unstable_lint,
+ builtin_syntax,
+ link_llvm_intrinsics,
+ needs_panic_runtime,
+ panic_runtime,
+ pattern_types,
+ rustdoc_internals,
+ contracts_internals,
+ freeze_impls,
+ unsized_fn_params,
+ field,
+ field_base,
+ field_type,
}
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index 8753eab43a..839df18159 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -283,6 +283,19 @@ impl ProjectFolders {
}
}
+ // Collect workspace roots not already covered by a local PackageRoot
+ // (e.g. virtual workspaces where no package lives at the workspace root).
+ // We need these to load workspace-root rust-analyzer.toml into a local source root.
+ let uncovered_ws_roots: Vec<AbsPathBuf> = workspaces
+ .iter()
+ .filter_map(|ws| {
+ let ws_root = ws.workspace_root().to_path_buf();
+ let dominated =
+ roots.iter().any(|root| root.is_local && root.include.contains(&ws_root));
+ (!dominated).then_some(ws_root)
+ })
+ .collect();
+
for root in roots.into_iter().filter(|it| !it.include.is_empty()) {
let file_set_roots: Vec<VfsPath> =
root.include.iter().cloned().map(VfsPath::from).collect();
@@ -291,6 +304,7 @@ impl ProjectFolders {
let mut dirs = vfs::loader::Directories::default();
dirs.extensions.push("rs".into());
dirs.extensions.push("toml".into());
+ dirs.extensions.push("md".into());
dirs.include.extend(root.include);
dirs.exclude.extend(root.exclude);
for excl in global_excludes {
@@ -335,6 +349,20 @@ impl ProjectFolders {
}
}
+ // For virtual workspaces, the workspace root has no local PackageRoot, so
+ // rust-analyzer.toml there would fall into a library source root and be
+ // ignored. Load it explicitly via Entry::Files and register the workspace
+ // root as a local file-set root so the file is classified as local.
+ for ws_root in &uncovered_ws_roots {
+ let ratoml_path = ws_root.join("rust-analyzer.toml");
+ let file_set_roots = vec![VfsPath::from(ws_root.clone())];
+ let entry = vfs::loader::Entry::Files(vec![ratoml_path]);
+ res.watch.push(res.load.len());
+ res.load.push(entry);
+ local_filesets.push(fsc.len() as u64);
+ fsc.add_file_set(file_set_roots);
+ }
+
if let Some(user_config_path) = user_config_dir_path {
let ratoml_path = {
let mut p = user_config_path.to_path_buf();
@@ -738,7 +766,7 @@ fn resolve_sub_span(
#[cfg(test)]
mod tests {
- use ide_db::base_db::RootQueryDb;
+ use ide_db::base_db::all_crates;
use vfs::file_set::FileSetConfigBuilder;
use super::*;
@@ -766,7 +794,7 @@ mod tests {
let (db, _vfs, _proc_macro) =
load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config).unwrap();
- let n_crates = db.all_crates().len();
+ let n_crates = all_crates(&db).len();
// RA has quite a few crates, but the exact count doesn't matter
assert!(n_crates > 20);
}
@@ -856,7 +884,7 @@ mod tests {
let fsc = builder.build();
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 2, 3] };
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
- vc.sort_by(|x, y| x.0.0.cmp(&y.0.0));
+ vc.sort_by_key(|x| x.0.0);
assert_eq!(vc, vec![(SourceRootId(2), SourceRootId(1)), (SourceRootId(3), SourceRootId(1))])
}
@@ -871,7 +899,7 @@ mod tests {
let fsc = builder.build();
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] };
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
- vc.sort_by(|x, y| x.0.0.cmp(&y.0.0));
+ vc.sort_by_key(|x| x.0.0);
assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),])
}
@@ -886,7 +914,7 @@ mod tests {
let fsc = builder.build();
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1, 3] };
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
- vc.sort_by(|x, y| x.0.0.cmp(&y.0.0));
+ vc.sort_by_key(|x| x.0.0);
assert_eq!(vc, vec![(SourceRootId(3), SourceRootId(1)),])
}
@@ -902,7 +930,7 @@ mod tests {
let fsc = builder.build();
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
- vc.sort_by(|x, y| x.0.0.cmp(&y.0.0));
+ vc.sort_by_key(|x| x.0.0);
assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0)),])
}
@@ -918,7 +946,7 @@ mod tests {
let fsc = builder.build();
let src = SourceRootConfig { fsc, local_filesets: vec![0, 1] };
let mut vc = src.source_root_parent_map().into_iter().collect::<Vec<_>>();
- vc.sort_by(|x, y| x.0.0.cmp(&y.0.0));
+ vc.sort_by_key(|x| x.0.0);
assert_eq!(vc, vec![(SourceRootId(1), SourceRootId(0)),])
}
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index e481bbe9bc..0623e7ea19 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -228,50 +228,64 @@ fn opt_visibility(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
let m = p.start();
p.bump(T![pub]);
- if p.at(T!['(']) {
- match p.nth(1) {
- // test crate_visibility
- // pub(crate) struct S;
- // pub(self) struct S;
- // pub(super) struct S;
-
- // test_err crate_visibility_empty_recover
- // pub() struct S;
-
- // test pub_parens_typepath
- // struct B(pub (super::A));
- // struct B(pub (crate::A,));
- T![crate] | T![self] | T![super] | T![ident] | T![')'] if p.nth(2) != T![:] => {
- // If we are in a tuple struct, then the parens following `pub`
- // might be an tuple field, not part of the visibility. So in that
- // case we don't want to consume an identifier.
-
- // test pub_tuple_field
- // struct MyStruct(pub (u32, u32));
- // struct MyStruct(pub (u32));
- // struct MyStruct(pub ());
- if !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) {
- p.bump(T!['(']);
- paths::vis_path(p);
- p.expect(T![')']);
- }
- }
- // test crate_visibility_in
- // pub(in super::A) struct S;
- // pub(in crate) struct S;
- T![in] => {
- p.bump(T!['(']);
- p.bump(T![in]);
- paths::vis_path(p);
- p.expect(T![')']);
- }
- _ => {}
- }
- }
+ opt_visibility_inner(p, in_tuple_field);
m.complete(p, VISIBILITY);
true
}
+fn opt_visibility_inner(p: &mut Parser<'_>, in_tuple_field: bool) -> bool {
+ if !p.at(T!['(']) {
+ return false;
+ }
+
+ match p.nth(1) {
+ // test crate_visibility
+ // pub(crate) struct S;
+ // pub(self) struct S;
+ // pub(super) struct S;
+
+ // test_err crate_visibility_empty_recover
+ // pub() struct S;
+
+ // test pub_parens_typepath
+ // struct B(pub (super::A));
+ // struct B(pub (crate::A,));
+ T![crate] | T![self] | T![super] | T![ident] | T![')']
+ if p.nth(2) != T![:]
+ // If we are in a tuple struct, then the parens following `pub`
+ // might be an tuple field, not part of the visibility. So in that
+ // case we don't want to consume an identifier.
+
+ // test pub_tuple_field
+ // struct MyStruct(pub (u32, u32));
+ // struct MyStruct(pub (u32));
+ // struct MyStruct(pub ());
+ && !(in_tuple_field && matches!(p.nth(1), T![ident] | T![')'])) =>
+ {
+ let m = p.start();
+ p.bump(T!['(']);
+ paths::vis_path(p);
+ p.expect(T![')']);
+ m.complete(p, VISIBILITY_INNER);
+ return true;
+ }
+ // test crate_visibility_in
+ // pub(in super::A) struct S;
+ // pub(in crate) struct S;
+ T![in] => {
+ let m = p.start();
+ p.bump(T!['(']);
+ p.bump(T![in]);
+ paths::vis_path(p);
+ p.expect(T![')']);
+ m.complete(p, VISIBILITY_INNER);
+ return true;
+ }
+ _ => {}
+ }
+ false
+}
+
fn opt_rename(p: &mut Parser<'_>) {
if p.at(T![as]) {
let m = p.start();
@@ -303,6 +317,18 @@ fn opt_ret_type(p: &mut Parser<'_>) -> bool {
}
}
+fn opt_no_arrow_ret_type(p: &mut Parser<'_>) -> bool {
+ if p.at_ts(PATH_NAME_REF_KINDS) {
+ let m = p.start();
+ p.error("missing thin-arrow `->`");
+ types::type_no_bounds(p);
+ m.complete(p, RET_TYPE);
+ true
+ } else {
+ false
+ }
+}
+
fn name_r(p: &mut Parser<'_>, recovery: TokenSet) {
if p.at(IDENT) {
let m = p.start();
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs
index c0cf43a87b..2eeaa25257 100644
--- a/crates/parser/src/grammar/attributes.rs
+++ b/crates/parser/src/grammar/attributes.rs
@@ -40,6 +40,86 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
// #![unsafe]
// #![unsafe =]
+fn cfg_attr_meta(p: &mut Parser<'_>, m: Marker) {
+ // test cfg_attr
+ // #![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))]
+ p.eat_contextual_kw(T![cfg_attr]);
+ p.bump(T!['(']);
+ cfg_predicate(p);
+ p.expect(T![,]);
+ while !p.at(T![')']) && !p.at(EOF) {
+ meta(p);
+ if !p.eat(T![,]) {
+ break;
+ }
+ }
+ p.expect(T![')']);
+ m.complete(p, CFG_ATTR_META);
+}
+
+const CFG_PREDICATE_FIRST_SET: TokenSet = TokenSet::new(&[T![true], T![false], T![ident]]);
+
+fn cfg_predicate(p: &mut Parser<'_>) {
+ let m = p.start();
+ if p.eat(T![true]) || p.eat(T![false]) {
+ // test cfg_true_false_pred
+ // #![cfg(true)]
+ // #![cfg(false)]
+ m.complete(p, CFG_ATOM);
+ return;
+ }
+ p.expect(T![ident]);
+ if p.eat(T![=]) {
+ if p.at(T![ident]) {
+ // This is required for completion, that inserts an identifier, to work in cases like
+ // `#[cfg(key = $0)]`, and also makes sense on itself.
+
+ // test_err key_ident_cfg_predicate
+ // #![cfg(key = value)]
+ p.err_and_bump("expected a string literal");
+ } else {
+ // test cfg_key_value_pred
+ // #![cfg(key = "value")]
+ p.expect(T![string]);
+ }
+ m.complete(p, CFG_ATOM);
+ } else if p.at(T!['(']) {
+ // test cfg_composite_pred
+ // #![cfg(any(a, all(b = "c", d)))]
+ delimited(
+ p,
+ T!['('],
+ T![')'],
+ T![,],
+ || "expected a cfg predicate".to_owned(),
+ CFG_PREDICATE_FIRST_SET,
+ |p| {
+ if p.at_ts(CFG_PREDICATE_FIRST_SET) {
+ cfg_predicate(p);
+ true
+ } else {
+ false
+ }
+ },
+ );
+ m.complete(p, CFG_COMPOSITE);
+ } else {
+ m.complete(p, CFG_ATOM);
+ }
+}
+
+fn cfg_meta(p: &mut Parser<'_>, m: Marker) {
+ // test cfg_meta
+ // #![cfg(foo)]
+ // #![cfg(foo = "bar",)]
+ p.eat_contextual_kw(T![cfg]);
+ p.bump(T!['(']);
+ cfg_predicate(p);
+ p.eat(T![,]);
+ p.expect(T![')']);
+ m.complete(p, CFG_META);
+}
+
// test metas
// #![simple_ident]
// #![simple::path]
@@ -62,11 +142,23 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
// #![unsafe(simple::path::tt[a b c])]
// #![unsafe(simple::path::tt{a b c})]
pub(super) fn meta(p: &mut Parser<'_>) {
- let meta = p.start();
- let is_unsafe = p.eat(T![unsafe]);
- if is_unsafe {
+ let m = p.start();
+ if p.eat(T![unsafe]) {
p.expect(T!['(']);
+ meta(p);
+ p.expect(T![')']);
+ m.complete(p, UNSAFE_META);
+ return;
+ }
+
+ if p.nth_at(1, T!['(']) {
+ if p.at_contextual_kw(T![cfg_attr]) {
+ return cfg_attr_meta(p, m);
+ } else if p.at_contextual_kw(T![cfg]) {
+ return cfg_meta(p, m);
+ }
}
+
paths::attr_path(p);
match p.current() {
@@ -75,13 +167,14 @@ pub(super) fn meta(p: &mut Parser<'_>) {
if expressions::expr(p).is_none() {
p.error("expected expression");
}
+ m.complete(p, KEY_VALUE_META);
+ }
+ T!['('] | T!['['] | T!['{'] => {
+ items::token_tree(p);
+ m.complete(p, TOKEN_TREE_META);
+ }
+ _ => {
+ m.complete(p, PATH_META);
}
- T!['('] | T!['['] | T!['{'] => items::token_tree(p),
- _ => {}
- }
- if is_unsafe {
- p.expect(T![')']);
}
-
- meta.complete(p, META);
}
diff --git a/crates/parser/src/grammar/items.rs b/crates/parser/src/grammar/items.rs
index c609f9383e..c5c6e04dd4 100644
--- a/crates/parser/src/grammar/items.rs
+++ b/crates/parser/src/grammar/items.rs
@@ -167,6 +167,25 @@ 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]) {
@@ -216,6 +235,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker, is_in_extern: bool) -> Res
T![trait] => traits::trait_(p, m),
T![impl] => traits::impl_(p, m),
+ T![type] if p.nth(1) == T![const] => consts::konst(p, m),
T![type] => type_alias(p, m),
// test extern_block
@@ -247,6 +267,9 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
T![use] => use_item::use_(p, m),
T![mod] => mod_item(p, m),
+ // test type_const
+ // type const FOO: i32 = 2;
+ T![type] if la == T![const] => consts::konst(p, m),
T![type] => type_alias(p, m),
T![struct] => adt::strukt(p, m),
T![enum] => adt::enum_(p, m),
@@ -422,7 +445,12 @@ fn fn_(p: &mut Parser<'_>, m: Marker) {
// test function_ret_type
// fn foo() {}
// fn bar() -> () {}
- opt_ret_type(p);
+ if !opt_ret_type(p) {
+ // test_err function_ret_type_missing_arrow
+ // fn foo() usize {}
+ // fn bar() super::Foo {}
+ opt_no_arrow_ret_type(p);
+ }
// test_err fn_ret_recovery
// fn foo() -> A>]) { let x = 1; }
diff --git a/crates/parser/src/grammar/items/adt.rs b/crates/parser/src/grammar/items/adt.rs
index cfba4c3a77..a030190ad3 100644
--- a/crates/parser/src/grammar/items/adt.rs
+++ b/crates/parser/src/grammar/items/adt.rs
@@ -133,7 +133,30 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) {
// struct S { #[attr] f: f32 }
attributes::outer_attrs(p);
opt_visibility(p, false);
- p.eat(T![unsafe]);
+
+ if p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test record_mut_restrictions_before
+ // struct Foo { mut(super) unsafe i: i32 }
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
+ // We accept mut restriction both after and before `unsafe`, as the order is undecided yet.
+ if p.eat(T![unsafe]) && p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test record_mut_restrictions_after
+ // struct Foo { unsafe mut(super) i: i32 }
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
if p.at(IDENT) {
name(p);
p.expect(T![:]);
@@ -175,6 +198,18 @@ fn tuple_field_list(p: &mut Parser<'_>) {
// struct S (#[attr] f32);
attributes::outer_attrs(p);
let has_vis = opt_visibility(p, true);
+
+ if p.at(T![mut]) && p.nth(1) == T!['('] {
+ // test tuple_mut_restrictions
+ // struct Foo(pub(crate) mut(super) i32);
+ let m = p.start();
+ p.bump(T![mut]);
+ if !opt_visibility_inner(p, false) {
+ p.error("expected a mut restriction");
+ }
+ m.complete(p, MUT_RESTRICTION);
+ }
+
if !p.at_ts(types::TYPE_FIRST) {
p.error("expected a type");
if has_vis {
diff --git a/crates/parser/src/grammar/items/consts.rs b/crates/parser/src/grammar/items/consts.rs
index e6a8aca586..cc5bb73bdc 100644
--- a/crates/parser/src/grammar/items/consts.rs
+++ b/crates/parser/src/grammar/items/consts.rs
@@ -3,6 +3,7 @@ use super::*;
// test const_item
// const C: u32 = 92;
pub(super) fn konst(p: &mut Parser<'_>, m: Marker) {
+ p.eat(T![type]);
p.bump(T![const]);
const_or_static(p, m, true);
}
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index c62356d5c9..667bb68c64 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -59,6 +59,9 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
}
_ if paths::is_path_start(p) => path_or_macro_type(p, allow_bounds),
LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
+ T!['{'] => {
+ p.err_recover("expected type, found `{`", TYPE_RECOVERY_SET);
+ }
_ => {
p.err_recover("expected type", TYPE_RECOVERY_SET);
}
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index a2295e4495..59fa3ee773 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -116,6 +116,8 @@ pub enum SyntaxKind {
AWAIT_KW,
BIKESHED_KW,
BUILTIN_KW,
+ CFG_ATTR_KW,
+ CFG_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
DYN_KW,
@@ -186,6 +188,10 @@ pub enum SyntaxKind {
BREAK_EXPR,
CALL_EXPR,
CAST_EXPR,
+ CFG_ATOM,
+ CFG_ATTR_META,
+ CFG_COMPOSITE,
+ CFG_META,
CLOSURE_EXPR,
CONST,
CONST_ARG,
@@ -212,10 +218,12 @@ pub enum SyntaxKind {
IDENT_PAT,
IF_EXPR,
IMPL,
+ IMPL_RESTRICTION,
IMPL_TRAIT_TYPE,
INDEX_EXPR,
INFER_TYPE,
ITEM_LIST,
+ KEY_VALUE_META,
LABEL,
LET_ELSE,
LET_EXPR,
@@ -238,9 +246,9 @@ pub enum SyntaxKind {
MATCH_ARM_LIST,
MATCH_EXPR,
MATCH_GUARD,
- META,
METHOD_CALL_EXPR,
MODULE,
+ MUT_RESTRICTION,
NAME,
NAME_REF,
NEVER_TYPE,
@@ -254,6 +262,7 @@ pub enum SyntaxKind {
PAREN_TYPE,
PATH,
PATH_EXPR,
+ PATH_META,
PATH_PAT,
PATH_SEGMENT,
PATH_TYPE,
@@ -285,6 +294,7 @@ pub enum SyntaxKind {
STMT_LIST,
STRUCT,
TOKEN_TREE,
+ TOKEN_TREE_META,
TRAIT,
TRY_BLOCK_MODIFIER,
TRY_EXPR,
@@ -302,6 +312,7 @@ pub enum SyntaxKind {
TYPE_PARAM,
UNDERSCORE_EXPR,
UNION,
+ UNSAFE_META,
USE,
USE_BOUND_GENERIC_ARGS,
USE_TREE,
@@ -309,6 +320,7 @@ pub enum SyntaxKind {
VARIANT,
VARIANT_LIST,
VISIBILITY,
+ VISIBILITY_INNER,
WHERE_CLAUSE,
WHERE_PRED,
WHILE_EXPR,
@@ -360,6 +372,10 @@ impl SyntaxKind {
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
+ | CFG_ATOM
+ | CFG_ATTR_META
+ | CFG_COMPOSITE
+ | CFG_META
| CLOSURE_EXPR
| CONST
| CONST_ARG
@@ -386,10 +402,12 @@ impl SyntaxKind {
| IDENT_PAT
| IF_EXPR
| IMPL
+ | IMPL_RESTRICTION
| IMPL_TRAIT_TYPE
| INDEX_EXPR
| INFER_TYPE
| ITEM_LIST
+ | KEY_VALUE_META
| LABEL
| LET_ELSE
| LET_EXPR
@@ -412,9 +430,9 @@ impl SyntaxKind {
| MATCH_ARM_LIST
| MATCH_EXPR
| MATCH_GUARD
- | META
| METHOD_CALL_EXPR
| MODULE
+ | MUT_RESTRICTION
| NAME
| NAME_REF
| NEVER_TYPE
@@ -428,6 +446,7 @@ impl SyntaxKind {
| PAREN_TYPE
| PATH
| PATH_EXPR
+ | PATH_META
| PATH_PAT
| PATH_SEGMENT
| PATH_TYPE
@@ -459,6 +478,7 @@ impl SyntaxKind {
| STMT_LIST
| STRUCT
| TOKEN_TREE
+ | TOKEN_TREE_META
| TRAIT
| TRY_BLOCK_MODIFIER
| TRY_EXPR
@@ -476,6 +496,7 @@ impl SyntaxKind {
| TYPE_PARAM
| UNDERSCORE_EXPR
| UNION
+ | UNSAFE_META
| USE
| USE_BOUND_GENERIC_ARGS
| USE_TREE
@@ -483,6 +504,7 @@ impl SyntaxKind {
| VARIANT
| VARIANT_LIST
| VISIBILITY
+ | VISIBILITY_INNER
| WHERE_CLAUSE
| WHERE_PRED
| WHILE_EXPR
@@ -601,6 +623,8 @@ impl SyntaxKind {
AUTO_KW => "auto",
BIKESHED_KW => "bikeshed",
BUILTIN_KW => "builtin",
+ CFG_KW => "cfg",
+ CFG_ATTR_KW => "cfg_attr",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
DYN_KW => "dyn",
@@ -704,6 +728,8 @@ impl SyntaxKind {
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
+ CFG_KW => true,
+ CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
@@ -795,6 +821,8 @@ impl SyntaxKind {
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
+ CFG_KW => true,
+ CFG_ATTR_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
DYN_KW if edition < Edition::Edition2018 => true,
@@ -949,6 +977,8 @@ impl SyntaxKind {
"auto" => AUTO_KW,
"bikeshed" => BIKESHED_KW,
"builtin" => BUILTIN_KW,
+ "cfg" => CFG_KW,
+ "cfg_attr" => CFG_ATTR_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
"dyn" if edition < Edition::Edition2018 => DYN_KW,
@@ -1121,6 +1151,8 @@ macro_rules ! T_ {
[auto] => { $ crate :: SyntaxKind :: AUTO_KW };
[bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW };
[builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW };
+ [cfg] => { $ crate :: SyntaxKind :: CFG_KW };
+ [cfg_attr] => { $ crate :: SyntaxKind :: CFG_ATTR_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
[dyn] => { $ crate :: SyntaxKind :: DYN_KW };
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs
index 4c001104fe..6dfb78b128 100644
--- a/crates/parser/test_data/generated/runner.rs
+++ b/crates/parser/test_data/generated/runner.rs
@@ -87,6 +87,22 @@ mod ok {
#[test]
fn cast_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/cast_expr.rs"); }
#[test]
+ fn cfg_attr() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_attr.rs"); }
+ #[test]
+ fn cfg_composite_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_composite_pred.rs");
+ }
+ #[test]
+ fn cfg_key_value_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_key_value_pred.rs");
+ }
+ #[test]
+ fn cfg_meta() { run_and_expect_no_errors("test_data/parser/inline/ok/cfg_meta.rs"); }
+ #[test]
+ fn cfg_true_false_pred() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/cfg_true_false_pred.rs");
+ }
+ #[test]
fn closure_binder() {
run_and_expect_no_errors("test_data/parser/inline/ok/closure_binder.rs");
}
@@ -332,6 +348,10 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/impl_item_never_type.rs");
}
#[test]
+ fn impl_restrictions() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/impl_restrictions.rs");
+ }
+ #[test]
fn impl_trait_type() {
run_and_expect_no_errors("test_data/parser/inline/ok/impl_trait_type.rs");
}
@@ -540,6 +560,14 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/record_literal_field_with_attr.rs");
}
#[test]
+ fn record_mut_restrictions_after() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_after.rs");
+ }
+ #[test]
+ fn record_mut_restrictions_before() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/record_mut_restrictions_before.rs");
+ }
+ #[test]
fn record_pat_field() {
run_and_expect_no_errors("test_data/parser/inline/ok/record_pat_field.rs");
}
@@ -642,6 +670,10 @@ mod ok {
run_and_expect_no_errors("test_data/parser/inline/ok/tuple_field_attrs.rs");
}
#[test]
+ fn tuple_mut_restrictions() {
+ run_and_expect_no_errors("test_data/parser/inline/ok/tuple_mut_restrictions.rs");
+ }
+ #[test]
fn tuple_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/tuple_pat.rs"); }
#[test]
fn tuple_pat_fields() {
@@ -656,6 +688,8 @@ mod ok {
#[test]
fn type_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/type_alias.rs"); }
#[test]
+ fn type_const() { run_and_expect_no_errors("test_data/parser/inline/ok/type_const.rs"); }
+ #[test]
fn type_item_type_params() {
run_and_expect_no_errors("test_data/parser/inline/ok/type_item_type_params.rs");
}
@@ -793,6 +827,10 @@ mod err {
run_and_expect_errors("test_data/parser/inline/err/fn_ret_recovery.rs");
}
#[test]
+ fn function_ret_type_missing_arrow() {
+ run_and_expect_errors("test_data/parser/inline/err/function_ret_type_missing_arrow.rs");
+ }
+ #[test]
fn gen_fn() {
run_and_expect_errors_with_edition(
"test_data/parser/inline/err/gen_fn.rs",
@@ -822,6 +860,10 @@ mod err {
);
}
#[test]
+ fn key_ident_cfg_predicate() {
+ run_and_expect_errors("test_data/parser/inline/err/key_ident_cfg_predicate.rs");
+ }
+ #[test]
fn let_else_right_curly_brace() {
run_and_expect_errors("test_data/parser/inline/err/let_else_right_curly_brace.rs");
}
diff --git a/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/crates/parser/test_data/parser/err/0005_attribute_recover.rast
index 77b4d06321..cf45dcf522 100644
--- a/crates/parser/test_data/parser/err/0005_attribute_recover.rast
+++ b/crates/parser/test_data/parser/err/0005_attribute_recover.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -37,7 +37,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/err/0025_nope.rast b/crates/parser/test_data/parser/err/0025_nope.rast
index b6bc008837..23964ab9d9 100644
--- a/crates/parser/test_data/parser/err/0025_nope.rast
+++ b/crates/parser/test_data/parser/err/0025_nope.rast
@@ -194,7 +194,7 @@ SOURCE_FILE
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 95: expected type
+error 95: expected type, found `{`
error 95: expected COMMA
error 96: expected field
error 98: expected field declaration
diff --git a/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast b/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
index 3768a55d53..31db794d9f 100644
--- a/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
+++ b/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast
@@ -26,5 +26,5 @@ SOURCE_FILE
L_CURLY "{"
R_CURLY "}"
WHITESPACE "\n"
-error 26: expected type
+error 26: expected type, found `{`
error 26: expected colon
diff --git a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
index b657e98341..2334b730e4 100644
--- a/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
+++ b/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
@@ -136,15 +136,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
diff --git a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
index b5bc3d84df..acacee2348 100644
--- a/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
+++ b/crates/parser/test_data/parser/err/0033_match_arms_outer_attrs.rast
@@ -48,15 +48,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
R_CURLY "}"
diff --git a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
index 172bc099b5..37116ca895 100644
--- a/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
+++ b/crates/parser/test_data/parser/inline/err/crate_visibility_empty_recover.rast
@@ -2,8 +2,9 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast
new file mode 100644
index 0000000000..c0bca6ed1c
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rast
@@ -0,0 +1,50 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "usize"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "bar"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ RET_TYPE
+ PATH_TYPE
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+error 9: missing thin-arrow `->`
+error 27: missing thin-arrow `->`
diff --git a/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs
new file mode 100644
index 0000000000..f48e539df5
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/function_ret_type_missing_arrow.rs
@@ -0,0 +1,2 @@
+fn foo() usize {}
+fn bar() super::Foo {}
diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast
new file mode 100644
index 0000000000..de5fc7d5bd
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rast
@@ -0,0 +1,19 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "key"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ ERROR
+ IDENT "value"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+error 13: expected a string literal
diff --git a/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs
new file mode 100644
index 0000000000..9a981bf939
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/err/key_ident_cfg_predicate.rs
@@ -0,0 +1 @@
+#![cfg(key = value)]
diff --git a/crates/parser/test_data/parser/inline/err/meta_recovery.rast b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
index b5c16e0798..9e456c9855 100644
--- a/crates/parser/test_data/parser/inline/err/meta_recovery.rast
+++ b/crates/parser/test_data/parser/inline/err/meta_recovery.rast
@@ -3,14 +3,14 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -24,7 +24,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH
PATH_SEGMENT
@@ -37,7 +37,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH
PATH_SEGMENT
@@ -52,18 +52,20 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
+ PATH_META
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
WHITESPACE " "
- EQ "="
+ KEY_VALUE_META
+ EQ "="
R_BRACK "]"
WHITESPACE "\n"
error 3: expected identifier, `self`, `super`, `crate`, or `Self`
@@ -77,7 +79,7 @@ error 41: expected L_PAREN
error 41: expected identifier, `self`, `super`, `crate`, or `Self`
error 41: expected R_PAREN
error 52: expected L_PAREN
-error 52: expected identifier, `self`, `super`, `crate`, or `Self`
+error 53: expected identifier, `self`, `super`, `crate`, or `Self`
error 54: expected expression
error 54: expected expression
error 54: expected R_PAREN
diff --git a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
index ae1074c368..672f2c2f7f 100644
--- a/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/arg_with_attr.rast
@@ -24,7 +24,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/array_attrs.rast b/crates/parser/test_data/parser/inline/ok/array_attrs.rast
index 6eb8af3311..2812bbf71b 100644
--- a/crates/parser/test_data/parser/inline/ok/array_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/array_attrs.rast
@@ -31,15 +31,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
INT_NUMBER "2"
diff --git a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
index 9cb3c8a5c3..7c6dbf65cf 100644
--- a/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/assoc_item_list_inner_attrs.rast
@@ -15,7 +15,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
index 81b7f2b3cb..248e6d1360 100644
--- a/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
+++ b/crates/parser/test_data/parser/inline/ok/attr_on_expr_stmt.rast
@@ -17,7 +17,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -39,7 +39,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -61,7 +61,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -71,7 +71,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -87,7 +87,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rast b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast
new file mode 100644
index 0000000000..9af94f447d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rast
@@ -0,0 +1,63 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_ATTR_META
+ CFG_ATTR_KW "cfg_attr"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "not"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ UNSAFE_META
+ UNSAFE_KW "unsafe"
+ L_PAREN "("
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ TOKEN_TREE
+ L_PAREN "("
+ R_PAREN ")"
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATTR_META
+ CFG_ATTR_KW "cfg_attr"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "all"
+ L_PAREN "("
+ CFG_ATOM
+ TRUE_KW "true"
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATOM
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"bar\""
+ R_PAREN ")"
+ COMMA ","
+ WHITESPACE " "
+ KEY_VALUE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "baz"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"baz\""
+ R_PAREN ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_attr.rs b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs
new file mode 100644
index 0000000000..5fe2776144
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_attr.rs
@@ -0,0 +1 @@
+#![cfg_attr(not(foo), unsafe(bar()), cfg_attr(all(true, foo = "bar"), baz = "baz"))]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast
new file mode 100644
index 0000000000..89d06d134f
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rast
@@ -0,0 +1,33 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
+ IDENT "any"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "a"
+ COMMA ","
+ WHITESPACE " "
+ CFG_COMPOSITE
+ IDENT "all"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "b"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"c\""
+ COMMA ","
+ WHITESPACE " "
+ CFG_ATOM
+ IDENT "d"
+ R_PAREN ")"
+ R_PAREN ")"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs
new file mode 100644
index 0000000000..7d830c1288
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_composite_pred.rs
@@ -0,0 +1 @@
+#![cfg(any(a, all(b = "c", d)))]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast
new file mode 100644
index 0000000000..e48d39bf55
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rast
@@ -0,0 +1,17 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "key"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"value\""
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs
new file mode 100644
index 0000000000..dc194ed86b
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_key_value_pred.rs
@@ -0,0 +1 @@
+#![cfg(key = "value")]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rast b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast
new file mode 100644
index 0000000000..f024cfd1aa
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rast
@@ -0,0 +1,30 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ STRING "\"bar\""
+ COMMA ","
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_meta.rs b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs
new file mode 100644
index 0000000000..ef0030e75f
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_meta.rs
@@ -0,0 +1,2 @@
+#![cfg(foo)]
+#![cfg(foo = "bar",)]
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast
new file mode 100644
index 0000000000..e33595a93d
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rast
@@ -0,0 +1,25 @@
+SOURCE_FILE
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ TRUE_KW "true"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
+ ATTR
+ POUND "#"
+ BANG "!"
+ L_BRACK "["
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
+ FALSE_KW "false"
+ R_PAREN ")"
+ R_BRACK "]"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs
new file mode 100644
index 0000000000..473582164a
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/cfg_true_false_pred.rs
@@ -0,0 +1,2 @@
+#![cfg(true)]
+#![cfg(false)]
diff --git a/crates/parser/test_data/parser/inline/ok/crate_visibility.rast b/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
index 8738292a9f..c946f19321 100644
--- a/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
+++ b/crates/parser/test_data/parser/inline/ok/crate_visibility.rast
@@ -2,12 +2,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -18,12 +19,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SELF_KW "self"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SELF_KW "self"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -34,12 +36,13 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SUPER_KW "super"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast b/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
index ac45c56956..1a551ea221 100644
--- a/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
+++ b/crates/parser/test_data/parser/inline/ok/crate_visibility_in.rast
@@ -2,19 +2,20 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- SUPER_KW "super"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "A"
- R_PAREN ")"
+ IDENT "A"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
@@ -25,14 +26,15 @@ SOURCE_FILE
STRUCT
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
STRUCT_KW "struct"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
index 28a216e873..5567a53c56 100644
--- a/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
+++ b/crates/parser/test_data/parser/inline/ok/generic_param_attribute.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -25,7 +25,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
new file mode 100644
index 0000000000..5f2680cbaa
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rast
@@ -0,0 +1,80 @@
+SOURCE_FILE
+ TRAIT
+ VISIBILITY
+ PUB_KW "pub"
+ WHITESPACE " "
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ IMPL_RESTRICTION
+ IMPL_KW "impl"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
+ WHITESPACE " "
+ TRAIT_KW "trait"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ TRAIT
+ IMPL_RESTRICTION
+ IMPL_KW "impl"
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
+ PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ COLON2 "::"
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ R_PAREN ")"
+ WHITESPACE " "
+ TRAIT_KW "trait"
+ WHITESPACE " "
+ NAME
+ IDENT "Bar"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ IMPL
+ IMPL_KW "impl"
+ WHITESPACE " "
+ TUPLE_TYPE
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ IMPL
+ IMPL_KW "impl"
+ WHITESPACE " "
+ PAREN_TYPE
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ WHITESPACE " "
+ ASSOC_ITEM_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
new file mode 100644
index 0000000000..0a46b158af
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/impl_restrictions.rs
@@ -0,0 +1,4 @@
+pub unsafe impl(crate) trait Foo {}
+impl(in super::bar) trait Bar {}
+impl () {}
+impl (i32) {}
diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
index 6fd9f42467..edb0438733 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arms_inner_attribute.rast
@@ -26,7 +26,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +41,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
index 0f7580c1a3..321db782d1 100644
--- a/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
+++ b/crates/parser/test_data/parser/inline/ok/match_arms_outer_attributes.rast
@@ -26,19 +26,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"some\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
@@ -55,19 +52,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"other\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
@@ -84,55 +78,46 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"many\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"attributes\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"before\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
WILDCARD_PAT
diff --git a/crates/parser/test_data/parser/inline/ok/metas.rast b/crates/parser/test_data/parser/inline/ok/metas.rast
index b1ac60b530..6360552a6f 100644
--- a/crates/parser/test_data/parser/inline/ok/metas.rast
+++ b/crates/parser/test_data/parser/inline/ok/metas.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH
PATH_SEGMENT
@@ -30,7 +30,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -46,7 +46,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH
PATH
@@ -72,7 +72,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -91,7 +91,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -110,7 +110,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -129,7 +129,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -158,7 +158,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -187,7 +187,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH
PATH
@@ -216,13 +216,14 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident"
+ PATH_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -230,18 +231,19 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ PATH_META
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "path"
+ IDENT "path"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -249,18 +251,19 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_expr"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- LITERAL
- STRING "\"\""
+ KEY_VALUE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -268,28 +271,29 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ KEY_VALUE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "Expr"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- LITERAL
- STRING "\"\""
+ IDENT "Expr"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ STRING "\"\""
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -297,21 +301,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_PAREN "("
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_PAREN ")"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_PAREN "("
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_PAREN ")"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -319,21 +324,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_BRACK "["
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_BRACK "]"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_BRACK "["
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_BRACK "]"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -341,21 +347,22 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "simple_ident_tt"
- TOKEN_TREE
- L_CURLY "{"
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_CURLY "}"
+ TOKEN_TREE_META
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple_ident_tt"
+ TOKEN_TREE
+ L_CURLY "{"
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_CURLY "}"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -363,31 +370,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_PAREN "("
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_PAREN ")"
+ IDENT "tt"
+ TOKEN_TREE
+ L_PAREN "("
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_PAREN ")"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -395,31 +403,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_BRACK "["
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_BRACK "]"
+ IDENT "tt"
+ TOKEN_TREE
+ L_BRACK "["
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_BRACK "]"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
@@ -427,31 +436,32 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ UNSAFE_META
UNSAFE_KW "unsafe"
L_PAREN "("
- PATH
+ TOKEN_TREE_META
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "simple"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "simple"
+ IDENT "path"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "path"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "tt"
- TOKEN_TREE
- L_CURLY "{"
- IDENT "a"
- WHITESPACE " "
- IDENT "b"
- WHITESPACE " "
- IDENT "c"
- R_CURLY "}"
+ IDENT "tt"
+ TOKEN_TREE
+ L_CURLY "{"
+ IDENT "a"
+ WHITESPACE " "
+ IDENT "b"
+ WHITESPACE " "
+ IDENT "c"
+ R_CURLY "}"
R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
index c63ea020a3..7495ba7b31 100644
--- a/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
+++ b/crates/parser/test_data/parser/inline/ok/param_outer_arg.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
index 639ee0eb77..cfa2694fd1 100644
--- a/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_field_attrs.rast
@@ -12,7 +12,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
index a1df70841e..717dee8c9f 100644
--- a/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_literal_field_with_attr.rast
@@ -25,15 +25,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast
new file mode 100644
index 0000000000..ebe3c81468
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rast
@@ -0,0 +1,35 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_FIELD
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ NAME
+ IDENT "i"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs
new file mode 100644
index 0000000000..bf37f60f85
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_after.rs
@@ -0,0 +1 @@
+struct Foo { unsafe mut(super) i: i32 }
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast
new file mode 100644
index 0000000000..7f76e737f7
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rast
@@ -0,0 +1,35 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ WHITESPACE " "
+ RECORD_FIELD_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ RECORD_FIELD
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ UNSAFE_KW "unsafe"
+ WHITESPACE " "
+ NAME
+ IDENT "i"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs
new file mode 100644
index 0000000000..9bbb80f205
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/record_mut_restrictions_before.rs
@@ -0,0 +1 @@
+struct Foo { mut(super) unsafe i: i32 }
diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
index f3d2fde466..7fba529be9 100644
--- a/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_pat_field.rast
@@ -88,18 +88,14 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
IDENT "any"
- TOKEN_TREE
- L_PAREN "("
- R_PAREN ")"
+ L_PAREN "("
R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
index f69ae1d644..af5b82b889 100644
--- a/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
+++ b/crates/parser/test_data/parser/inline/ok/record_pat_field_list.rast
@@ -146,18 +146,14 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_COMPOSITE
IDENT "any"
- TOKEN_TREE
- L_PAREN "("
- R_PAREN ")"
+ L_PAREN "("
R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
DOT2 ".."
diff --git a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
index db583f7d52..3a163e5b8e 100644
--- a/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
+++ b/crates/parser/test_data/parser/inline/ok/self_param_outer_attr.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
index 39857b23c6..76954927d5 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_attrs.rast
@@ -34,15 +34,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
INT_NUMBER "2"
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
index 1699602f4f..1f7100c46d 100644
--- a/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
+++ b/crates/parser/test_data/parser/inline/ok/tuple_field_attrs.rast
@@ -11,7 +11,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast
new file mode 100644
index 0000000000..944133ff85
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rast
@@ -0,0 +1,37 @@
+SOURCE_FILE
+ STRUCT
+ STRUCT_KW "struct"
+ WHITESPACE " "
+ NAME
+ IDENT "Foo"
+ TUPLE_FIELD_LIST
+ L_PAREN "("
+ TUPLE_FIELD
+ VISIBILITY
+ PUB_KW "pub"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
+ WHITESPACE " "
+ MUT_RESTRICTION
+ MUT_KW "mut"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs
new file mode 100644
index 0000000000..42653b0043
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/tuple_mut_restrictions.rs
@@ -0,0 +1 @@
+struct Foo(pub(crate) mut(super) i32);
diff --git a/crates/parser/test_data/parser/inline/ok/type_const.rast b/crates/parser/test_data/parser/inline/ok/type_const.rast
new file mode 100644
index 0000000000..9ceae9e44b
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/type_const.rast
@@ -0,0 +1,22 @@
+SOURCE_FILE
+ CONST
+ TYPE_KW "type"
+ WHITESPACE " "
+ CONST_KW "const"
+ WHITESPACE " "
+ NAME
+ IDENT "FOO"
+ COLON ":"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "2"
+ SEMICOLON ";"
+ WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/type_const.rs b/crates/parser/test_data/parser/inline/ok/type_const.rs
new file mode 100644
index 0000000000..8e2c425922
--- /dev/null
+++ b/crates/parser/test_data/parser/inline/ok/type_const.rs
@@ -0,0 +1 @@
+type const FOO: i32 = 2;
diff --git a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
index cb63ba80e7..ddab028c06 100644
--- a/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
+++ b/crates/parser/test_data/parser/ok/0006_inner_attributes.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -29,7 +29,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -44,7 +44,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -89,7 +89,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -104,7 +104,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -123,7 +123,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -138,7 +138,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -153,7 +153,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -175,7 +175,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0008_mod_item.rast b/crates/parser/test_data/parser/ok/0008_mod_item.rast
index adee67181b..cb6da8717d 100644
--- a/crates/parser/test_data/parser/ok/0008_mod_item.rast
+++ b/crates/parser/test_data/parser/ok/0008_mod_item.rast
@@ -48,7 +48,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
index dbb9bc54da..47a5cfce08 100644
--- a/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
+++ b/crates/parser/test_data/parser/ok/0011_outer_attribute.rast
@@ -3,21 +3,18 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "test"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n"
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +38,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0012_visibility.rast b/crates/parser/test_data/parser/ok/0012_visibility.rast
index 3d9322947b..348498daa9 100644
--- a/crates/parser/test_data/parser/ok/0012_visibility.rast
+++ b/crates/parser/test_data/parser/ok/0012_visibility.rast
@@ -52,12 +52,13 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- CRATE_KW "crate"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ CRATE_KW "crate"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
@@ -75,12 +76,13 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- PATH
- PATH_SEGMENT
- NAME_REF
- SUPER_KW "super"
- R_PAREN ")"
+ VISIBILITY_INNER
+ L_PAREN "("
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ SUPER_KW "super"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
@@ -98,24 +100,25 @@ SOURCE_FILE
FN
VISIBILITY
PUB_KW "pub"
- L_PAREN "("
- IN_KW "in"
- WHITESPACE " "
- PATH
+ VISIBILITY_INNER
+ L_PAREN "("
+ IN_KW "in"
+ WHITESPACE " "
PATH
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "foo"
+ IDENT "bar"
COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "bar"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "baz"
- R_PAREN ")"
+ IDENT "baz"
+ R_PAREN ")"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
diff --git a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
index 7c914e2542..c5d054702f 100644
--- a/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
+++ b/crates/parser/test_data/parser/ok/0017_attr_trailing_comma.rast
@@ -3,7 +3,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
index 318d492ab4..15ce6c70be 100644
--- a/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
+++ b/crates/parser/test_data/parser/ok/0035_weird_exprs.rast
@@ -11,7 +11,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -26,7 +26,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -41,7 +41,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -71,7 +71,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ KEY_VALUE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0044_let_attrs.rast b/crates/parser/test_data/parser/ok/0044_let_attrs.rast
index f3c20337e4..fcdc1a9895 100644
--- a/crates/parser/test_data/parser/ok/0044_let_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0044_let_attrs.rast
@@ -18,19 +18,16 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "feature"
WHITESPACE " "
EQ "="
WHITESPACE " "
STRING "\"backtrace\""
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE "\n "
LET_KW "let"
diff --git a/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
index c22d99f1ae..f26bb85df2 100644
--- a/crates/parser/test_data/parser/ok/0045_block_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0045_block_attrs.rast
@@ -16,7 +16,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -38,7 +38,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -53,7 +53,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -77,7 +77,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -119,7 +119,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
@@ -211,7 +211,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
index 4eb51cfdf0..3d33eb4ff7 100644
--- a/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
+++ b/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast
@@ -14,7 +14,7 @@ SOURCE_FILE
POUND "#"
BANG "!"
L_BRACK "["
- META
+ TOKEN_TREE_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
index eafee90db4..24d4392282 100644
--- a/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
+++ b/crates/parser/test_data/parser/ok/0051_parameter_attrs.rast
@@ -10,7 +10,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -20,7 +20,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -55,7 +55,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -116,7 +116,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -158,7 +158,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -181,7 +181,7 @@ SOURCE_FILE
POUND "#"
WHITESPACE " "
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -228,7 +228,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -255,7 +255,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -282,7 +282,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -316,7 +316,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -352,7 +352,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -389,7 +389,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -422,7 +422,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -456,7 +456,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
index b94d43beb3..c300b7af50 100644
--- a/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
+++ b/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast
@@ -5,7 +5,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
index 1415a866b6..b92d78e5bd 100644
--- a/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
+++ b/crates/parser/test_data/parser/ok/0062_macro_2.0.rast
@@ -54,7 +54,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
index e36399123b..f1c6d2efeb 100644
--- a/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
+++ b/crates/parser/test_data/parser/ok/0063_variadic_fun.rast
@@ -96,15 +96,12 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "cfg"
- TOKEN_TREE
- L_PAREN "("
+ CFG_META
+ CFG_KW "cfg"
+ L_PAREN "("
+ CFG_ATOM
IDENT "never"
- R_PAREN ")"
+ R_PAREN ")"
R_BRACK "]"
WHITESPACE " "
SLICE_PAT
diff --git a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
index 3d00b27ab8..5229b97eb2 100644
--- a/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
+++ b/crates/parser/test_data/parser/ok/0070_expr_attr_placement.rast
@@ -19,7 +19,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -39,7 +39,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
index 1cafc775cd..c0685448f2 100644
--- a/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
+++ b/crates/parser/test_data/parser/ok/0071_stmt_attr_placement.rast
@@ -17,7 +17,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -31,7 +31,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
@@ -56,7 +56,7 @@ SOURCE_FILE
ATTR
POUND "#"
L_BRACK "["
- META
+ PATH_META
PATH
PATH_SEGMENT
NAME_REF
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
index cd8944aa61..248de70f0e 100644
--- a/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
+++ b/crates/proc-macro-api/src/legacy_protocol/msg/flat.rs
@@ -495,6 +495,10 @@ impl<'a, T: SpanTransformer<Span = span::Span>> Writer<'a, '_, T, tt::iter::TtIt
}
}
+ #[expect(
+ clippy::explicit_counter_loop,
+ reason = "it looks better the current way since we use `first_tt` before the loop"
+ )]
fn subtree(&mut self, idx: usize, n_tt: usize, subtree: tt::iter::TtIter<'a>) {
let mut first_tt = self.token_tree.len();
self.token_tree.resize(first_tt + n_tt, !0);
diff --git a/crates/proc-macro-srv/src/dylib/proc_macros.rs b/crates/proc-macro-srv/src/dylib/proc_macros.rs
index 4065dbd0b4..cf00be0327 100644
--- a/crates/proc-macro-srv/src/dylib/proc_macros.rs
+++ b/crates/proc-macro-srv/src/dylib/proc_macros.rs
@@ -7,7 +7,7 @@ pub(crate) struct ProcMacros([bridge::client::ProcMacro]);
impl From<bridge::PanicMessage> for crate::PanicMessage {
fn from(p: bridge::PanicMessage) -> Self {
- Self { message: p.as_str().map(|s| s.to_owned()) }
+ Self { message: p.into_string() }
}
}
diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml
index 4828419003..8377e94c8d 100644
--- a/crates/profile/Cargo.toml
+++ b/crates/profile/Cargo.toml
@@ -16,8 +16,8 @@ doctest = false
cfg-if = "1.0.1"
jemalloc-ctl = { version = "0.5.4", package = "tikv-jemalloc-ctl", optional = true }
-[target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies]
-perf-event = "=0.4.7"
+[target.'cfg(all(target_os = "linux", not(target_env = "ohos"), any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))'.dependencies]
+perf-event = "=0.4.8"
[target.'cfg(all(target_os = "linux", target_env = "gnu"))'.dependencies]
libc.workspace = true
diff --git a/crates/profile/src/stop_watch.rs b/crates/profile/src/stop_watch.rs
index 00c37c01d2..a1c1383ad5 100644
--- a/crates/profile/src/stop_watch.rs
+++ b/crates/profile/src/stop_watch.rs
@@ -11,7 +11,11 @@ use crate::MemoryUsage;
pub struct StopWatch {
time: Instant,
- #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
+ #[cfg(all(
+ target_os = "linux",
+ not(target_env = "ohos"),
+ any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
+ ))]
counter: Option<perf_event::Counter>,
memory: MemoryUsage,
}
@@ -24,7 +28,11 @@ pub struct StopWatchSpan {
impl StopWatch {
pub fn start() -> StopWatch {
- #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
+ #[cfg(all(
+ target_os = "linux",
+ not(target_env = "ohos"),
+ any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
+ ))]
let counter = {
// When debugging rust-analyzer using rr, the perf-related syscalls cause it to abort.
// We allow disabling perf by setting the env var `RA_DISABLE_PERF`.
@@ -51,7 +59,11 @@ impl StopWatch {
let time = Instant::now();
StopWatch {
time,
- #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
+ #[cfg(all(
+ target_os = "linux",
+ not(target_env = "ohos"),
+ any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
+ ))]
counter,
memory,
}
@@ -60,13 +72,19 @@ impl StopWatch {
pub fn elapsed(&mut self) -> StopWatchSpan {
let time = self.time.elapsed();
- #[cfg(all(target_os = "linux", not(target_env = "ohos")))]
+ #[cfg(all(
+ target_os = "linux",
+ not(target_env = "ohos"),
+ any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
+ ))]
let instructions = self.counter.as_mut().and_then(|it| {
it.read().map_err(|err| eprintln!("Failed to read perf counter: {err}")).ok()
});
- #[cfg(all(target_os = "linux", target_env = "ohos"))]
- let instructions = None;
- #[cfg(not(target_os = "linux"))]
+ #[cfg(not(all(
+ target_os = "linux",
+ not(target_env = "ohos"),
+ any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")
+ )))]
let instructions = None;
let memory = MemoryUsage::now() - self.memory;
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index 792206b74f..5d8273832b 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -131,6 +131,8 @@ pub struct CargoConfig {
pub run_build_script_command: Option<Vec<String>>,
/// Extra args to pass to the cargo command.
pub extra_args: Vec<String>,
+ /// Extra args passed only to `cargo metadata`, not other cargo commands.
+ pub metadata_extra_args: Vec<String>,
/// Extra env vars to set when invoking the cargo command
pub extra_env: FxHashMap<String, Option<String>>,
pub invocation_strategy: InvocationStrategy,
@@ -320,6 +322,8 @@ pub struct CargoMetadataConfig {
pub targets: Vec<String>,
/// Extra args to pass to the cargo command.
pub extra_args: Vec<String>,
+ /// Extra args passed directly to `cargo metadata` without filtering.
+ pub metadata_extra_args: Vec<String>,
/// Extra env vars to set when invoking the cargo command
pub extra_env: FxHashMap<String, Option<String>>,
/// What kind of metadata are we fetching: workspace, rustc, or sysroot.
@@ -679,6 +683,7 @@ impl FetchMetadata {
other_options.push(arg.to_owned());
}
}
+ other_options.extend(config.metadata_extra_args.iter().cloned());
let mut lockfile_copy = None;
if cargo_toml.is_rust_manifest() {
diff --git a/crates/project-model/src/env.rs b/crates/project-model/src/env.rs
index 51c447945c..37cfcd5545 100644
--- a/crates/project-model/src/env.rs
+++ b/crates/project-model/src/env.rs
@@ -79,18 +79,31 @@ pub(crate) fn cargo_config_env(
for (key, entry) in env_toml {
let key = key.as_ref().as_ref();
let value = match entry.as_ref() {
- DeValue::String(s) => String::from(s.clone()),
+ DeValue::String(s) => {
+ // Plain string entries have no `force` option, so they should not
+ // override existing environment variables (matching Cargo behavior).
+ if extra_env.get(key).is_some_and(Option::is_some) {
+ continue;
+ }
+ if let Ok(val) = std::env::var(key) { val } else { String::from(s.clone()) }
+ }
DeValue::Table(entry) => {
// Each entry MUST have a `value` key.
let Some(map) = entry.get("value").and_then(|v| v.as_ref().as_str()) else {
continue;
};
- // If the entry already exists in the environment AND the `force` key is not set to
- // true, then don't overwrite the value.
- if extra_env.get(key).is_some_and(Option::is_some)
- && !entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false)
- {
- continue;
+ let is_forced =
+ entry.get("force").and_then(|v| v.as_ref().as_bool()).unwrap_or(false);
+ // If the entry already exists in the environment AND the `force` key is not set
+ // to true, use the existing value instead of the config value.
+ if !is_forced {
+ if extra_env.get(key).is_some_and(Option::is_some) {
+ continue;
+ }
+ if let Ok(val) = std::env::var(key) {
+ env.insert(key, val);
+ continue;
+ }
}
if let Some(base) = entry.get("relative").and_then(|v| {
@@ -124,38 +137,80 @@ fn parse_output_cargo_config_env_works() {
.unwrap();
let config_path = cwd.join(".cargo").join("config.toml");
let raw = r#"
-env.CARGO_WORKSPACE_DIR.relative = true
-env.CARGO_WORKSPACE_DIR.value = ""
-env.INVALID.relative = "invalidbool"
-env.INVALID.value = "../relative"
-env.RELATIVE.relative = true
-env.RELATIVE.value = "../relative"
-env.TEST.value = "test"
-env.FORCED.value = "test"
-env.FORCED.force = true
-env.UNFORCED.value = "test"
-env.UNFORCED.forced = false
-env.OVERWRITTEN.value = "test"
-env.NOT_AN_OBJECT = "value"
+env.RA_TEST_WORKSPACE_DIR.relative = true
+env.RA_TEST_WORKSPACE_DIR.value = ""
+env.RA_TEST_INVALID.relative = "invalidbool"
+env.RA_TEST_INVALID.value = "../relative"
+env.RA_TEST_RELATIVE.relative = true
+env.RA_TEST_RELATIVE.value = "../relative"
+env.RA_TEST_UNSET.value = "test"
+env.RA_TEST_FORCED.value = "test"
+env.RA_TEST_FORCED.force = true
+env.RA_TEST_UNFORCED.value = "test"
+env.RA_TEST_UNFORCED.forced = false
+env.RA_TEST_OVERWRITTEN.value = "test"
+env.RA_TEST_NOT_AN_OBJECT = "value"
"#;
let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n");
let config = CargoConfigFile::from_string_for_test(raw);
let extra_env = [
- ("FORCED", Some("ignored")),
- ("UNFORCED", Some("newvalue")),
- ("OVERWRITTEN", Some("newvalue")),
- ("TEST", None),
+ ("RA_TEST_FORCED", Some("ignored")),
+ ("RA_TEST_UNFORCED", Some("newvalue")),
+ ("RA_TEST_OVERWRITTEN", Some("newvalue")),
+ ("RA_TEST_UNSET", None),
]
.iter()
- .map(|(k, v)| (k.to_string(), v.map(ToString::to_string)))
+ .map(|(k, v)| (k.to_string(), v.map(str::to_owned)))
.collect();
let env = cargo_config_env(&Some(config), &extra_env);
- assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
- assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
- assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
- assert_eq!(env.get("TEST").as_deref(), Some("test"));
- assert_eq!(env.get("FORCED").as_deref(), Some("test"));
- assert_eq!(env.get("UNFORCED").as_deref(), Some("newvalue"));
- assert_eq!(env.get("OVERWRITTEN").as_deref(), Some("newvalue"));
- assert_eq!(env.get("NOT_AN_OBJECT").as_deref(), Some("value"));
+ assert_eq!(env.get("RA_TEST_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
+ assert_eq!(env.get("RA_TEST_RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
+ assert_eq!(env.get("RA_TEST_INVALID").as_deref(), Some("../relative"));
+ assert_eq!(env.get("RA_TEST_UNSET").as_deref(), Some("test"));
+ assert_eq!(env.get("RA_TEST_FORCED").as_deref(), Some("test"));
+ assert_eq!(env.get("RA_TEST_UNFORCED").as_deref(), Some("newvalue"));
+ assert_eq!(env.get("RA_TEST_OVERWRITTEN").as_deref(), Some("newvalue"));
+ assert_eq!(env.get("RA_TEST_NOT_AN_OBJECT").as_deref(), Some("value"));
+}
+
+#[test]
+fn cargo_config_env_respects_process_env() {
+ use itertools::Itertools;
+
+ let cwd = paths::AbsPathBuf::try_from(
+ paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap(),
+ )
+ .unwrap();
+ let config_path = cwd.join(".cargo").join("config.toml");
+
+ // SAFETY: this test is not run in parallel with other tests that depend on these env vars.
+ unsafe {
+ std::env::set_var("RA_TEST_PROCESS_ENV_STRING", "from_process");
+ std::env::set_var("RA_TEST_PROCESS_ENV_TABLE", "from_process");
+ std::env::set_var("RA_TEST_PROCESS_ENV_FORCED", "from_process");
+ }
+
+ let raw = r#"
+env.RA_TEST_PROCESS_ENV_STRING = "from_config"
+env.RA_TEST_PROCESS_ENV_TABLE.value = "from_config"
+env.RA_TEST_PROCESS_ENV_FORCED.value = "from_config"
+env.RA_TEST_PROCESS_ENV_FORCED.force = true
+"#;
+ let raw = raw.lines().map(|l| format!("{l} # {config_path}")).join("\n");
+ let config = CargoConfigFile::from_string_for_test(raw);
+ let extra_env = FxHashMap::default();
+ let env = cargo_config_env(&Some(config), &extra_env);
+
+ // Plain string form should use process env value, not config value
+ assert_eq!(env.get("RA_TEST_PROCESS_ENV_STRING").as_deref(), Some("from_process"));
+ // Table form without force should use process env value, not config value
+ assert_eq!(env.get("RA_TEST_PROCESS_ENV_TABLE").as_deref(), Some("from_process"));
+ // Table form with force=true should override process env
+ assert_eq!(env.get("RA_TEST_PROCESS_ENV_FORCED").as_deref(), Some("from_config"));
+
+ unsafe {
+ std::env::remove_var("RA_TEST_PROCESS_ENV_STRING");
+ std::env::remove_var("RA_TEST_PROCESS_ENV_TABLE");
+ std::env::remove_var("RA_TEST_PROCESS_ENV_FORCED");
+ }
}
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 581b5fa514..29a19bc32e 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -216,6 +216,7 @@ impl ProjectWorkspace {
features,
rustc_source,
extra_args,
+ metadata_extra_args,
extra_env,
set_test,
cfg_overrides,
@@ -289,6 +290,7 @@ impl ProjectWorkspace {
features: features.clone(),
targets: targets.clone(),
extra_args: extra_args.clone(),
+ metadata_extra_args: metadata_extra_args.clone(),
extra_env: extra_env.clone(),
toolchain_version: toolchain.clone(),
kind: "workspace",
@@ -343,6 +345,7 @@ impl ProjectWorkspace {
features: crate::CargoFeatures::default(),
targets: targets.clone(),
extra_args: extra_args.clone(),
+ metadata_extra_args: metadata_extra_args.clone(),
extra_env: extra_env.clone(),
toolchain_version: toolchain.clone(),
kind: "rustc-dev"
@@ -575,6 +578,7 @@ impl ProjectWorkspace {
features: config.features.clone(),
targets,
extra_args: config.extra_args.clone(),
+ metadata_extra_args: config.metadata_extra_args.clone(),
extra_env: config.extra_env.clone(),
toolchain_version: toolchain.clone(),
kind: "detached-file",
@@ -1942,6 +1946,7 @@ fn sysroot_metadata_config(
features: Default::default(),
targets,
extra_args: Default::default(),
+ metadata_extra_args: config.metadata_extra_args.clone(),
extra_env: config.extra_env.clone(),
toolchain_version,
kind: "sysroot",
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index beb83a8173..da2ec74019 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -28,7 +28,7 @@ dissimilar.workspace = true
ide-completion.workspace = true
indexmap.workspace = true
itertools.workspace = true
-scip = "0.5.2"
+scip = "0.7.1"
lsp-types = { version = "=0.95.0", features = ["proposed"] }
parking_lot = "0.12.4"
xflags = "0.3.2"
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 74828cba02..bf9a66bf3f 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -23,11 +23,12 @@ use hir_def::{
use hir_ty::InferenceResult;
use ide::{
Analysis, AnalysisHost, AnnotationConfig, DiagnosticsConfig, Edition, InlayFieldsToResolve,
- InlayHintsConfig, LineCol, RootDatabase,
+ InlayHintsConfig, LineCol, RaFixtureConfig, RootDatabase,
};
use ide_db::{
- EditionedFileId, LineIndexDatabase, MiniCore, SnippetCap,
+ EditionedFileId, SnippetCap,
base_db::{SourceDatabase, salsa::Database},
+ line_index,
};
use itertools::Itertools;
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace};
@@ -1367,6 +1368,7 @@ impl flags::AnalysisStats {
&InlayHintsConfig {
render_colons: false,
type_hints: true,
+ type_hints_placement: ide::TypeHintsPlacement::Inline,
sized_bound: false,
discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true,
@@ -1397,7 +1399,7 @@ impl flags::AnalysisStats {
closing_brace_hints_min_lines: Some(20),
fields_to_resolve: InlayFieldsToResolve::empty(),
range_exclusive_hints: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
},
analysis.editioned_file_id_to_vfs(file_id),
None,
@@ -1416,7 +1418,7 @@ impl flags::AnalysisStats {
annotate_enum_variant_references: false,
location: ide::AnnotationLocation::AboveName,
filter_adjacent_derive_implementations: false,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
for &file_id in file_ids {
let msg = format!("annotations: {}", vfs.file_path(file_id.file_id(db)));
@@ -1486,7 +1488,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id:
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range_rooted(db);
let path = vfs.file_path(original_range.file_id.file_id(db));
- let line_index = db.line_index(original_range.file_id.file_id(db));
+ let line_index = line_index(db, original_range.file_id.file_id(db));
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1502,7 +1504,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range_rooted(db);
let path = vfs.file_path(original_range.file_id.file_id(db));
- let line_index = db.line_index(original_range.file_id.file_id(db));
+ let line_index = line_index(db, original_range.file_id.file_id(db));
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1521,7 +1523,7 @@ fn expr_syntax_range<'a>(
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range_rooted(db);
let path = vfs.file_path(original_range.file_id.file_id(db));
- let line_index = db.line_index(original_range.file_id.file_id(db));
+ let line_index = line_index(db, original_range.file_id.file_id(db));
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
@@ -1542,7 +1544,7 @@ fn pat_syntax_range<'a>(
let node = src.map(|e| e.to_node(&root).syntax().clone());
let original_range = node.as_ref().original_file_range_rooted(db);
let path = vfs.file_path(original_range.file_id.file_id(db));
- let line_index = db.line_index(original_range.file_id.file_id(db));
+ let line_index = line_index(db, original_range.file_id.file_id(db));
let text_range = original_range.range;
let (start, end) =
(line_index.line_col(text_range.start()), line_index.line_col(text_range.end()));
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
index efbaad3c49..e50e1c26bb 100644
--- a/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -6,7 +6,7 @@ use rustc_hash::FxHashSet;
use hir::{Crate, Module, db::HirDatabase, sym};
use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity};
-use ide_db::{LineIndexDatabase, base_db::SourceDatabase};
+use ide_db::{base_db::SourceDatabase, line_index};
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
use crate::cli::{flags, progress_report::ProgressReport};
@@ -99,7 +99,7 @@ impl flags::Diagnostics {
}
let Diagnostic { code, message, range, severity, .. } = diagnostic;
- let line_index = db.line_index(range.file_id);
+ let line_index = line_index(db, range.file_id);
let start = line_index.line_col(range.range.start());
let end = line_index.line_col(range.range.end());
bar.println(format!(
diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs
index 3950a581fd..4f6de6850a 100644
--- a/crates/rust-analyzer/src/cli/lsif.rs
+++ b/crates/rust-analyzer/src/cli/lsif.rs
@@ -8,7 +8,7 @@ use ide::{
RootDatabase, StaticIndex, StaticIndexedFile, TokenId, TokenStaticData,
VendoredLibrariesConfig,
};
-use ide_db::{LineIndexDatabase, line_index::WideEncoding};
+use ide_db::{line_index, line_index::WideEncoding};
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace};
use lsp_types::lsif;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
@@ -120,9 +120,9 @@ impl LsifManager<'_, '_> {
}
let file_id = id.file_id;
let doc_id = self.get_file_id(file_id);
- let line_index = self.db.line_index(file_id);
+ let line_index = line_index(self.db, file_id);
let line_index = LineIndex {
- index: line_index,
+ index: line_index.clone(),
encoding: PositionEncoding::Wide(WideEncoding::Utf16),
endings: LineEndings::Unix,
};
@@ -241,9 +241,9 @@ impl LsifManager<'_, '_> {
let StaticIndexedFile { file_id, tokens, folds, .. } = file;
let doc_id = self.get_file_id(file_id);
let text = self.analysis.file_text(file_id).unwrap();
- let line_index = self.db.line_index(file_id);
+ let line_index = line_index(self.db, file_id);
let line_index = LineIndex {
- index: line_index,
+ index: line_index.clone(),
encoding: PositionEncoding::Wide(WideEncoding::Utf16),
endings: LineEndings::Unix,
};
diff --git a/crates/rust-analyzer/src/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs
index e8c88cadf6..0f7ef84a0e 100644
--- a/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/crates/rust-analyzer/src/cli/run_tests.rs
@@ -2,7 +2,7 @@
use hir::{Crate, Module};
use hir_ty::db::HirDatabase;
-use ide_db::{LineIndexDatabase, base_db::SourceDatabase};
+use ide_db::{base_db::SourceDatabase, line_index};
use profile::StopWatch;
use project_model::{CargoConfig, RustLibSource};
use syntax::TextRange;
@@ -38,7 +38,7 @@ impl flags::RunTests {
})
.filter(|x| x.is_test(db));
let span_formatter = |file_id, text_range: TextRange| {
- let line_col = match db.line_index(file_id).try_line_col(text_range.start()) {
+ let line_col = match line_index(db, file_id).try_line_col(text_range.start()) {
None => " (unknown line col)".to_owned(),
Some(x) => format!("#{}:{}", x.line + 1, x.col),
};
diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs
index ef6d4399e6..bca38ed82f 100644
--- a/crates/rust-analyzer/src/cli/scip.rs
+++ b/crates/rust-analyzer/src/cli/scip.rs
@@ -7,7 +7,7 @@ use ide::{
RootDatabase, StaticIndex, StaticIndexedFile, SymbolInformationKind, TextRange, TokenId,
TokenStaticData, VendoredLibrariesConfig,
};
-use ide_db::LineIndexDatabase;
+use ide_db::line_index;
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
use rustc_hash::{FxHashMap, FxHashSet};
use scip::types::{self as scip_types, SymbolInformation};
@@ -348,7 +348,7 @@ fn get_relative_filepath(
fn get_line_index(db: &RootDatabase, file_id: FileId) -> LineIndex {
LineIndex {
- index: db.line_index(file_id),
+ index: line_index(db, file_id).clone(),
encoding: PositionEncoding::Utf8,
endings: LineEndings::Unix,
}
diff --git a/crates/rust-analyzer/src/cli/unresolved_references.rs b/crates/rust-analyzer/src/cli/unresolved_references.rs
index 2d9b870f4d..f8eacbb670 100644
--- a/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -1,7 +1,7 @@
//! Reports references in code that the IDE layer cannot resolve.
use hir::{AnyDiagnostic, Crate, Module, Semantics, db::HirDatabase, sym};
use ide::{AnalysisHost, RootDatabase, TextRange};
-use ide_db::{FxHashSet, LineIndexDatabase as _, base_db::SourceDatabase, defs::NameRefClass};
+use ide_db::{FxHashSet, base_db::SourceDatabase, defs::NameRefClass, line_index};
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at};
use parser::SyntaxKind;
use syntax::{AstNode, WalkEvent, ast};
@@ -75,7 +75,7 @@ impl flags::UnresolvedReferences {
let file_path = vfs.file_path(file_id);
eprintln!("processing crate: {crate_name}, module: {file_path}",);
- let line_index = db.line_index(file_id);
+ let line_index = line_index(db, file_id);
let file_text = db.file_text(file_id);
for range in find_unresolved_references(db, &sema, file_id, &module) {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 2ccd85f0e3..3a88a8fe84 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -12,7 +12,8 @@ use ide::{
CompletionFieldsToResolve, DiagnosticsConfig, GenericParameterHints, GotoDefinitionConfig,
GotoImplementationConfig, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig,
- MemoryLayoutHoverRenderKind, RenameConfig, Snippet, SnippetScope, SourceRootId,
+ MemoryLayoutHoverRenderKind, RaFixtureConfig, RenameConfig, Snippet, SnippetScope,
+ SourceRootId,
};
use ide_db::{
MiniCore, SnippetCap,
@@ -313,6 +314,9 @@ config_data! {
/// Hide inlay type hints for constructors.
inlayHints_typeHints_hideNamedConstructor: bool = false,
+ /// Where to render type hints relative to their binding pattern.
+ inlayHints_typeHints_location: TypeHintsLocation = TypeHintsLocation::Inline,
+
/// Enable the experimental support for interpreting tests.
interpret_tests: bool = false,
@@ -727,6 +731,11 @@ config_data! {
/// the `Problems Panel`.
diagnostics_warningsAsInfo: Vec<String> = vec![],
+ /// Disable support for `#[rust_analyzer::rust_fixture]` snippets.
+ ///
+ /// If you are not working on rust-analyzer itself, you should ignore this config.
+ disableFixtureSupport: bool = false,
+
/// Enforce the import granularity setting for all files. If set to false rust-analyzer will
/// try to keep import styles consistent per file.
imports_granularity_enforce: bool = false,
@@ -822,6 +831,9 @@ config_data! {
///
/// Set this to `"all"` to pass `--all-features` to cargo.
cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]),
+ /// Extra arguments passed only to `cargo metadata`, not to other cargo invocations.
+ /// Useful for flags like `--config` that `cargo metadata` supports.
+ cargo_metadataExtraArgs: Vec<String> = vec![],
/// Whether to pass `--no-default-features` to cargo.
cargo_noDefaultFeatures: bool = false,
/// Whether to skip fetching dependencies. If set to "true", the analysis is performed
@@ -948,18 +960,30 @@ config_data! {
/// Override the command used for bench runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
- /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
- /// replace the package name, target option (such as `--bin` or `--example`), the target name and
- /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+ /// Use the placeholders:
+ /// - `${package}`: package name.
+ /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+ /// - `${target}`: target name (empty for `--lib`).
+ /// - `${test_name}`: the test path filter, e.g. `module::bench_func`.
+ /// - `${exact}`: `--exact` for single benchmarks, empty for modules.
+ /// - `${include_ignored}`: always empty for benchmarks.
+ /// - `${executable_args}`: all of the above binary args bundled together
+ /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
runnables_bench_overrideCommand: Option<Vec<String>> = None,
/// Command to be executed instead of 'cargo' for runnables.
runnables_command: Option<String> = None,
- /// Override the command used for bench runnables.
+ /// Override the command used for doc-test runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
- /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
- /// replace the package name, target option (such as `--bin` or `--example`), the target name and
- /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+ /// Use the placeholders:
+ /// - `${package}`: package name.
+ /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+ /// - `${target}`: target name (empty for `--lib`).
+ /// - `${test_name}`: the test path filter, e.g. `module::func`.
+ /// - `${exact}`: always empty for doc-tests.
+ /// - `${include_ignored}`: always empty for doc-tests.
+ /// - `${executable_args}`: all of the above binary args bundled together
+ /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
runnables_doctest_overrideCommand: Option<Vec<String>> = None,
/// Additional arguments to be passed to cargo for runnables such as
/// tests or binaries. For example, it may be `--release`.
@@ -977,9 +1001,15 @@ config_data! {
/// Override the command used for test runnables.
/// The first element of the array should be the program to execute (for example, `cargo`).
///
- /// Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
- /// replace the package name, target option (such as `--bin` or `--example`), the target name and
- /// the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+ /// Available placeholders:
+ /// - `${package}`: package name.
+ /// - `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+ /// - `${target}`: target name (empty for `--lib`).
+ /// - `${test_name}`: the test path filter, e.g. `module::test_func`.
+ /// - `${exact}`: `--exact` for single tests, empty for modules.
+ /// - `${include_ignored}`: `--include-ignored` for single tests, empty otherwise.
+ /// - `${executable_args}`: all of the above binary args bundled together
+ /// (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
runnables_test_overrideCommand: Option<Vec<String>> = None,
/// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
@@ -1061,6 +1091,7 @@ struct ClientInfo {
version: Option<Version>,
}
+/// The configuration of this rust-analyzer instance.
#[derive(Clone)]
pub struct Config {
/// Projects that have a Cargo.toml or a rust-project.json in a
@@ -1070,11 +1101,16 @@ pub struct Config {
/// Projects whose configuration was generated by a command
/// configured in discoverConfig.
discovered_projects_from_command: Vec<ProjectJsonFromCommand>,
- /// The workspace roots as registered by the LSP client
+ /// The workspace roots as registered by the LSP client.
workspace_roots: Vec<AbsPathBuf>,
caps: ClientCapabilities,
- /// The LSP root path, deprecated in favor of `workspace_roots`
+
+ /// The root of the first project encountered. This is deprecated
+ /// because rust-analyzer might be handling multiple projects.
+ ///
+ /// Prefer `workspace_roots` and `workspace_root_for()`.
root_path: AbsPathBuf,
+
snippets: Vec<Snippet>,
client_info: Option<ClientInfo>,
@@ -1504,6 +1540,8 @@ pub struct LensConfig {
// annotations
pub location: AnnotationLocation,
pub filter_adjacent_derive_implementations: bool,
+
+ disable_ra_fixture: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -1559,7 +1597,7 @@ impl LensConfig {
annotate_method_references: self.method_refs,
annotate_enum_variant_references: self.enum_variant_refs,
location: self.location.into(),
- minicore,
+ ra_fixture: RaFixtureConfig { minicore, disable_ra_fixture: self.disable_ra_fixture },
filter_adjacent_derive_implementations: self.filter_adjacent_derive_implementations,
}
}
@@ -1776,9 +1814,23 @@ impl Config {
s
}
- pub fn root_path(&self) -> &AbsPathBuf {
- // We should probably use `workspace_roots` here if set
- &self.root_path
+ /// Find the workspace root that contains the given path, using the
+ /// longest prefix match.
+ pub fn workspace_root_for(&self, path: &AbsPath) -> &AbsPathBuf {
+ self.workspace_roots
+ .iter()
+ .filter(|root| path.starts_with(root.as_path()))
+ .max_by_key(|root| root.as_str().len())
+ .unwrap_or(self.default_root_path())
+ }
+
+ /// Best-effort root path for the current project.
+ ///
+ /// Use `workspace_root_for` where possible, because
+ /// `default_root_path` may return the wrong path when a user has
+ /// multiple workspaces.
+ pub fn default_root_path(&self) -> &AbsPathBuf {
+ self.workspace_roots.first().unwrap_or(&self.root_path)
}
pub fn caps(&self) -> &ClientCapabilities {
@@ -1816,8 +1868,15 @@ impl Config {
}
}
+ pub fn ra_fixture<'a>(&self, minicore: MiniCore<'a>) -> RaFixtureConfig<'a> {
+ RaFixtureConfig { minicore, disable_ra_fixture: *self.disableFixtureSupport(None) }
+ }
+
pub fn call_hierarchy<'a>(&self, minicore: MiniCore<'a>) -> CallHierarchyConfig<'a> {
- CallHierarchyConfig { exclude_tests: self.references_excludeTests().to_owned(), minicore }
+ CallHierarchyConfig {
+ exclude_tests: self.references_excludeTests().to_owned(),
+ ra_fixture: self.ra_fixture(minicore),
+ }
}
pub fn completion<'a>(
@@ -1878,7 +1937,7 @@ impl Config {
})
.collect(),
exclude_traits: self.completion_excludeTraits(source_root),
- minicore,
+ ra_fixture: self.ra_fixture(minicore),
}
}
@@ -1987,12 +2046,12 @@ impl Config {
None => ide::SubstTyLen::Unlimited,
},
show_drop_glue: *self.hover_dropGlue_enable(),
- minicore,
+ ra_fixture: self.ra_fixture(minicore),
}
}
pub fn goto_definition<'a>(&self, minicore: MiniCore<'a>) -> GotoDefinitionConfig<'a> {
- GotoDefinitionConfig { minicore }
+ GotoDefinitionConfig { ra_fixture: self.ra_fixture(minicore) }
}
pub fn inlay_hints<'a>(&self, minicore: MiniCore<'a>) -> InlayHintsConfig<'a> {
@@ -2001,6 +2060,10 @@ impl Config {
InlayHintsConfig {
render_colons: self.inlayHints_renderColons().to_owned(),
type_hints: self.inlayHints_typeHints_enable().to_owned(),
+ type_hints_placement: match self.inlayHints_typeHints_location() {
+ TypeHintsLocation::Inline => ide::TypeHintsPlacement::Inline,
+ TypeHintsLocation::EndOfLine => ide::TypeHintsPlacement::EndOfLine,
+ },
sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
parameter_hints_for_missing_arguments: self
@@ -2082,7 +2145,7 @@ impl Config {
implicit_drop_hints: self.inlayHints_implicitDrops_enable().to_owned(),
implied_dyn_trait_hints: self.inlayHints_impliedDynTraitHints_enable().to_owned(),
range_exclusive_hints: self.inlayHints_rangeExclusiveHints_enable().to_owned(),
- minicore,
+ ra_fixture: self.ra_fixture(minicore),
}
}
@@ -2135,7 +2198,7 @@ impl Config {
.to_owned(),
inject_doc_comment: self.semanticHighlighting_doc_comment_inject_enable().to_owned(),
syntactic_name_ref_highlighting: false,
- minicore,
+ ra_fixture: self.ra_fixture(minicore),
}
}
@@ -2402,6 +2465,7 @@ impl Config {
target_dir_config: self.target_dir_from_config(source_root),
set_test: *self.cfg_setTest(source_root),
no_deps: *self.cargo_noDeps(source_root),
+ metadata_extra_args: self.cargo_metadataExtraArgs(source_root).clone(),
}
}
@@ -2621,6 +2685,7 @@ impl Config {
location: *self.lens_location(),
filter_adjacent_derive_implementations: *self
.gotoImplementations_filterAdjacentDerives(),
+ disable_ra_fixture: *self.disableFixtureSupport(None),
}
}
@@ -2999,6 +3064,13 @@ enum ClosureStyle {
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
+enum TypeHintsLocation {
+ Inline,
+ EndOfLine,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
enum ReborrowHintsDef {
Mutable,
#[serde(with = "true_or_always")]
@@ -3908,6 +3980,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"`hide`: Shows `...` for every closure type",
],
},
+ "TypeHintsLocation" => set! {
+ "type": "string",
+ "enum": ["inline", "end_of_line"],
+ "enumDescriptions": [
+ "Render type hints directly after the binding identifier.",
+ "Render type hints after the end of the containing `let` statement when possible.",
+ ],
+ },
"Option<MemoryLayoutHoverRenderKindDef>" => set! {
"anyOf": [
{
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index c41696bf3f..aad8bece95 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -673,27 +673,31 @@ impl FlycheckActor {
if self.diagnostics_received == DiagnosticsReceived::NotYet {
tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
// We finished without receiving any diagnostics.
- // Clear everything for good measure
- match &self.scope {
- FlycheckScope::Workspace => {
- self.send(FlycheckMessage::ClearDiagnostics {
- id: self.id,
- kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
- });
- }
- FlycheckScope::Package { package, workspace_deps } => {
- for pkg in
- std::iter::once(package).chain(workspace_deps.iter().flatten())
- {
- self.send(FlycheckMessage::ClearDiagnostics {
- id: self.id,
- kind: ClearDiagnosticsKind::All(ClearScope::Package(
- pkg.clone(),
- )),
- });
- }
- }
- }
+ //
+ // `cargo check` generally outputs something, even if there are no
+ // warnings/errors, so we always know which package was checked.
+ //
+ // ```text
+ // $ cargo check --message-format=json 2>/dev/null
+ // {"reason":"compiler-artifact","package_id":"path+file:///Users/wilfred/tmp/scratch#0.1.0",...}
+ // ```
+ //
+ // However, rustc only returns JSON if there are diagnostics present, so a
+ // build without warnings or errors has an empty output.
+ //
+ // ```
+ // $ rustc --error-format=json bad.rs
+ // {"$message_type":"diagnostic","message":"mismatched types","...}
+ //
+ // $ rustc --error-format=json good.rs
+ // ```
+ //
+ // So if we got zero diagnostics, it was almost certainly a check that
+ // wasn't specific to a package.
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
+ });
} else if res.is_ok() {
// We clear diagnostics for packages on
// `[CargoCheckMessage::CompilerArtifact]` but there seem to be setups where
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index ad07da7759..0c1c067ffa 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -7,9 +7,9 @@ use anyhow::Context;
use base64::{Engine, prelude::BASE64_STANDARD};
use ide::{
- AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve, FilePosition,
- FileRange, FileStructureConfig, FindAllRefsConfig, HoverAction, HoverGotoTypeData,
- InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
+ AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
+ CompletionItemImport, FilePosition, FileRange, FileStructureConfig, FindAllRefsConfig,
+ HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, Runnable, RunnableKind,
SingleResolve, SourceChange, TextEdit,
};
use ide_db::{FxHashMap, SymbolKind};
@@ -332,7 +332,7 @@ pub(crate) fn handle_view_crate_graph(
params: ViewCrateGraphParams,
) -> anyhow::Result<String> {
let _p = tracing::info_span!("handle_view_crate_graph").entered();
- let dot = snap.analysis.view_crate_graph(params.full)?.map_err(anyhow::Error::msg)?;
+ let dot = snap.analysis.view_crate_graph(params.full)?;
Ok(dot)
}
@@ -1233,7 +1233,10 @@ pub(crate) fn handle_completion_resolve(
.resolve_completion_edits(
&forced_resolve_completions_config,
position,
- resolve_data.imports.into_iter().map(|import| import.full_import_path),
+ resolve_data.imports.into_iter().map(|import| CompletionItemImport {
+ path: import.full_import_path,
+ as_underscore: import.as_underscore,
+ }),
)?
.into_iter()
.flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
@@ -1264,11 +1267,15 @@ pub(crate) fn handle_folding_range(
params: FoldingRangeParams,
) -> anyhow::Result<Option<Vec<FoldingRange>>> {
let _p = tracing::info_span!("handle_folding_range").entered();
+
let file_id = try_default!(from_proto::file_id(&snap, &params.text_document.uri)?);
- let folds = snap.analysis.folding_ranges(file_id)?;
+ let collapsed_text = snap.config.folding_range_collapsed_text();
+ let folds = snap.analysis.folding_ranges(file_id, collapsed_text)?;
+
let text = snap.analysis.file_text(file_id)?;
let line_index = snap.file_line_index(file_id)?;
let line_folding_only = snap.config.line_folding_only();
+
let res = folds
.into_iter()
.map(|it| to_proto::folding_range(&text, &line_index, line_folding_only, it))
@@ -1392,10 +1399,14 @@ pub(crate) fn handle_references(
let exclude_imports = snap.config.find_all_refs_exclude_imports();
let exclude_tests = snap.config.find_all_refs_exclude_tests();
-
let Some(refs) = snap.analysis.find_all_refs(
position,
- &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
+ &FindAllRefsConfig {
+ search_scope: None,
+ ra_fixture: snap.config.ra_fixture(snap.minicore()),
+ exclude_imports,
+ exclude_tests,
+ },
)?
else {
return Ok(None);
@@ -1416,12 +1427,7 @@ pub(crate) fn handle_references(
refs.references
.into_iter()
.flat_map(|(file_id, refs)| {
- refs.into_iter()
- .filter(|&(_, category)| {
- (!exclude_imports || !category.contains(ReferenceCategory::IMPORT))
- && (!exclude_tests || !category.contains(ReferenceCategory::TEST))
- })
- .map(move |(range, _)| FileRange { file_id, range })
+ refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
})
.chain(decl)
})
@@ -2202,7 +2208,13 @@ fn show_ref_command_link(
.analysis
.find_all_refs(
*position,
- &FindAllRefsConfig { search_scope: None, minicore: snap.minicore() },
+ &FindAllRefsConfig {
+ search_scope: None,
+
+ ra_fixture: snap.config.ra_fixture(snap.minicore()),
+ exclude_imports: snap.config.find_all_refs_exclude_imports(),
+ exclude_tests: snap.config.find_all_refs_exclude_tests(),
+ },
)
.unwrap_or(None)
{
@@ -2432,8 +2444,7 @@ fn run_rustfmt(
}
RustfmtConfig::CustomCommand { command, args } => {
let cmd = Utf8PathBuf::from(&command);
- let target_spec =
- crates.first().and_then(|&crate_id| snap.target_spec_for_file(file_id, crate_id));
+ let target_spec = TargetSpec::for_file(snap, file_id).ok().flatten();
let extra_env = snap.config.extra_env(source_root_id);
let mut cmd = match target_spec {
Some(TargetSpec::Cargo(_)) => {
@@ -2443,7 +2454,14 @@ fn run_rustfmt(
let cmd_path = if command.contains(std::path::MAIN_SEPARATOR)
|| (cfg!(windows) && command.contains('/'))
{
- snap.config.root_path().join(cmd).into()
+ let project_root = Utf8PathBuf::from_path_buf(current_dir.clone())
+ .ok()
+ .and_then(|p| AbsPathBuf::try_from(p).ok());
+ let project_root = project_root
+ .as_ref()
+ .map(|dir| snap.config.workspace_root_for(dir))
+ .unwrap_or(snap.config.default_root_path());
+ project_root.join(cmd).into()
} else {
cmd
};
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 6a74b8a54d..af449c473a 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -13,10 +13,10 @@
use hir::ChangeWithProcMacros;
use ide::{
AnalysisHost, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
- FilePosition, TextSize,
+ FilePosition, RaFixtureConfig, TextSize,
};
use ide_db::{
- MiniCore, SnippetCap,
+ SnippetCap,
imports::insert_use::{ImportGranularity, InsertUseConfig},
};
use project_model::CargoConfig;
@@ -190,7 +190,7 @@ fn integrated_completion_benchmark() {
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -245,7 +245,7 @@ fn integrated_completion_benchmark() {
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -298,7 +298,7 @@ fn integrated_completion_benchmark() {
exclude_traits: &[],
enable_auto_await: true,
enable_auto_iter: true,
- minicore: MiniCore::default(),
+ ra_fixture: RaFixtureConfig::default(),
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
diff --git a/crates/rust-analyzer/src/lsp.rs b/crates/rust-analyzer/src/lsp.rs
index c7a5a95e66..a6a35dadd9 100644
--- a/crates/rust-analyzer/src/lsp.rs
+++ b/crates/rust-analyzer/src/lsp.rs
@@ -3,7 +3,7 @@
use core::fmt;
use hir::Mutability;
-use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance};
+use ide::{CompletionItem, CompletionItemImport, CompletionItemRefMode, CompletionRelevance};
use tenthash::TentHash;
pub mod ext;
@@ -136,8 +136,10 @@ pub(crate) fn completion_item_hash(item: &CompletionItem, is_ref_completion: boo
hasher.update(item.import_to_add.len().to_ne_bytes());
for import_path in &item.import_to_add {
- hasher.update(import_path.len().to_ne_bytes());
- hasher.update(import_path);
+ let CompletionItemImport { path, as_underscore } = import_path;
+ hasher.update(path.len().to_ne_bytes());
+ hasher.update(path);
+ hasher.update([u8::from(*as_underscore)]);
}
hasher.finalize()
diff --git a/crates/rust-analyzer/src/lsp/capabilities.rs b/crates/rust-analyzer/src/lsp/capabilities.rs
index d6a694be91..3ad4cb70b4 100644
--- a/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -335,6 +335,20 @@ impl ClientCapabilities {
.unwrap_or_default()
}
+ pub fn folding_range_collapsed_text(&self) -> bool {
+ (|| -> _ {
+ self.0
+ .text_document
+ .as_ref()?
+ .folding_range
+ .as_ref()?
+ .folding_range
+ .as_ref()?
+ .collapsed_text
+ })()
+ .unwrap_or_default()
+ }
+
pub fn hierarchical_symbols(&self) -> bool {
(|| -> _ {
self.0
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index e6493eefef..5d0d9209de 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -858,6 +858,7 @@ pub struct InlayHintResolveData {
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionImport {
pub full_import_path: String,
+ pub as_underscore: bool,
}
#[derive(Debug, Deserialize, Default)]
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index e5b983dcbf..d857f23b67 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -413,7 +413,10 @@ fn completion_item(
item.import_to_add
.clone()
.into_iter()
- .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path })
+ .map(|import| lsp_ext::CompletionImport {
+ full_import_path: import.path,
+ as_underscore: import.as_underscore,
+ })
.collect()
} else {
Vec::new()
@@ -913,9 +916,9 @@ pub(crate) fn folding_range(
text: &str,
line_index: &LineIndex,
line_folding_only: bool,
- fold: Fold,
+ Fold { range: text_range, kind, collapsed_text }: Fold,
) -> lsp_types::FoldingRange {
- let kind = match fold.kind {
+ let kind = match kind {
FoldKind::Comment => Some(lsp_types::FoldingRangeKind::Comment),
FoldKind::Imports => Some(lsp_types::FoldingRangeKind::Imports),
FoldKind::Region => Some(lsp_types::FoldingRangeKind::Region),
@@ -930,17 +933,19 @@ pub(crate) fn folding_range(
| FoldKind::Array
| FoldKind::ExternCrates
| FoldKind::MatchArm
- | FoldKind::Function => None,
+ | FoldKind::Function
+ | FoldKind::Stmt(_)
+ | FoldKind::TailExpr(_) => None,
};
- let range = range(line_index, fold.range);
+ let range = range(line_index, text_range);
if line_folding_only {
// Clients with line_folding_only == true (such as VSCode) will fold the whole end line
// even if it contains text not in the folding range. To prevent that we exclude
// range.end.line from the folding region if there is more text after range.end
// on the same line.
- let has_more_text_on_end_line = text[TextRange::new(fold.range.end(), TextSize::of(text))]
+ let has_more_text_on_end_line = text[TextRange::new(text_range.end(), TextSize::of(text))]
.chars()
.take_while(|it| *it != '\n')
.any(|it| !it.is_whitespace());
@@ -951,13 +956,20 @@ pub(crate) fn folding_range(
range.end.line
};
+ let collapsed_text = collapsed_text.map(|collapsed_text| {
+ let range_start = text_range.start();
+ let line_start = range_start - TextSize::from(range.start.character);
+ let text_before_range = &text[TextRange::new(line_start, range_start)];
+ format!("{text_before_range}{collapsed_text}")
+ });
+
lsp_types::FoldingRange {
start_line: range.start.line,
start_character: None,
end_line,
end_character: None,
kind,
- collapsed_text: None,
+ collapsed_text,
}
} else {
lsp_types::FoldingRange {
@@ -966,7 +978,7 @@ pub(crate) fn folding_range(
end_line: range.end.line,
end_character: Some(range.end.character),
kind,
- collapsed_text: None,
+ collapsed_text,
}
}
}
@@ -2037,8 +2049,8 @@ fn main() {
}"#;
let (analysis, file_id) = Analysis::from_single_file(text.to_owned());
- let folds = analysis.folding_ranges(file_id).unwrap();
- assert_eq!(folds.len(), 4);
+ let folds = analysis.folding_ranges(file_id, true).unwrap();
+ assert_eq!(folds.len(), 5);
let line_index = LineIndex {
index: Arc::new(ide::LineIndex::new(text)),
@@ -2048,7 +2060,7 @@ fn main() {
let converted: Vec<lsp_types::FoldingRange> =
folds.into_iter().map(|it| folding_range(text, &line_index, true, it)).collect();
- let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
+ let expected_lines = [(0, 2), (4, 10), (5, 9), (5, 6), (7, 9)];
assert_eq!(converted.len(), expected_lines.len());
for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
assert_eq!(folding_range.start_line, *start_line);
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 7c494de6f7..f5b3658ea9 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -144,12 +144,11 @@ impl fmt::Debug for Event {
};
match self {
- Event::Lsp(lsp_server::Message::Notification(not)) => {
- if notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
- || notification_is::<lsp_types::notification::DidChangeTextDocument>(not)
- {
- return debug_non_verbose(not, f);
- }
+ Event::Lsp(lsp_server::Message::Notification(not))
+ if (notification_is::<lsp_types::notification::DidOpenTextDocument>(not)
+ || notification_is::<lsp_types::notification::DidChangeTextDocument>(not)) =>
+ {
+ return debug_non_verbose(not, f);
}
Event::Task(Task::Response(resp)) => {
return f
@@ -830,12 +829,19 @@ impl GlobalState {
let command = cfg.command.clone();
let discover = DiscoverCommand::new(self.discover_sender.clone(), command);
+ let discover_path = match &arg {
+ DiscoverProjectParam::Buildfile(it) => it,
+ DiscoverProjectParam::Path(it) => it,
+ };
+ let current_dir =
+ self.config.workspace_root_for(discover_path.as_path()).clone();
+
let arg = match arg {
DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it),
DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
};
- match discover.spawn(arg, self.config.root_path().as_ref()) {
+ match discover.spawn(arg, current_dir.as_ref()) {
Ok(handle) => {
if self.discover_jobs_active == 0 {
let title = &cfg.progress_label.clone();
@@ -953,7 +959,7 @@ impl GlobalState {
if let Some(dir) = dir {
message += &format!(
": {}",
- match dir.strip_prefix(self.config.root_path()) {
+ match dir.strip_prefix(self.config.workspace_root_for(&dir)) {
Some(relative_path) => relative_path.as_utf8_path(),
None => dir.as_ref(),
}
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 83f4a19b39..1832275eb3 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -390,7 +390,7 @@ impl GlobalState {
info!(%cause, "will fetch build data");
let workspaces = Arc::clone(&self.workspaces);
let config = self.config.cargo(None);
- let root_path = self.config.root_path().clone();
+ let root_path = self.config.default_root_path().clone();
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| {
sender.send(Task::FetchBuildData(BuildDataProgress::Begin)).unwrap();
@@ -582,7 +582,8 @@ impl GlobalState {
[
(base.clone(), "**/*.rs"),
(base.clone(), "**/Cargo.{lock,toml}"),
- (base, "**/rust-analyzer.toml"),
+ (base.clone(), "**/rust-analyzer.toml"),
+ (base, "**/*.md"),
]
})
})
@@ -607,6 +608,7 @@ impl GlobalState {
format!("{base}/**/*.rs"),
format!("{base}/**/Cargo.{{toml,lock}}"),
format!("{base}/**/rust-analyzer.toml"),
+ format!("{base}/**/*.md"),
]
})
})
@@ -883,7 +885,7 @@ impl GlobalState {
config,
crate::flycheck::FlycheckConfigJson::default(),
None,
- self.config.root_path().clone(),
+ self.config.default_root_path().clone(),
None,
None,
)]
@@ -975,6 +977,7 @@ pub(crate) fn should_refresh_for_change(
change_kind: ChangeKind,
additional_paths: &[&str],
) -> bool {
+ // Note: build scripts are retriggered on file save, no refresh is necessary
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
@@ -991,15 +994,20 @@ pub(crate) fn should_refresh_for_change(
return true;
}
+ // .cargo/config{.toml}
+ if matches!(file_name, "config.toml" | "config")
+ && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false)
+ {
+ return true;
+ }
+
+ // Everything below only matters when files are created or deleted
if change_kind == ChangeKind::Modify {
return false;
}
- // .cargo/config{.toml}
if path.extension().unwrap_or_default() != "rs" {
- let is_cargo_config = matches!(file_name, "config.toml" | "config")
- && path.parent().map(|parent| parent.as_str().ends_with(".cargo")).unwrap_or(false);
- return is_cargo_config;
+ return false;
}
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_str().ends_with(it)) {
diff --git a/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs
index 8be061cacf..5bdc9d8ca3 100644
--- a/crates/rust-analyzer/src/target_spec.rs
+++ b/crates/rust-analyzer/src/target_spec.rs
@@ -230,6 +230,18 @@ impl CargoTargetSpec {
};
let test_name = test_name.unwrap_or_default();
+ let exact = match kind {
+ RunnableKind::Test { test_id } | RunnableKind::Bench { test_id } => match test_id {
+ TestId::Path(_) => "--exact",
+ TestId::Name(_) => "",
+ },
+ _ => "",
+ };
+ let include_ignored = match kind {
+ RunnableKind::Test { .. } => "--include-ignored",
+ _ => "",
+ };
+
let target_arg = |kind| match kind {
TargetKind::Bin => "--bin",
TargetKind::Test => "--test",
@@ -249,7 +261,9 @@ impl CargoTargetSpec {
.replace("${package}", &spec.package)
.replace("${target_arg}", target_arg(spec.target_kind))
.replace("${target}", target(spec.target_kind, &spec.target))
- .replace("${test_name}", &test_name),
+ .replace("${test_name}", &test_name)
+ .replace("${exact}", exact)
+ .replace("${include_ignored}", include_ignored),
_ => arg,
};
@@ -274,15 +288,13 @@ impl CargoTargetSpec {
let mut executable_args = Vec::new();
match kind {
- RunnableKind::Test { test_id, attr } => {
+ RunnableKind::Test { test_id } => {
executable_args.push(test_id.to_string());
if let TestId::Path(_) = test_id {
executable_args.push("--exact".to_owned());
}
executable_args.extend(extra_test_binary_args);
- if attr.ignore {
- executable_args.push("--ignored".to_owned());
- }
+ executable_args.push("--include-ignored".to_owned());
}
RunnableKind::TestMod { path } => {
executable_args.push(path.clone());
@@ -369,23 +381,13 @@ mod tests {
SmolStr,
ast::{self, AstNode},
};
- use syntax_bridge::{
- DocCommentDesugarMode,
- dummy_test_span_utils::{DUMMY, DummyTestSpanMap},
- syntax_node_to_token_tree,
- };
fn check(cfg: &str, expected_features: &[&str]) {
let cfg_expr = {
let source_file = ast::SourceFile::parse(cfg, Edition::CURRENT).ok().unwrap();
- let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
- let tt = syntax_node_to_token_tree(
- tt.syntax(),
- &DummyTestSpanMap,
- DUMMY,
- DocCommentDesugarMode::Mbe,
- );
- CfgExpr::parse(&tt)
+ let cfg_predicate =
+ source_file.syntax().descendants().find_map(ast::CfgPredicate::cast).unwrap();
+ CfgExpr::parse_from_ast(cfg_predicate)
};
let mut features = vec![];
diff --git a/crates/rust-analyzer/tests/slow-tests/flycheck.rs b/crates/rust-analyzer/tests/slow-tests/flycheck.rs
index c1d53fb33a..e5d4d7c88e 100644
--- a/crates/rust-analyzer/tests/slow-tests/flycheck.rs
+++ b/crates/rust-analyzer/tests/slow-tests/flycheck.rs
@@ -110,3 +110,46 @@ fn main() {}
diagnostics.diagnostics,
);
}
+
+#[test]
+#[ignore = "this test tends to stuck, FIXME: investigate that"]
+fn test_flycheck_diagnostics_with_override_command_cleared_after_fix() {
+ if skip_slow_tests() {
+ return;
+ }
+
+ // Start with a program that is lint clean.
+ let server = Project::with_fixture(
+ r#"
+//- /Cargo.toml
+[package]
+name = "foo"
+version = "0.0.0"
+
+//- /src/main.rs
+fn main() {}
+"#,
+ )
+ .with_config(serde_json::json!({
+ "checkOnSave": true,
+ "check": {
+ "overrideCommand": ["rustc", "--error-format=json", "$saved_file"]
+ }
+ }))
+ .server()
+ .wait_until_workspace_is_loaded();
+
+ // Introduce an unused variable.
+ server.write_file_and_save("src/main.rs", "fn main() {\n let x = 1;\n}\n".to_owned());
+
+ let diags = server.wait_for_diagnostics();
+ assert!(
+ diags.diagnostics.iter().any(|d| d.message.contains("unused variable")),
+ "expected unused variable diagnostic, got: {:?}",
+ diags.diagnostics,
+ );
+
+ // Fix it and verify that diagnostics are cleared.
+ server.write_file_and_save("src/main.rs", "fn main() {\n let _x = 1;\n}\n".to_owned());
+ server.wait_for_diagnostics_cleared();
+}
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index fcdc8bb7cd..a863263078 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -262,7 +262,7 @@ fn main() {}
{
"args": {
"cargoArgs": ["test", "--package", "foo", "--test", "spam"],
- "executableArgs": ["test_eggs", "--exact", "--nocapture"],
+ "executableArgs": ["test_eggs", "--exact", "--nocapture", "--include-ignored"],
"overrideCargo": null,
"cwd": server.path().join("foo"),
"workspaceRoot": server.path().join("foo")
@@ -1577,6 +1577,6 @@ fn test() {
let res: serde_json::Value = serde_json::from_str(res.as_str().unwrap()).unwrap();
let arr = res.as_array().unwrap();
- assert_eq!(arr.len(), 2);
+ assert_eq!(arr.len(), 1);
expect![[r#"{"goal":"Goal { param_env: ParamEnv { clauses: [] }, predicate: Binder { value: TraitPredicate(usize: Trait, polarity:Positive), bound_vars: [] } }","result":"Err(NoSolution)","depth":0,"candidates":[]}"#]].assert_eq(&arr[0].to_string());
}
diff --git a/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
index cac7efd84a..dd113babff 100644
--- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs
+++ b/crates/rust-analyzer/tests/slow-tests/ratoml.rs
@@ -1008,3 +1008,45 @@ fn main() {
InternalTestingFetchConfigResponse::CheckWorkspace(true),
);
}
+
+#[test]
+fn ratoml_virtual_workspace() {
+ if skip_slow_tests() {
+ return;
+ }
+
+ let server = RatomlTest::new(
+ vec![
+ r#"
+//- /p1/Cargo.toml
+[workspace]
+members = ["member"]
+"#,
+ r#"
+//- /p1/rust-analyzer.toml
+assist.emitMustUse = true
+"#,
+ r#"
+//- /p1/member/Cargo.toml
+[package]
+name = "member"
+version = "0.1.0"
+edition = "2021"
+"#,
+ r#"
+//- /p1/member/src/lib.rs
+pub fn add(left: usize, right: usize) -> usize {
+ left + right
+}
+"#,
+ ],
+ vec!["p1"],
+ None,
+ );
+
+ server.query(
+ InternalTestingFetchConfigOption::AssistEmitMustUse,
+ 3,
+ InternalTestingFetchConfigResponse::AssistEmitMustUse(true),
+ );
+}
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 7ee31f3d53..7390403673 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -173,7 +173,8 @@ impl Project<'_> {
chalk_filter: std::env::var("CHALK_DEBUG").ok(),
profile_filter: std::env::var("RA_PROFILE").ok(),
json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(),
- };
+ }
+ .init();
});
let FixtureWithProjectMeta {
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index fe05ef9465..0a81cef52e 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -81,25 +81,24 @@ const _: () = {
#[derive(Hash)]
struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>);
- impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike<StructKey<'db, T0, T1, T2, T3>>
- for SyntaxContextData
+ impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike<StructKey<'db, T0, T1, T2, T3>> for SyntaxContextData
where
- Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>,
- Transparency: zalsa_::interned::HashEqLike<T1>,
- Edition: zalsa_::interned::HashEqLike<T2>,
- SyntaxContext: zalsa_::interned::HashEqLike<T3>,
+ Option<MacroCallId>: zalsa_::HashEqLike<T0>,
+ Transparency: zalsa_::HashEqLike<T1>,
+ Edition: zalsa_::HashEqLike<T2>,
+ SyntaxContext: zalsa_::HashEqLike<T3>,
{
fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
- zalsa_::interned::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h);
- zalsa_::interned::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h);
- zalsa_::interned::HashEqLike::<T2>::hash(&self.edition, &mut *h);
- zalsa_::interned::HashEqLike::<T3>::hash(&self.parent, &mut *h);
+ zalsa_::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h);
+ zalsa_::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h);
+ zalsa_::HashEqLike::<T2>::hash(&self.edition, &mut *h);
+ zalsa_::HashEqLike::<T3>::hash(&self.parent, &mut *h);
}
fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool {
- zalsa_::interned::HashEqLike::<T0>::eq(&self.outer_expn, &data.0)
- && zalsa_::interned::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1)
- && zalsa_::interned::HashEqLike::<T2>::eq(&self.edition, &data.2)
- && zalsa_::interned::HashEqLike::<T3>::eq(&self.parent, &data.3)
+ zalsa_::HashEqLike::<T0>::eq(&self.outer_expn, &data.0)
+ && zalsa_::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1)
+ && zalsa_::HashEqLike::<T2>::eq(&self.edition, &data.2)
+ && zalsa_::HashEqLike::<T3>::eq(&self.parent, &data.3)
}
}
impl zalsa_struct_::Configuration for SyntaxContext {
@@ -203,10 +202,10 @@ const _: () = {
impl<'db> SyntaxContext {
pub fn new<
Db,
- T0: zalsa_::interned::Lookup<Option<MacroCallId>> + std::hash::Hash,
- T1: zalsa_::interned::Lookup<Transparency> + std::hash::Hash,
- T2: zalsa_::interned::Lookup<Edition> + std::hash::Hash,
- T3: zalsa_::interned::Lookup<SyntaxContext> + std::hash::Hash,
+ T0: zalsa_::Lookup<Option<MacroCallId>> + std::hash::Hash,
+ T1: zalsa_::Lookup<Transparency> + std::hash::Hash,
+ T2: zalsa_::Lookup<Edition> + std::hash::Hash,
+ T3: zalsa_::Lookup<SyntaxContext> + std::hash::Hash,
>(
db: &'db Db,
outer_expn: T0,
@@ -218,10 +217,10 @@ const _: () = {
) -> Self
where
Db: ?Sized + salsa::Database,
- Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>,
- Transparency: zalsa_::interned::HashEqLike<T1>,
- Edition: zalsa_::interned::HashEqLike<T2>,
- SyntaxContext: zalsa_::interned::HashEqLike<T3>,
+ Option<MacroCallId>: zalsa_::HashEqLike<T0>,
+ Transparency: zalsa_::HashEqLike<T1>,
+ Edition: zalsa_::HashEqLike<T2>,
+ SyntaxContext: zalsa_::HashEqLike<T3>,
{
let (zalsa, zalsa_local) = db.zalsas();
@@ -236,10 +235,10 @@ const _: () = {
std::marker::PhantomData,
),
|id, data| SyntaxContextData {
- outer_expn: zalsa_::interned::Lookup::into_owned(data.0),
- outer_transparency: zalsa_::interned::Lookup::into_owned(data.1),
- edition: zalsa_::interned::Lookup::into_owned(data.2),
- parent: zalsa_::interned::Lookup::into_owned(data.3),
+ outer_expn: zalsa_::Lookup::into_owned(data.0),
+ outer_transparency: zalsa_::Lookup::into_owned(data.1),
+ edition: zalsa_::Lookup::into_owned(data.2),
+ parent: zalsa_::Lookup::into_owned(data.3),
opaque: opaque(zalsa_::FromId::from_id(id)),
opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)),
},
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 8909fb423c..e65836ed8d 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -21,6 +21,7 @@ rustc-literal-escaper.workspace = true
smol_str.workspace = true
triomphe.workspace = true
tracing.workspace = true
+smallvec.workspace = true
parser.workspace = true
stdx.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 3113fc7430..768cf2013d 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -126,9 +126,41 @@ MacroStmts =
Attr =
'#' '!'? '[' Meta ']'
+CfgAttrMeta =
+ 'cfg_attr' '(' CfgPredicate ',' (Meta (',' Meta)* ','?) ')'
+
+CfgMeta =
+ 'cfg' '(' CfgPredicate ','? ')'
+
+CfgPredicate =
+ CfgAtom
+| CfgComposite
+
+CfgAtom =
+ ('#ident' | 'true' | 'false') ('=' '@string')?
+
+CfgComposite =
+ keyword:'#ident' '(' (CfgPredicate (',' CfgPredicate)* ','?) ')'
+
Meta =
- 'unsafe' '(' Path ('=' Expr | TokenTree)? ')'
-| Path ('=' Expr | TokenTree)?
+ CfgAttrMeta
+| CfgMeta
+| UnsafeMeta
+| PathMeta
+| KeyValueMeta
+| TokenTreeMeta
+
+UnsafeMeta =
+ 'unsafe' '(' Meta ')'
+
+PathMeta =
+ Path
+
+KeyValueMeta =
+ Path '=' Expr
+
+TokenTreeMeta =
+ Path TokenTree
//*************************//
// Items //
@@ -245,6 +277,7 @@ RecordFieldList =
RecordField =
Attr* Visibility? 'unsafe'?
+ MutRestriction?
Name ':' Type ('=' default_val:ConstArg)?
TupleFieldList =
@@ -252,12 +285,16 @@ TupleFieldList =
TupleField =
Attr* Visibility?
+ MutRestriction?
Type
FieldList =
RecordFieldList
| TupleFieldList
+MutRestriction =
+ 'mut' VisibilityInner
+
Enum =
Attr* Visibility?
'enum' Name GenericParamList? WhereClause?
@@ -291,6 +328,7 @@ VariantDef =
Const =
Attr* Visibility?
'default'?
+ 'type'?
'const' (Name | '_') GenericParamList? ':' Type
('=' body:Expr)?
WhereClause? ';'
@@ -304,10 +342,14 @@ Static =
Trait =
Attr* Visibility?
'unsafe'? 'auto'?
+ ImplRestriction?
'trait' Name GenericParamList?
(((':' TypeBoundList?)? WhereClause? AssocItemList) |
('=' TypeBoundList? WhereClause? ';'))
+ImplRestriction =
+ 'impl' VisibilityInner
+
AssocItemList =
'{' Attr* AssocItem* '}'
@@ -336,8 +378,10 @@ ExternItem =
| TypeAlias
Visibility =
- 'pub' ('(' 'in'? Path ')')?
+ 'pub' VisibilityInner?
+VisibilityInner =
+ '(' 'in'? Path ')'
//****************************//
// Statements and Expressions //
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs
index 5d67fd4491..dc592a4372 100644
--- a/crates/syntax/src/ast.rs
+++ b/crates/syntax/src/ast.rs
@@ -25,9 +25,9 @@ pub use self::{
expr_ext::{ArrayExprKind, BlockModifier, CallableExpr, ElseBranch, LiteralKind},
generated::{nodes::*, tokens::*},
node_ext::{
- AttrKind, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind, SelfParamKind,
- SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind, TypeOrConstParam,
- VisibilityKind,
+ AttrKind, CfgAtomKey, FieldKind, Macro, NameLike, NameOrNameRef, PathSegmentKind,
+ SelfParamKind, SlicePatComponents, StructKind, TokenTreeChildren, TypeBoundKind,
+ TypeOrConstParam, VisibilityKind,
},
operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp},
token_ext::{
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index b706d7f722..b20aa90d06 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -1,12 +1,15 @@
//! This module contains functions for editing syntax trees. As the trees are
//! 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 crate::{
- AstToken, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
- ast::{self, AstNode, make},
- syntax_editor::{SyntaxEditor, SyntaxMappingBuilder},
+ AstToken, NodeOrToken, SyntaxElement,
+ SyntaxKind::WHITESPACE,
+ SyntaxNode, SyntaxToken,
+ ast::{self, AstNode, HasName, make},
+ syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder},
ted,
};
@@ -105,8 +108,7 @@ impl IndentLevel {
}
pub(super) fn clone_increase_indent(self, node: &SyntaxNode) -> SyntaxNode {
- let node = node.clone_subtree();
- let mut editor = SyntaxEditor::new(node.clone());
+ let (editor, node) = SyntaxEditor::new(node.clone());
let tokens = node
.preorder_with_tokens()
.filter_map(|event| match event {
@@ -140,8 +142,7 @@ impl IndentLevel {
}
pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode {
- let node = node.clone_subtree();
- let mut editor = SyntaxEditor::new(node.clone());
+ let (editor, node) = SyntaxEditor::new(node.clone());
let tokens = node
.preorder_with_tokens()
.filter_map(|event| match event {
@@ -196,6 +197,61 @@ pub trait AstNodeEdit: AstNode + Clone + Sized {
impl<N: AstNode + Clone> AstNodeEdit for N {}
+impl ast::IdentPat {
+ pub fn set_pat(&self, pat: Option<ast::Pat>, editor: &SyntaxEditor) -> ast::IdentPat {
+ let make = editor.make();
+ match pat {
+ None => {
+ if let Some(at_token) = self.at_token() {
+ // Remove `@ Pat`
+ let start = at_token.clone().into();
+ let end = self
+ .pat()
+ .map(|it| it.syntax().clone().into())
+ .unwrap_or_else(|| at_token.into());
+ editor.delete_all(start..=end);
+
+ // Remove any trailing ws
+ if let Some(last) =
+ self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
+ {
+ last.detach();
+ }
+ }
+ }
+ Some(pat) => {
+ if let Some(old_pat) = self.pat() {
+ // Replace existing pattern
+ editor.replace(old_pat.syntax(), pat.syntax())
+ } else if let Some(at_token) = self.at_token() {
+ // Have an `@` token but not a pattern yet
+ editor.insert(Position::after(at_token), pat.syntax());
+ } else {
+ // Don't have an `@`, should have a name
+ let name = self.name().unwrap();
+ let elements = vec![
+ make.whitespace(" ").into(),
+ make.token(T![@]).into(),
+ make.whitespace(" ").into(),
+ pat.syntax().clone().into(),
+ ];
+
+ if self.syntax().parent().is_none() {
+ let (local, local_self) = SyntaxEditor::with_ast_node(self);
+ let local_name = local_self.name().unwrap();
+ local.insert_all(Position::after(local_name.syntax()), elements);
+ let edit = local.finish();
+ return ast::IdentPat::cast(edit.new_root().clone()).unwrap();
+ } else {
+ editor.insert_all(Position::after(name.syntax()), elements);
+ }
+ }
+ }
+ }
+ self.clone()
+ }
+}
+
#[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 7f59ae4213..46ea4daba8 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -9,221 +9,12 @@ use crate::{
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
SyntaxNode, SyntaxToken,
algo::{self, neighbor},
- ast::{self, HasGenericParams, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
- syntax_editor::{Position, SyntaxEditor},
+ ast::{self, edit::IndentLevel, make},
ted,
};
use super::{GenericParam, HasName};
-pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
- fn get_or_create_where_clause(&self) -> ast::WhereClause;
-}
-
-impl GenericParamsOwnerEdit for ast::Fn {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = if let Some(name) = self.name() {
- ted::Position::after(name.syntax)
- } else if let Some(fn_token) = self.fn_token() {
- ted::Position::after(fn_token)
- } else if let Some(param_list) = self.param_list() {
- ted::Position::before(param_list.syntax)
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let position = if let Some(ty) = self.ret_type() {
- ted::Position::after(ty.syntax())
- } else if let Some(param_list) = self.param_list() {
- ted::Position::after(param_list.syntax())
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-impl GenericParamsOwnerEdit for ast::Impl {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = match self.impl_token() {
- Some(imp_token) => ted::Position::after(imp_token),
- None => ted::Position::last_child_of(self.syntax()),
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let position = match self.assoc_item_list() {
- Some(items) => ted::Position::before(items.syntax()),
- None => ted::Position::last_child_of(self.syntax()),
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-impl GenericParamsOwnerEdit for ast::Trait {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = if let Some(name) = self.name() {
- ted::Position::after(name.syntax)
- } else if let Some(trait_token) = self.trait_token() {
- ted::Position::after(trait_token)
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let position = match (self.assoc_item_list(), self.semicolon_token()) {
- (Some(items), _) => ted::Position::before(items.syntax()),
- (_, Some(tok)) => ted::Position::before(tok),
- (None, None) => ted::Position::last_child_of(self.syntax()),
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-impl GenericParamsOwnerEdit for ast::TypeAlias {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = if let Some(name) = self.name() {
- ted::Position::after(name.syntax)
- } else if let Some(trait_token) = self.type_token() {
- ted::Position::after(trait_token)
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let position = match self.eq_token() {
- Some(tok) => ted::Position::before(tok),
- None => match self.semicolon_token() {
- Some(tok) => ted::Position::before(tok),
- None => ted::Position::last_child_of(self.syntax()),
- },
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-impl GenericParamsOwnerEdit for ast::Struct {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = if let Some(name) = self.name() {
- ted::Position::after(name.syntax)
- } else if let Some(struct_token) = self.struct_token() {
- ted::Position::after(struct_token)
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let tfl = self.field_list().and_then(|fl| match fl {
- ast::FieldList::RecordFieldList(_) => None,
- ast::FieldList::TupleFieldList(it) => Some(it),
- });
- let position = if let Some(tfl) = tfl {
- ted::Position::after(tfl.syntax())
- } else if let Some(gpl) = self.generic_param_list() {
- ted::Position::after(gpl.syntax())
- } else if let Some(name) = self.name() {
- ted::Position::after(name.syntax())
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-impl GenericParamsOwnerEdit for ast::Enum {
- fn get_or_create_generic_param_list(&self) -> ast::GenericParamList {
- match self.generic_param_list() {
- Some(it) => it,
- None => {
- let position = if let Some(name) = self.name() {
- ted::Position::after(name.syntax)
- } else if let Some(enum_token) = self.enum_token() {
- ted::Position::after(enum_token)
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_generic_param_list(position)
- }
- }
- }
-
- fn get_or_create_where_clause(&self) -> ast::WhereClause {
- if self.where_clause().is_none() {
- let position = if let Some(gpl) = self.generic_param_list() {
- ted::Position::after(gpl.syntax())
- } else if let Some(name) = self.name() {
- ted::Position::after(name.syntax())
- } else {
- ted::Position::last_child_of(self.syntax())
- };
- create_where_clause(position);
- }
- self.where_clause().unwrap()
- }
-}
-
-fn create_where_clause(position: ted::Position) {
- let where_clause = make::where_clause(empty()).clone_for_update();
- ted::insert(position, where_clause.syntax());
-}
-
-fn create_generic_param_list(position: ted::Position) -> ast::GenericParamList {
- let gpl = make::generic_param_list(empty()).clone_for_update();
- ted::insert_raw(position, gpl.syntax());
- gpl
-}
-
pub trait AttrsOwnerEdit: ast::HasAttrs {
fn remove_attrs_and_docs(&self) {
remove_attrs_and_docs(self.syntax());
@@ -723,10 +514,11 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
let indent = IndentLevel::from_node(node);
match l.next_sibling_or_token() {
- Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
- if ws.next_sibling_or_token()?.into_token()? == r {
- ted::replace(ws, make::tokens::whitespace(&format!("\n{indent}")));
- }
+ 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}")));
@@ -736,128 +528,6 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
Some(())
}
-impl ast::IdentPat {
- pub fn set_pat(&self, pat: Option<ast::Pat>) {
- match pat {
- None => {
- if let Some(at_token) = self.at_token() {
- // Remove `@ Pat`
- let start = at_token.clone().into();
- let end = self
- .pat()
- .map(|it| it.syntax().clone().into())
- .unwrap_or_else(|| at_token.into());
-
- ted::remove_all(start..=end);
-
- // Remove any trailing ws
- if let Some(last) =
- self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
- {
- last.detach();
- }
- }
- }
- Some(pat) => {
- if let Some(old_pat) = self.pat() {
- // Replace existing pattern
- ted::replace(old_pat.syntax(), pat.syntax())
- } else if let Some(at_token) = self.at_token() {
- // Have an `@` token but not a pattern yet
- ted::insert(ted::Position::after(at_token), pat.syntax());
- } else {
- // Don't have an `@`, should have a name
- let name = self.name().unwrap();
-
- ted::insert_all(
- ted::Position::after(name.syntax()),
- vec![
- make::token(T![@]).into(),
- make::tokens::single_space().into(),
- pat.syntax().clone().into(),
- ],
- )
- }
- }
- }
- }
-
- pub fn set_pat_with_editor(
- &self,
- pat: Option<ast::Pat>,
- syntax_editor: &mut SyntaxEditor,
- syntax_factory: &SyntaxFactory,
- ) {
- match pat {
- None => {
- if let Some(at_token) = self.at_token() {
- // Remove `@ Pat`
- let start = at_token.clone().into();
- let end = self
- .pat()
- .map(|it| it.syntax().clone().into())
- .unwrap_or_else(|| at_token.into());
- syntax_editor.delete_all(start..=end);
-
- // Remove any trailing ws
- if let Some(last) =
- self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
- {
- last.detach();
- }
- }
- }
- Some(pat) => {
- if let Some(old_pat) = self.pat() {
- // Replace existing pattern
- syntax_editor.replace(old_pat.syntax(), pat.syntax())
- } else if let Some(at_token) = self.at_token() {
- // Have an `@` token but not a pattern yet
- syntax_editor.insert(Position::after(at_token), pat.syntax());
- } else {
- // Don't have an `@`, should have a name
- let name = self.name().unwrap();
-
- syntax_editor.insert_all(
- Position::after(name.syntax()),
- vec![
- syntax_factory.whitespace(" ").into(),
- syntax_factory.token(T![@]).into(),
- syntax_factory.whitespace(" ").into(),
- pat.syntax().clone().into(),
- ],
- )
- }
- }
- }
- }
-}
-
-pub trait HasVisibilityEdit: ast::HasVisibility {
- fn set_visibility(&self, visibility: Option<ast::Visibility>) {
- if let Some(visibility) = visibility {
- match self.visibility() {
- Some(current_visibility) => {
- ted::replace(current_visibility.syntax(), visibility.syntax())
- }
- None => {
- let vis_before = self
- .syntax()
- .children_with_tokens()
- .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR))
- .unwrap_or_else(|| self.syntax().first_child_or_token().unwrap());
-
- ted::insert(ted::Position::before(vis_before), visibility.syntax());
- }
- }
- } else if let Some(visibility) = self.visibility() {
- ted::remove(visibility.syntax());
- }
- }
-}
-
-impl<T: ast::HasVisibility> HasVisibilityEdit for T {}
-
pub trait Indent: AstNode + Clone + Sized {
fn indent_level(&self) -> IndentLevel {
IndentLevel::from_node(self.syntax())
@@ -879,8 +549,6 @@ impl<N: AstNode + Clone> Indent for N {}
#[cfg(test)]
mod tests {
- use std::fmt;
-
use parser::Edition;
use crate::SourceFile;
@@ -893,33 +561,6 @@ mod tests {
}
#[test]
- fn test_create_generic_param_list() {
- fn check_create_gpl<N: GenericParamsOwnerEdit + fmt::Display>(before: &str, after: &str) {
- let gpl_owner = ast_mut_from_text::<N>(before);
- gpl_owner.get_or_create_generic_param_list();
- assert_eq!(gpl_owner.to_string(), after);
- }
-
- check_create_gpl::<ast::Fn>("fn foo", "fn foo<>");
- check_create_gpl::<ast::Fn>("fn foo() {}", "fn foo<>() {}");
-
- check_create_gpl::<ast::Impl>("impl", "impl<>");
- check_create_gpl::<ast::Impl>("impl Struct {}", "impl<> Struct {}");
- check_create_gpl::<ast::Impl>("impl Trait for Struct {}", "impl<> Trait for Struct {}");
-
- check_create_gpl::<ast::Trait>("trait Trait<>", "trait Trait<>");
- check_create_gpl::<ast::Trait>("trait Trait<> {}", "trait Trait<> {}");
-
- check_create_gpl::<ast::Struct>("struct A", "struct A<>");
- check_create_gpl::<ast::Struct>("struct A;", "struct A<>;");
- check_create_gpl::<ast::Struct>("struct A();", "struct A<>();");
- check_create_gpl::<ast::Struct>("struct A {}", "struct A<> {}");
-
- check_create_gpl::<ast::Enum>("enum E", "enum E<>");
- check_create_gpl::<ast::Enum>("enum E {", "enum E<> {");
- }
-
- #[test]
fn test_increase_indent() {
let arm_list = ast_mut_from_text::<ast::Fn>(
"fn foo() {
@@ -936,32 +577,4 @@ mod tests {
}",
);
}
-
- #[test]
- fn test_ident_pat_set_pat() {
- #[track_caller]
- fn check(before: &str, expected: &str, pat: Option<ast::Pat>) {
- let pat = pat.map(|it| it.clone_for_update());
-
- let ident_pat = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {before} }}"));
- ident_pat.set_pat(pat);
-
- let after = ast_mut_from_text::<ast::IdentPat>(&format!("fn f() {{ {expected} }}"));
- assert_eq!(ident_pat.to_string(), after.to_string());
- }
-
- // replacing
- check("let a @ _;", "let a @ ();", Some(make::tuple_pat([]).into()));
-
- // note: no trailing semicolon is added for the below tests since it
- // seems to be picked up by the ident pat during error recovery?
-
- // adding
- check("let a ", "let a @ ()", Some(make::tuple_pat([]).into()));
- check("let a @ ", "let a @ ()", Some(make::tuple_pat([]).into()));
-
- // removing
- check("let a @ ()", "let a", None);
- check("let a @ ", "let a", None);
- }
}
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 7334de0fd9..9a2bba9ebf 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -377,6 +377,68 @@ impl CastExpr {
#[inline]
pub fn as_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![as]) }
}
+pub struct CfgAtom {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgAtom {
+ #[inline]
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ #[inline]
+ pub fn false_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![false]) }
+ #[inline]
+ pub fn ident_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+ #[inline]
+ pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
+ #[inline]
+ pub fn true_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![true]) }
+}
+pub struct CfgAttrMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgAttrMeta {
+ #[inline]
+ pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) }
+ #[inline]
+ pub fn metas(&self) -> AstChildren<Meta> { support::children(&self.syntax) }
+ #[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 comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
+ pub fn cfg_attr_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![cfg_attr])
+ }
+}
+pub struct CfgComposite {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgComposite {
+ #[inline]
+ pub fn cfg_predicates(&self) -> AstChildren<CfgPredicate> { support::children(&self.syntax) }
+ #[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 keyword(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![ident]) }
+}
+pub struct CfgMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl CfgMeta {
+ #[inline]
+ pub fn cfg_predicate(&self) -> Option<CfgPredicate> { support::child(&self.syntax) }
+ #[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 comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ #[inline]
+ pub fn cfg_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![cfg]) }
+}
pub struct ClosureExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -422,6 +484,8 @@ impl Const {
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
#[inline]
pub fn default_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![default]) }
+ #[inline]
+ pub fn type_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![type]) }
}
pub struct ConstArg {
pub(crate) syntax: SyntaxNode,
@@ -746,6 +810,15 @@ impl Impl {
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
+pub struct ImplRestriction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ImplRestriction {
+ #[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn impl_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![impl]) }
+}
pub struct ImplTraitType {
pub(crate) syntax: SyntaxNode,
}
@@ -783,6 +856,17 @@ impl ItemList {
#[inline]
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
}
+pub struct KeyValueMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl KeyValueMeta {
+ #[inline]
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+ #[inline]
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+}
pub struct Label {
pub(crate) syntax: SyntaxNode,
}
@@ -1012,25 +1096,6 @@ impl MatchGuard {
#[inline]
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
}
-pub struct Meta {
- pub(crate) syntax: SyntaxNode,
-}
-impl Meta {
- #[inline]
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
- #[inline]
- pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
- #[inline]
- pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
- #[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 eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- #[inline]
- pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
-}
pub struct MethodCallExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -1060,6 +1125,15 @@ impl Module {
#[inline]
pub fn mod_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mod]) }
}
+pub struct MutRestriction {
+ pub(crate) syntax: SyntaxNode,
+}
+impl MutRestriction {
+ #[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
+}
pub struct Name {
pub(crate) syntax: SyntaxNode,
}
@@ -1225,6 +1299,13 @@ impl PathExpr {
#[inline]
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
}
+pub struct PathMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl PathMeta {
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+}
pub struct PathPat {
pub(crate) syntax: SyntaxNode,
}
@@ -1339,6 +1420,8 @@ impl RecordField {
#[inline]
pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
#[inline]
+ pub fn mut_restriction(&self) -> Option<MutRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
#[inline]
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
@@ -1607,6 +1690,15 @@ impl TokenTree {
#[inline]
pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) }
}
+pub struct TokenTreeMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl TokenTreeMeta {
+ #[inline]
+ pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
+ #[inline]
+ pub fn token_tree(&self) -> Option<TokenTree> { support::child(&self.syntax) }
+}
pub struct Trait {
pub(crate) syntax: SyntaxNode,
}
@@ -1620,6 +1712,8 @@ impl Trait {
#[inline]
pub fn assoc_item_list(&self) -> Option<AssocItemList> { support::child(&self.syntax) }
#[inline]
+ pub fn impl_restriction(&self) -> Option<ImplRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
#[inline]
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
@@ -1673,6 +1767,8 @@ impl ast::HasDocComments for TupleField {}
impl ast::HasVisibility for TupleField {}
impl TupleField {
#[inline]
+ pub fn mut_restriction(&self) -> Option<MutRestriction> { support::child(&self.syntax) }
+ #[inline]
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
}
pub struct TupleFieldList {
@@ -1834,6 +1930,19 @@ impl Union {
#[inline]
pub fn union_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![union]) }
}
+pub struct UnsafeMeta {
+ pub(crate) syntax: SyntaxNode,
+}
+impl UnsafeMeta {
+ #[inline]
+ pub fn meta(&self) -> Option<Meta> { support::child(&self.syntax) }
+ #[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 unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
+}
pub struct Use {
pub(crate) syntax: SyntaxNode,
}
@@ -1918,6 +2027,15 @@ pub struct Visibility {
}
impl Visibility {
#[inline]
+ pub fn visibility_inner(&self) -> Option<VisibilityInner> { support::child(&self.syntax) }
+ #[inline]
+ pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) }
+}
+pub struct VisibilityInner {
+ pub(crate) syntax: SyntaxNode,
+}
+impl VisibilityInner {
+ #[inline]
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
#[inline]
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
@@ -1925,8 +2043,6 @@ impl Visibility {
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
- #[inline]
- pub fn pub_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pub]) }
}
pub struct WhereClause {
pub(crate) syntax: SyntaxNode,
@@ -2025,6 +2141,12 @@ impl ast::HasAttrs for AssocItem {}
impl ast::HasDocComments for AssocItem {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum CfgPredicate {
+ CfgAtom(CfgAtom),
+ CfgComposite(CfgComposite),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
ArrayExpr(ArrayExpr),
AsmExpr(AsmExpr),
@@ -2119,6 +2241,16 @@ pub enum Item {
impl ast::HasAttrs for Item {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum Meta {
+ CfgAttrMeta(CfgAttrMeta),
+ CfgMeta(CfgMeta),
+ KeyValueMeta(KeyValueMeta),
+ PathMeta(PathMeta),
+ TokenTreeMeta(TokenTreeMeta),
+ UnsafeMeta(UnsafeMeta),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pat {
BoxPat(BoxPat),
ConstBlockPat(ConstBlockPat),
@@ -3133,6 +3265,134 @@ impl fmt::Debug for CastExpr {
f.debug_struct("CastExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for CfgAtom {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_ATOM
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATOM }
+ #[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 CfgAtom {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgAtom {}
+impl PartialEq for CfgAtom {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgAtom {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgAtom {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgAtom").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgAttrMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_ATTR_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_ATTR_META }
+ #[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 CfgAttrMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgAttrMeta {}
+impl PartialEq for CfgAttrMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgAttrMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgAttrMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgAttrMeta").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgComposite {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_COMPOSITE
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_COMPOSITE }
+ #[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 CfgComposite {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgComposite {}
+impl PartialEq for CfgComposite {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgComposite {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgComposite {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgComposite").field("syntax", &self.syntax).finish()
+ }
+}
+impl AstNode for CfgMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ CFG_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == CFG_META }
+ #[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 CfgMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for CfgMeta {}
+impl PartialEq for CfgMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for CfgMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for CfgMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CfgMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for ClosureExpr {
#[inline]
fn kind() -> SyntaxKind
@@ -3965,6 +4225,38 @@ impl fmt::Debug for Impl {
f.debug_struct("Impl").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for ImplRestriction {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ IMPL_RESTRICTION
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == IMPL_RESTRICTION }
+ #[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 ImplRestriction {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for ImplRestriction {}
+impl PartialEq for ImplRestriction {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for ImplRestriction {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for ImplRestriction {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ImplRestriction").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for ImplTraitType {
#[inline]
fn kind() -> SyntaxKind
@@ -4093,6 +4385,38 @@ impl fmt::Debug for ItemList {
f.debug_struct("ItemList").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for KeyValueMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ KEY_VALUE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == KEY_VALUE_META }
+ #[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 KeyValueMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for KeyValueMeta {}
+impl PartialEq for KeyValueMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for KeyValueMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for KeyValueMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("KeyValueMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Label {
#[inline]
fn kind() -> SyntaxKind
@@ -4797,16 +5121,16 @@ impl fmt::Debug for MatchGuard {
f.debug_struct("MatchGuard").field("syntax", &self.syntax).finish()
}
}
-impl AstNode for Meta {
+impl AstNode for MethodCallExpr {
#[inline]
fn kind() -> SyntaxKind
where
Self: Sized,
{
- META
+ METHOD_CALL_EXPR
}
#[inline]
- fn can_cast(kind: SyntaxKind) -> bool { kind == META }
+ fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
@@ -4814,31 +5138,31 @@ impl AstNode for Meta {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl hash::Hash for Meta {
+impl hash::Hash for MethodCallExpr {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
}
-impl Eq for Meta {}
-impl PartialEq for Meta {
+impl Eq for MethodCallExpr {}
+impl PartialEq for MethodCallExpr {
fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
}
-impl Clone for Meta {
+impl Clone for MethodCallExpr {
fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
}
-impl fmt::Debug for Meta {
+impl fmt::Debug for MethodCallExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Meta").field("syntax", &self.syntax).finish()
+ f.debug_struct("MethodCallExpr").field("syntax", &self.syntax).finish()
}
}
-impl AstNode for MethodCallExpr {
+impl AstNode for Module {
#[inline]
fn kind() -> SyntaxKind
where
Self: Sized,
{
- METHOD_CALL_EXPR
+ MODULE
}
#[inline]
- fn can_cast(kind: SyntaxKind) -> bool { kind == METHOD_CALL_EXPR }
+ fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
@@ -4846,31 +5170,31 @@ impl AstNode for MethodCallExpr {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl hash::Hash for MethodCallExpr {
+impl hash::Hash for Module {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
}
-impl Eq for MethodCallExpr {}
-impl PartialEq for MethodCallExpr {
+impl Eq for Module {}
+impl PartialEq for Module {
fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
}
-impl Clone for MethodCallExpr {
+impl Clone for Module {
fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
}
-impl fmt::Debug for MethodCallExpr {
+impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("MethodCallExpr").field("syntax", &self.syntax).finish()
+ f.debug_struct("Module").field("syntax", &self.syntax).finish()
}
}
-impl AstNode for Module {
+impl AstNode for MutRestriction {
#[inline]
fn kind() -> SyntaxKind
where
Self: Sized,
{
- MODULE
+ MUT_RESTRICTION
}
#[inline]
- fn can_cast(kind: SyntaxKind) -> bool { kind == MODULE }
+ fn can_cast(kind: SyntaxKind) -> bool { kind == MUT_RESTRICTION }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
@@ -4878,19 +5202,19 @@ impl AstNode for Module {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl hash::Hash for Module {
+impl hash::Hash for MutRestriction {
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
}
-impl Eq for Module {}
-impl PartialEq for Module {
+impl Eq for MutRestriction {}
+impl PartialEq for MutRestriction {
fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
}
-impl Clone for Module {
+impl Clone for MutRestriction {
fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
}
-impl fmt::Debug for Module {
+impl fmt::Debug for MutRestriction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Module").field("syntax", &self.syntax).finish()
+ f.debug_struct("MutRestriction").field("syntax", &self.syntax).finish()
}
}
impl AstNode for Name {
@@ -5309,6 +5633,38 @@ impl fmt::Debug for PathExpr {
f.debug_struct("PathExpr").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for PathMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ PATH_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == PATH_META }
+ #[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 PathMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for PathMeta {}
+impl PartialEq for PathMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for PathMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for PathMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PathMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for PathPat {
#[inline]
fn kind() -> SyntaxKind
@@ -6301,6 +6657,38 @@ impl fmt::Debug for TokenTree {
f.debug_struct("TokenTree").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for TokenTreeMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ TOKEN_TREE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == TOKEN_TREE_META }
+ #[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 TokenTreeMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for TokenTreeMeta {}
+impl PartialEq for TokenTreeMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for TokenTreeMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for TokenTreeMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TokenTreeMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Trait {
#[inline]
fn kind() -> SyntaxKind
@@ -6845,6 +7233,38 @@ impl fmt::Debug for Union {
f.debug_struct("Union").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for UnsafeMeta {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ UNSAFE_META
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == UNSAFE_META }
+ #[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 UnsafeMeta {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for UnsafeMeta {}
+impl PartialEq for UnsafeMeta {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for UnsafeMeta {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for UnsafeMeta {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnsafeMeta").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for Use {
#[inline]
fn kind() -> SyntaxKind
@@ -7069,6 +7489,38 @@ impl fmt::Debug for Visibility {
f.debug_struct("Visibility").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for VisibilityInner {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ VISIBILITY_INNER
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == VISIBILITY_INNER }
+ #[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 VisibilityInner {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for VisibilityInner {}
+impl PartialEq for VisibilityInner {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for VisibilityInner {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for VisibilityInner {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("VisibilityInner").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for WhereClause {
#[inline]
fn kind() -> SyntaxKind
@@ -7413,6 +7865,34 @@ impl AstNode for AssocItem {
}
}
}
+impl From<CfgAtom> for CfgPredicate {
+ #[inline]
+ fn from(node: CfgAtom) -> CfgPredicate { CfgPredicate::CfgAtom(node) }
+}
+impl From<CfgComposite> for CfgPredicate {
+ #[inline]
+ fn from(node: CfgComposite) -> CfgPredicate { CfgPredicate::CfgComposite(node) }
+}
+impl AstNode for CfgPredicate {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, CFG_ATOM | CFG_COMPOSITE) }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ CFG_ATOM => CfgPredicate::CfgAtom(CfgAtom { syntax }),
+ CFG_COMPOSITE => CfgPredicate::CfgComposite(CfgComposite { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ CfgPredicate::CfgAtom(it) => &it.syntax,
+ CfgPredicate::CfgComposite(it) => &it.syntax,
+ }
+ }
+}
impl From<ArrayExpr> for Expr {
#[inline]
fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) }
@@ -7970,6 +8450,63 @@ impl AstNode for Item {
}
}
}
+impl From<CfgAttrMeta> for Meta {
+ #[inline]
+ fn from(node: CfgAttrMeta) -> Meta { Meta::CfgAttrMeta(node) }
+}
+impl From<CfgMeta> for Meta {
+ #[inline]
+ fn from(node: CfgMeta) -> Meta { Meta::CfgMeta(node) }
+}
+impl From<KeyValueMeta> for Meta {
+ #[inline]
+ fn from(node: KeyValueMeta) -> Meta { Meta::KeyValueMeta(node) }
+}
+impl From<PathMeta> for Meta {
+ #[inline]
+ fn from(node: PathMeta) -> Meta { Meta::PathMeta(node) }
+}
+impl From<TokenTreeMeta> for Meta {
+ #[inline]
+ fn from(node: TokenTreeMeta) -> Meta { Meta::TokenTreeMeta(node) }
+}
+impl From<UnsafeMeta> for Meta {
+ #[inline]
+ fn from(node: UnsafeMeta) -> Meta { Meta::UnsafeMeta(node) }
+}
+impl AstNode for Meta {
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool {
+ matches!(
+ kind,
+ CFG_ATTR_META | CFG_META | KEY_VALUE_META | PATH_META | TOKEN_TREE_META | UNSAFE_META
+ )
+ }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ let res = match syntax.kind() {
+ CFG_ATTR_META => Meta::CfgAttrMeta(CfgAttrMeta { syntax }),
+ CFG_META => Meta::CfgMeta(CfgMeta { syntax }),
+ KEY_VALUE_META => Meta::KeyValueMeta(KeyValueMeta { syntax }),
+ PATH_META => Meta::PathMeta(PathMeta { syntax }),
+ TOKEN_TREE_META => Meta::TokenTreeMeta(TokenTreeMeta { syntax }),
+ UNSAFE_META => Meta::UnsafeMeta(UnsafeMeta { syntax }),
+ _ => return None,
+ };
+ Some(res)
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode {
+ match self {
+ Meta::CfgAttrMeta(it) => &it.syntax,
+ Meta::CfgMeta(it) => &it.syntax,
+ Meta::KeyValueMeta(it) => &it.syntax,
+ Meta::PathMeta(it) => &it.syntax,
+ Meta::TokenTreeMeta(it) => &it.syntax,
+ Meta::UnsafeMeta(it) => &it.syntax,
+ }
+ }
+}
impl From<BoxPat> for Pat {
#[inline]
fn from(node: BoxPat) -> Pat { Pat::BoxPat(node) }
@@ -9334,6 +9871,11 @@ impl std::fmt::Display for AssocItem {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for CfgPredicate {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Expr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9364,6 +9906,11 @@ impl std::fmt::Display for Item {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for Meta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Pat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9524,6 +10071,26 @@ impl std::fmt::Display for CastExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for CfgAtom {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgAttrMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgComposite {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
+impl std::fmt::Display for CfgMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ClosureExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9654,6 +10221,11 @@ impl std::fmt::Display for Impl {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for ImplRestriction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ImplTraitType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9674,6 +10246,11 @@ impl std::fmt::Display for ItemList {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for KeyValueMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -9784,17 +10361,17 @@ impl std::fmt::Display for MatchGuard {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for Meta {
+impl std::fmt::Display for MethodCallExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for MethodCallExpr {
+impl std::fmt::Display for Module {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for Module {
+impl std::fmt::Display for MutRestriction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
@@ -9864,6 +10441,11 @@ impl std::fmt::Display for PathExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for PathMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for PathPat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10019,6 +10601,11 @@ impl std::fmt::Display for TokenTree {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for TokenTreeMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Trait {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10104,6 +10691,11 @@ impl std::fmt::Display for Union {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for UnsafeMeta {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for Use {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -10139,6 +10731,11 @@ impl std::fmt::Display for Visibility {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for VisibilityInner {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for WhereClause {
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 00971569a2..ac02cc9e43 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -1322,6 +1322,18 @@ pub fn meta_path(path: ast::Path) -> ast::Meta {
ast_from_text(&format!("#[{path}]"))
}
+pub fn cfg_attr_meta(
+ predicate: ast::CfgPredicate,
+ inner: impl IntoIterator<Item = ast::Meta>,
+) -> ast::CfgAttrMeta {
+ let inner = inner.into_iter().join(", ");
+ ast_from_text(&format!("#![cfg_attr({predicate}, {inner})]"))
+}
+
+pub fn cfg_flag(flag: &str) -> ast::CfgPredicate {
+ ast_from_text(&format!("#![cfg({flag})]"))
+}
+
pub fn token_tree(
delimiter: SyntaxKind,
tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>,
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 63e4608d0f..751f8d7e1c 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -8,6 +8,7 @@ use std::{borrow::Cow, fmt, iter::successors};
use itertools::Itertools;
use parser::SyntaxKind;
use rowan::{GreenNodeData, GreenTokenData};
+use smallvec::{SmallVec, smallvec};
use crate::{
NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText,
@@ -77,6 +78,15 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
}
}
+fn into_comma(it: NodeOrToken<SyntaxNode, SyntaxToken>) -> Option<SyntaxToken> {
+ let token = match it {
+ NodeOrToken::Token(it) => it,
+ NodeOrToken::Node(node) if node.kind() == SyntaxKind::ERROR => node.first_token()?,
+ NodeOrToken::Node(_) => return None,
+ };
+ (token.kind() == T![,]).then_some(token)
+}
+
impl ast::Abi {
pub fn abi_string(&self) -> Option<ast::String> {
support::token(&self.syntax, SyntaxKind::STRING).and_then(ast::String::cast)
@@ -192,53 +202,95 @@ impl AttrKind {
}
}
-impl ast::Attr {
+impl ast::Meta {
pub fn as_simple_atom(&self) -> Option<SmolStr> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.simple_name()
+ Some(self.as_simple_path()?.as_single_name_ref()?.text().into())
}
pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
- let tt = self.meta()?.token_tree()?;
- Some((self.simple_name()?, tt))
+ let ast::Meta::TokenTreeMeta(meta) = self else { return None };
+ Some((meta.path()?.as_single_name_ref()?.text().into(), meta.token_tree()?))
}
pub fn as_simple_path(&self) -> Option<ast::Path> {
- let meta = self.meta()?;
- if meta.eq_token().is_some() || meta.token_tree().is_some() {
- return None;
- }
- self.path()
+ let ast::Meta::PathMeta(meta) = self else { return None };
+ meta.path()
}
pub fn simple_name(&self) -> Option<SmolStr> {
- let path = self.meta()?.path()?;
- match (path.segment(), path.qualifier()) {
- (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
- _ => None,
+ match self {
+ ast::Meta::CfgAttrMeta(_) => Some(SmolStr::new_static("cfg_attr")),
+ ast::Meta::CfgMeta(_) => Some(SmolStr::new_static("cfg")),
+ _ => {
+ let path = self.path()?;
+ match (path.segment(), path.qualifier()) {
+ (Some(segment), None) => Some(segment.syntax().first_token()?.text().into()),
+ _ => None,
+ }
+ }
}
}
- pub fn kind(&self) -> AttrKind {
- match self.excl_token() {
- Some(_) => AttrKind::Inner,
- None => AttrKind::Outer,
+ pub fn path(&self) -> Option<ast::Path> {
+ match self {
+ ast::Meta::CfgAttrMeta(_) | ast::Meta::CfgMeta(_) => None,
+ ast::Meta::KeyValueMeta(it) => it.path(),
+ ast::Meta::PathMeta(it) => it.path(),
+ ast::Meta::TokenTreeMeta(it) => it.path(),
+ ast::Meta::UnsafeMeta(it) => it.meta()?.path(),
+ }
+ }
+
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(self) -> SmallVec<[ast::Meta; 1]> {
+ match self {
+ ast::Meta::CfgAttrMeta(meta) => {
+ meta.metas().flat_map(|meta| meta.skip_cfg_attrs()).collect()
+ }
+ _ => smallvec![self],
}
}
+ /// FIXME: Calling this is almost always incorrect, as `cfg_attr` can contains multiple `Meta`s.
+ pub fn parent_attr(&self) -> Option<ast::Attr> {
+ self.syntax().ancestors().find_map(ast::Attr::cast)
+ }
+}
+
+impl ast::Attr {
+ pub fn as_simple_atom(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.as_simple_atom())
+ }
+
+ pub fn as_simple_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
+ self.meta().and_then(|meta| meta.as_simple_call())
+ }
+
+ pub fn as_simple_path(&self) -> Option<ast::Path> {
+ self.meta().and_then(|meta| meta.as_simple_path())
+ }
+
+ pub fn simple_name(&self) -> Option<SmolStr> {
+ self.meta().and_then(|meta| meta.simple_name())
+ }
+
pub fn path(&self) -> Option<ast::Path> {
- self.meta()?.path()
+ self.meta().and_then(|meta| meta.path())
}
- pub fn expr(&self) -> Option<ast::Expr> {
- self.meta()?.expr()
+ pub fn kind(&self) -> AttrKind {
+ match self.excl_token() {
+ Some(_) => AttrKind::Inner,
+ None => AttrKind::Outer,
+ }
}
- pub fn token_tree(&self) -> Option<ast::TokenTree> {
- self.meta()?.token_tree()
+ /// Includes `cfg_attr()` inner metas (without considering the predicate).
+ pub fn skip_cfg_attrs(&self) -> SmallVec<[ast::Meta; 1]> {
+ match self.meta() {
+ Some(meta) => meta.skip_cfg_attrs(),
+ None => SmallVec::new(),
+ }
}
}
@@ -896,6 +948,15 @@ pub enum VisibilityKind {
impl ast::Visibility {
pub fn kind(&self) -> VisibilityKind {
+ match self.visibility_inner() {
+ Some(inner) => inner.kind(),
+ None => VisibilityKind::Pub,
+ }
+ }
+}
+
+impl ast::VisibilityInner {
+ pub fn kind(&self) -> VisibilityKind {
match self.path() {
Some(path) => {
if let Some(segment) =
@@ -1006,12 +1067,6 @@ impl ast::TokenTree {
}
}
-impl ast::Meta {
- pub fn parent_attr(&self) -> Option<ast::Attr> {
- self.syntax().parent().and_then(ast::Attr::cast)
- }
-}
-
impl ast::GenericArgList {
pub fn lifetime_args(&self) -> impl Iterator<Item = ast::LifetimeArg> {
self.generic_args().filter_map(|arg| match arg {
@@ -1037,6 +1092,21 @@ impl ast::GenericParamList {
}
}
+impl ast::ArgList {
+ /// Comma separated args, argument may be empty
+ pub fn args_maybe_empty(&self) -> impl Iterator<Item = Option<ast::Expr>> {
+ // (Expr? ','?)*
+ let mut after_arg = false;
+ self.syntax().children_with_tokens().filter_map(move |it| {
+ if into_comma(it.clone()).is_some() {
+ if std::mem::take(&mut after_arg) { None } else { Some(None) }
+ } else {
+ Some(ast::Expr::cast(it.into_node()?).inspect(|_| after_arg = true))
+ }
+ })
+ }
+}
+
impl ast::ForExpr {
pub fn iterable(&self) -> Option<ast::Expr> {
// If the iterable is a BlockExpr, check if the body is missing.
@@ -1140,6 +1210,25 @@ impl ast::OrPat {
}
}
+#[derive(Debug, Clone)]
+pub enum CfgAtomKey {
+ True,
+ False,
+ Ident(SyntaxToken),
+}
+
+impl ast::CfgAtom {
+ pub fn key(&self) -> Option<CfgAtomKey> {
+ if self.true_token().is_some() {
+ Some(CfgAtomKey::True)
+ } else if self.false_token().is_some() {
+ Some(CfgAtomKey::False)
+ } else {
+ self.ident_token().map(CfgAtomKey::Ident)
+ }
+ }
+}
+
/// An iterator over the elements in an [`ast::TokenTree`].
///
/// Does not yield trivia or the delimiters.
diff --git a/crates/syntax/src/ast/syntax_factory.rs b/crates/syntax/src/ast/syntax_factory.rs
index f3ae7544cc..9369a4e700 100644
--- a/crates/syntax/src/ast/syntax_factory.rs
+++ b/crates/syntax/src/ast/syntax_factory.rs
@@ -12,6 +12,7 @@ use std::cell::{RefCell, RefMut};
use crate::syntax_editor::SyntaxMapping;
+#[derive(Debug)]
pub struct SyntaxFactory {
// Stored in a refcell so that the factory methods can be &self
mappings: Option<RefCell<SyntaxMapping>>,
@@ -19,7 +20,7 @@ pub struct SyntaxFactory {
impl SyntaxFactory {
/// Creates a new [`SyntaxFactory`], generating mappings between input nodes and generated nodes.
- pub fn with_mappings() -> Self {
+ pub(crate) fn with_mappings() -> Self {
Self { mappings: Some(RefCell::new(SyntaxMapping::default())) }
}
@@ -28,13 +29,8 @@ impl SyntaxFactory {
Self { mappings: None }
}
- /// Gets all of the tracked syntax mappings, if any.
- pub fn finish_with_mappings(self) -> SyntaxMapping {
- self.mappings.unwrap_or_default().into_inner()
- }
-
/// Take all of the tracked syntax mappings, leaving `SyntaxMapping::default()` in its place, if any.
- pub fn take(&self) -> SyntaxMapping {
+ pub(crate) fn take(&self) -> SyntaxMapping {
self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default()
}
diff --git a/crates/syntax/src/ast/syntax_factory/constructors.rs b/crates/syntax/src/ast/syntax_factory/constructors.rs
index 44114a7802..0f3b3d301c 100644
--- a/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -67,6 +67,26 @@ impl SyntaxFactory {
make::type_bound(bound).clone_for_update()
}
+ pub fn type_bound_text(&self, bound: &str) -> ast::TypeBound {
+ make::type_bound_text(bound).clone_for_update()
+ }
+
+ pub fn use_tree_list(
+ &self,
+ use_trees: impl IntoIterator<Item = ast::UseTree>,
+ ) -> ast::UseTreeList {
+ let (use_trees, input) = iterator_input(use_trees);
+ let ast = make::use_tree_list(use_trees).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_children(input, ast.use_trees().map(|b| b.syntax().clone()));
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn type_bound_list(
&self,
bounds: impl IntoIterator<Item = ast::TypeBound>,
@@ -76,7 +96,9 @@ impl SyntaxFactory {
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
- builder.map_children(input, ast.bounds().map(|b| b.syntax().clone()));
+ for (input_node, output_bound) in input.into_iter().zip(ast.bounds()) {
+ builder.map_node(input_node, output_bound.syntax().clone());
+ }
builder.finish(&mut mapping);
}
@@ -171,6 +193,18 @@ impl SyntaxFactory {
ast
}
+ pub fn untyped_param(&self, pat: ast::Pat) -> ast::Param {
+ let ast = make::untyped_param(pat.clone()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(pat.syntax().clone(), ast.pat().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
&self,
is_unsafe: bool,
@@ -189,7 +223,7 @@ impl SyntaxFactory {
}
builder.map_children(
params_input,
- ast.param_list().unwrap().params().map(|p| p.syntax().clone()),
+ ast.syntax().children().filter(|c| ast::Param::can_cast(c.kind())),
);
if let Some(ret_type) = ret_type {
builder
@@ -222,13 +256,17 @@ impl SyntaxFactory {
builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone());
}
}
+ builder.finish(&mut mapping);
+
if let Some(type_bound_list) = ast.type_bound_list() {
- builder.map_children(
+ let mut bounds_builder =
+ SyntaxMappingBuilder::new(type_bound_list.syntax().clone());
+ bounds_builder.map_children(
bounds_input,
type_bound_list.bounds().map(|b| b.syntax().clone()),
);
+ bounds_builder.finish(&mut mapping);
}
- builder.finish(&mut mapping);
}
ast
@@ -464,11 +502,13 @@ impl SyntaxFactory {
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(name_ref.syntax().clone(), ast.name_ref().unwrap().syntax().clone());
- builder.map_children(
- input,
- ast.generic_arg_list().unwrap().generic_args().map(|a| a.syntax().clone()),
- );
builder.finish(&mut mapping);
+
+ let generic_arg_list = ast.generic_arg_list().unwrap();
+ let mut arg_builder = SyntaxMappingBuilder::new(generic_arg_list.syntax().clone());
+ arg_builder
+ .map_children(input, generic_arg_list.generic_args().map(|a| a.syntax().clone()));
+ arg_builder.finish(&mut mapping);
}
ast
@@ -609,9 +649,16 @@ impl SyntaxFactory {
let ast = make::path_from_segments(segments, is_abs).clone_for_update();
if let Some(mut mapping) = self.mappings() {
- let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
- builder.map_children(input, ast.segments().map(|it| it.syntax().clone()));
- builder.finish(&mut mapping);
+ let mut current_path = Some(ast.clone());
+ for input_segment in input.iter().rev() {
+ let Some(path) = current_path else { break };
+ if let Some(segment) = path.segment() {
+ let mut builder = SyntaxMappingBuilder::new(path.syntax().clone());
+ builder.map_node(input_segment.clone(), segment.syntax().clone());
+ builder.finish(&mut mapping);
+ }
+ current_path = path.qualifier();
+ }
}
ast
@@ -1053,13 +1100,15 @@ impl SyntaxFactory {
let ast = make::expr_closure(args, expr.clone()).clone_for_update();
if let Some(mut mapping) = self.mappings() {
- let mut builder = SyntaxMappingBuilder::new(ast.syntax.clone());
- builder.map_children(
- input,
- ast.param_list().unwrap().params().map(|param| param.syntax().clone()),
- );
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(expr.syntax().clone(), ast.body().unwrap().syntax().clone());
builder.finish(&mut mapping);
+
+ let param_list = ast.param_list().unwrap();
+ let mut params_builder = SyntaxMappingBuilder::new(param_list.syntax().clone());
+ params_builder
+ .map_children(input, param_list.params().map(|param| param.syntax().clone()));
+ params_builder.finish(&mut mapping);
}
ast
@@ -1848,7 +1897,36 @@ impl SyntaxFactory {
if let Some(mut mapping) = self.mappings() {
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
- builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone());
+ let ast::Meta::TokenTreeMeta(meta) = &ast else { unreachable!() };
+ builder.map_node(tt.syntax().clone(), meta.token_tree().unwrap().syntax().clone());
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
+ pub fn cfg_flag(&self, flag: &str) -> ast::CfgPredicate {
+ make::cfg_flag(flag).clone_for_update()
+ }
+
+ pub fn cfg_attr_meta(
+ &self,
+ predicate: ast::CfgPredicate,
+ inner: impl IntoIterator<Item = ast::Meta>,
+ ) -> ast::CfgAttrMeta {
+ let inner = Vec::from_iter(inner);
+ let ast = make::cfg_attr_meta(predicate.clone(), inner.iter().cloned()).clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ builder.map_node(
+ predicate.syntax().clone(),
+ ast.cfg_predicate().unwrap().syntax().clone(),
+ );
+ builder.map_children(
+ inner.iter().map(|it| it.syntax().clone()),
+ ast.metas().map(|it| it.syntax().clone()),
+ );
builder.finish(&mut mapping);
}
@@ -1960,6 +2038,47 @@ impl SyntaxFactory {
ast
}
+ pub fn trait_(
+ &self,
+ is_unsafe: bool,
+ ident: &str,
+ generic_param_list: Option<ast::GenericParamList>,
+ where_clause: Option<ast::WhereClause>,
+ assoc_items: ast::AssocItemList,
+ ) -> ast::Trait {
+ let ast = make::trait_(
+ is_unsafe,
+ ident,
+ generic_param_list.clone(),
+ where_clause.clone(),
+ assoc_items.clone(),
+ )
+ .clone_for_update();
+
+ if let Some(mut mapping) = self.mappings() {
+ let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+ if let Some(generic_param_list) = generic_param_list {
+ builder.map_node(
+ generic_param_list.syntax().clone(),
+ ast.generic_param_list().unwrap().syntax().clone(),
+ );
+ }
+ if let Some(where_clause) = where_clause {
+ builder.map_node(
+ where_clause.syntax().clone(),
+ ast.where_clause().unwrap().syntax().clone(),
+ );
+ }
+ builder.map_node(
+ assoc_items.syntax().clone(),
+ ast.assoc_item_list().unwrap().syntax().clone(),
+ );
+ builder.finish(&mut mapping);
+ }
+
+ ast
+ }
+
pub fn ret_type(&self, ty: ast::Type) -> ast::RetType {
let ast = make::ret_type(ty.clone()).clone_for_update();
@@ -2053,6 +2172,17 @@ impl SyntaxFactory {
self.path_unqualified(self.path_segment(self.name_ref(ident)))
}
+ pub fn path_from_idents<'a>(
+ &self,
+ parts: impl IntoIterator<Item = &'a str>,
+ ) -> Option<ast::Path> {
+ make::ext::path_from_idents(parts).map(|path| path.clone_for_update())
+ }
+
+ pub fn token_tree_from_node(&self, node: &SyntaxNode) -> ast::TokenTree {
+ make::ext::token_tree_from_node(node).clone_for_update()
+ }
+
pub fn expr_unit(&self) -> ast::Expr {
self.expr_tuple([]).into()
}
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index 83ab87c1c6..29b3b0930a 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -32,11 +32,7 @@ impl ast::Comment {
}
pub fn prefix(&self) -> &'static str {
- let &(prefix, _kind) = CommentKind::BY_PREFIX
- .iter()
- .find(|&(prefix, kind)| self.kind() == *kind && self.text().starts_with(prefix))
- .unwrap();
- prefix
+ self.kind().prefix()
}
/// Returns the textual content of a doc comment node as a single string with prefix and suffix
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs
index 2f4109a2c9..6fe5abb84e 100644
--- a/crates/syntax/src/ast/traits.rs
+++ b/crates/syntax/src/ast/traits.rs
@@ -73,9 +73,6 @@ pub trait HasAttrs: AstNode {
fn attrs(&self) -> AstChildren<ast::Attr> {
support::children(self.syntax())
}
- fn has_atom_attr(&self, atom: &str) -> bool {
- self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom)
- }
/// This may return the same node as called with (with `SourceFile`). The caller has the responsibility
/// to avoid duplicate attributes.
diff --git a/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs
index e6937e4d0f..edd063ffd4 100644
--- a/crates/syntax/src/syntax_editor.rs
+++ b/crates/syntax/src/syntax_editor.rs
@@ -5,6 +5,7 @@
//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
use std::{
+ cell::RefCell,
fmt, iter,
num::NonZeroU32,
ops::RangeInclusive,
@@ -14,7 +15,10 @@ use std::{
use rowan::TextRange;
use rustc_hash::FxHashMap;
-use crate::{SyntaxElement, SyntaxNode, SyntaxToken};
+use crate::{
+ AstNode, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
+ ast::{self, edit::IndentLevel, syntax_factory::SyntaxFactory},
+};
mod edit_algo;
mod edits;
@@ -26,31 +30,60 @@ pub use mapping::{SyntaxMapping, SyntaxMappingBuilder};
#[derive(Debug)]
pub struct SyntaxEditor {
root: SyntaxNode,
- changes: Vec<Change>,
- mappings: SyntaxMapping,
- annotations: Vec<(SyntaxElement, SyntaxAnnotation)>,
+ changes: RefCell<Vec<Change>>,
+ annotations: RefCell<Vec<(SyntaxElement, SyntaxAnnotation)>>,
+ make: SyntaxFactory,
}
impl SyntaxEditor {
- /// Creates a syntax editor to start editing from `root`
- pub fn new(root: SyntaxNode) -> Self {
- Self { root, changes: vec![], mappings: SyntaxMapping::default(), annotations: vec![] }
+ /// Creates a syntax editor from `root`.
+ ///
+ /// The returned `root` is guaranteed to be a detached, immutable node.
+ /// If the provided node is not a root (i.e., has a parent) or is already
+ /// mutable, it is cloned into a fresh subtree to satisfy syntax editor
+ /// invariants.
+ pub fn new(root: SyntaxNode) -> (Self, SyntaxNode) {
+ let mut root = root;
+
+ if root.parent().is_some() || root.is_mutable() {
+ root = root.clone_subtree()
+ };
+
+ let editor = Self {
+ root: root.clone(),
+ changes: RefCell::new(Vec::new()),
+ annotations: RefCell::new(Vec::new()),
+ make: SyntaxFactory::with_mappings(),
+ };
+
+ (editor, root)
}
- pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) {
- self.annotations.push((element.syntax_element(), annotation))
+ /// Typed-node variant of [`SyntaxEditor::new`].
+ pub fn with_ast_node<T>(root: &T) -> (Self, T)
+ where
+ T: AstNode,
+ {
+ let (editor, root) = Self::new(root.syntax().clone());
+
+ (editor, T::cast(root).unwrap())
}
- pub fn add_annotation_all(
- &mut self,
- elements: Vec<impl Element>,
- annotation: SyntaxAnnotation,
- ) {
+ pub fn make(&self) -> &SyntaxFactory {
+ &self.make
+ }
+
+ pub fn add_annotation(&self, element: impl Element, annotation: SyntaxAnnotation) {
+ self.annotations.borrow_mut().push((element.syntax_element(), annotation))
+ }
+
+ pub fn add_annotation_all(&self, elements: Vec<impl Element>, annotation: SyntaxAnnotation) {
self.annotations
+ .borrow_mut()
.extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation)));
}
- pub fn merge(&mut self, mut other: SyntaxEditor) {
+ pub fn merge(&self, other: SyntaxEditor) {
debug_assert!(
self.root == other.root || other.root.ancestors().any(|node| node == self.root),
"{:?} is not in the same tree as {:?}",
@@ -58,74 +91,92 @@ impl SyntaxEditor {
self.root
);
- self.changes.append(&mut other.changes);
- self.mappings.merge(other.mappings);
- self.annotations.append(&mut other.annotations);
+ self.changes.borrow_mut().append(&mut other.changes.into_inner());
+ if let Some(mut m) = self.make.mappings() {
+ m.merge(other.make.take());
+ }
+ self.annotations.borrow_mut().append(&mut other.annotations.into_inner());
}
- pub fn insert(&mut self, position: Position, element: impl Element) {
+ pub fn insert(&self, position: Position, element: impl Element) {
debug_assert!(is_ancestor_or_self(&position.parent(), &self.root));
- self.changes.push(Change::Insert(position, element.syntax_element()))
+ self.changes.borrow_mut().push(Change::Insert(position, element.syntax_element()))
}
- pub fn insert_all(&mut self, position: Position, elements: Vec<SyntaxElement>) {
+ pub fn insert_all(&self, position: Position, elements: Vec<SyntaxElement>) {
debug_assert!(is_ancestor_or_self(&position.parent(), &self.root));
- self.changes.push(Change::InsertAll(position, elements))
+ self.changes.borrow_mut().push(Change::InsertAll(position, elements))
}
- pub fn delete(&mut self, element: impl Element) {
+ pub fn insert_with_whitespace(&self, position: Position, element: impl Element) {
+ self.insert_all_with_whitespace(position, vec![element.syntax_element()])
+ }
+
+ pub fn insert_all_with_whitespace(&self, position: Position, mut elements: Vec<SyntaxElement>) {
+ if let Some(first) = elements.first()
+ && let Some(ws) = ws_before(&position, first, &self.make)
+ {
+ elements.insert(0, ws.into());
+ }
+ if let Some(last) = elements.last()
+ && let Some(ws) = ws_after(&position, last, &self.make)
+ {
+ elements.push(ws.into());
+ }
+ self.insert_all(position, elements)
+ }
+
+ pub fn delete(&self, element: impl Element) {
let element = element.syntax_element();
debug_assert!(is_ancestor_or_self_of_element(&element, &self.root));
debug_assert!(
!matches!(&element, SyntaxElement::Node(node) if node == &self.root),
"should not delete root node"
);
- self.changes.push(Change::Replace(element.syntax_element(), None));
+ self.changes.borrow_mut().push(Change::Replace(element.syntax_element(), None));
}
- pub fn delete_all(&mut self, range: RangeInclusive<SyntaxElement>) {
+ pub fn delete_all(&self, range: RangeInclusive<SyntaxElement>) {
if range.start() == range.end() {
self.delete(range.start());
return;
}
debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root));
- self.changes.push(Change::ReplaceAll(range, Vec::new()))
+ self.changes.borrow_mut().push(Change::ReplaceAll(range, Vec::new()))
}
- pub fn replace(&mut self, old: impl Element, new: impl Element) {
+ 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.push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
+ self.changes
+ .borrow_mut()
+ .push(Change::Replace(old.syntax_element(), Some(new.syntax_element())));
}
- pub fn replace_with_many(&mut self, old: impl Element, new: Vec<SyntaxElement>) {
+ pub fn replace_with_many(&self, old: impl Element, new: Vec<SyntaxElement>) {
let old = old.syntax_element();
debug_assert!(is_ancestor_or_self_of_element(&old, &self.root));
debug_assert!(
!(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1),
"cannot replace root node with many elements"
);
- self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new));
+ self.changes.borrow_mut().push(Change::ReplaceWithMany(old.syntax_element(), new));
}
- pub fn replace_all(&mut self, range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) {
+ pub fn replace_all(&self, range: RangeInclusive<SyntaxElement>, new: Vec<SyntaxElement>) {
if range.start() == range.end() {
self.replace_with_many(range.start(), new);
return;
}
debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root));
- self.changes.push(Change::ReplaceAll(range, new))
+ self.changes.borrow_mut().push(Change::ReplaceAll(range, new))
}
pub fn finish(self) -> SyntaxEdit {
edit_algo::apply_edits(self)
}
-
- pub fn add_mappings(&mut self, other: SyntaxMapping) {
- self.mappings.merge(other);
- }
}
/// Represents a completed [`SyntaxEditor`] operation.
@@ -384,6 +435,86 @@ impl Element for SyntaxToken {
}
}
+fn ws_before(
+ position: &Position,
+ new: &SyntaxElement,
+ factory: &SyntaxFactory,
+) -> Option<SyntaxToken> {
+ let prev = match &position.repr {
+ PositionRepr::FirstChild(_) => return None,
+ PositionRepr::After(it) => it,
+ };
+
+ if prev.kind() == T!['{']
+ && new.kind() == SyntaxKind::USE
+ && let Some(item_list) = prev.parent().and_then(ast::ItemList::cast)
+ {
+ let mut indent = IndentLevel::from_element(&item_list.syntax().clone().into());
+ indent.0 += 1;
+ return Some(factory.whitespace(&format!("\n{indent}")));
+ }
+
+ if prev.kind() == T!['{']
+ && ast::Stmt::can_cast(new.kind())
+ && let Some(stmt_list) = prev.parent().and_then(ast::StmtList::cast)
+ {
+ let mut indent = IndentLevel::from_element(&stmt_list.syntax().clone().into());
+ indent.0 += 1;
+ return Some(factory.whitespace(&format!("\n{indent}")));
+ }
+
+ ws_between(prev, new, factory)
+}
+
+fn ws_after(
+ position: &Position,
+ new: &SyntaxElement,
+ factory: &SyntaxFactory,
+) -> Option<SyntaxToken> {
+ let next = match &position.repr {
+ PositionRepr::FirstChild(parent) => parent.first_child_or_token()?,
+ PositionRepr::After(sibling) => sibling.next_sibling_or_token()?,
+ };
+ ws_between(new, &next, factory)
+}
+
+fn ws_between(
+ left: &SyntaxElement,
+ right: &SyntaxElement,
+ factory: &SyntaxFactory,
+) -> Option<SyntaxToken> {
+ if left.kind() == SyntaxKind::WHITESPACE || right.kind() == SyntaxKind::WHITESPACE {
+ return None;
+ }
+ if right.kind() == T![;] || right.kind() == T![,] {
+ return None;
+ }
+ if left.kind() == T![<] || right.kind() == T![>] {
+ return None;
+ }
+ if left.kind() == T![&] && right.kind() == SyntaxKind::LIFETIME {
+ return None;
+ }
+ if right.kind() == SyntaxKind::GENERIC_ARG_LIST {
+ return None;
+ }
+ if right.kind() == SyntaxKind::USE {
+ let mut indent = IndentLevel::from_element(left);
+ if left.kind() == SyntaxKind::USE {
+ indent.0 = IndentLevel::from_element(right).0.max(indent.0);
+ }
+ return Some(factory.whitespace(&format!("\n{indent}")));
+ }
+ if left.kind() == SyntaxKind::ATTR {
+ let mut indent = IndentLevel::from_element(right);
+ if right.kind() == SyntaxKind::ATTR {
+ indent.0 = IndentLevel::from_element(left).0.max(indent.0);
+ }
+ return Some(factory.whitespace(&format!("\n{indent}")));
+ }
+ Some(factory.whitespace(" "))
+}
+
fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool {
node == ancestor || node.ancestors().any(|it| &it == ancestor)
}
@@ -399,7 +530,7 @@ mod tests {
use crate::{
AstNode,
- ast::{self, make, syntax_factory::SyntaxFactory},
+ ast::{self, make},
};
use super::*;
@@ -420,12 +551,12 @@ mod tests {
.into(),
);
+ let (editor, root) = SyntaxEditor::with_ast_node(&root);
+ let make = editor.make();
+
let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap();
let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap();
- let mut editor = SyntaxEditor::new(root.syntax().clone());
- let make = SyntaxFactory::with_mappings();
-
let name = make::name("var_name");
let name_ref = make::name_ref("var_name").clone_for_update();
@@ -434,7 +565,8 @@ mod tests {
editor.add_annotation(name_ref.syntax(), placeholder_snippet);
let new_block = make.block_expr(
- [make
+ [editor
+ .make()
.let_stmt(
make.ident_pat(false, false, name.clone()).into(),
None,
@@ -446,7 +578,6 @@ mod tests {
editor.replace(to_replace.syntax(), name_ref.syntax());
editor.replace(to_wrap.syntax(), new_block.syntax());
- editor.add_mappings(make.finish_with_mappings());
let edit = editor.finish();
@@ -460,8 +591,8 @@ mod tests {
assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2);
assert!(
edit.annotations
- .iter()
- .flat_map(|(_, elements)| elements)
+ .values()
+ .flatten()
.all(|element| element.ancestors().any(|it| &it == edit.new_root()))
)
}
@@ -478,11 +609,10 @@ mod tests {
None,
);
+ let (editor, root) = SyntaxEditor::with_ast_node(&root);
+ let make = editor.make();
let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap();
- let mut editor = SyntaxEditor::new(root.syntax().clone());
- let make = SyntaxFactory::without_mappings();
-
editor.insert(
Position::first_child_of(root.stmt_list().unwrap().syntax()),
make.let_stmt(
@@ -530,13 +660,13 @@ mod tests {
),
);
+ 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 second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap();
- let mut editor = SyntaxEditor::new(root.syntax().clone());
- let make = SyntaxFactory::with_mappings();
-
let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
let first_let = make.let_stmt(
@@ -557,7 +687,6 @@ mod tests {
);
editor.insert(Position::after(second_let.syntax()), third_let.syntax());
editor.replace(inner_block.syntax(), new_block_expr.syntax());
- editor.add_mappings(make.finish_with_mappings());
let edit = editor.finish();
@@ -584,10 +713,10 @@ mod tests {
None,
);
- let inner_block = root.clone();
+ let (editor, root) = SyntaxEditor::with_ast_node(&root);
+ let make = editor.make();
- let mut editor = SyntaxEditor::new(root.syntax().clone());
- let make = SyntaxFactory::with_mappings();
+ let inner_block = root;
let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
@@ -602,7 +731,6 @@ mod tests {
first_let.syntax(),
);
editor.replace(inner_block.syntax(), new_block_expr.syntax());
- editor.add_mappings(make.finish_with_mappings());
let edit = editor.finish();
@@ -632,7 +760,7 @@ mod tests {
false,
);
- let mut editor = SyntaxEditor::new(parent_fn.syntax().clone());
+ let (editor, parent_fn) = SyntaxEditor::with_ast_node(&parent_fn);
if let Some(ret_ty) = parent_fn.ret_type() {
editor.delete(ret_ty.syntax().clone());
@@ -659,7 +787,8 @@ mod tests {
let arg_list =
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
- let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
+ let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list);
+
let target_expr = make::token(parser::SyntaxKind::UNDERSCORE);
for arg in arg_list.args() {
@@ -677,7 +806,8 @@ mod tests {
let arg_list =
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
- let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
+ let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list);
+
let target_expr = make::expr_literal("3").clone_for_update();
for arg in arg_list.args() {
@@ -695,7 +825,8 @@ mod tests {
let arg_list =
make::arg_list([make::expr_literal("1").into(), make::expr_literal("2").into()]);
- let mut editor = SyntaxEditor::new(arg_list.syntax().clone());
+ let (editor, arg_list) = SyntaxEditor::with_ast_node(&arg_list);
+
let target_expr = make::ext::expr_unit().clone_for_update();
for arg in arg_list.args() {
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs
index e697d97061..27ea03ec09 100644
--- a/crates/syntax/src/syntax_editor/edit_algo.rs
+++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -35,7 +35,10 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
// - changed nodes become part of the changed node set (useful for the formatter to only change those parts)
// - Propagate annotations
- let SyntaxEditor { root, mut changes, mappings, annotations } = editor;
+ let SyntaxEditor { root, changes, annotations, make } = editor;
+ let mut changes = changes.into_inner();
+ let annotations = annotations.into_inner();
+ let mappings = make.take();
let mut node_depths = FxHashMap::<SyntaxNode, usize>::default();
let mut get_node_depth = |node: SyntaxNode| {
@@ -192,11 +195,8 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
}
};
}
- Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(new_target))) => {
+ Change::Replace(SyntaxElement::Node(target), Some(SyntaxElement::Node(_))) => {
*target = tree_mutator.make_syntax_mut(target);
- if new_target.ancestors().any(|node| node == tree_mutator.immutable) {
- *new_target = new_target.clone_for_update();
- }
}
Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => {
*target = tree_mutator.make_element_mut(target);
@@ -210,6 +210,56 @@ pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit {
}
match &mut changes[index as usize] {
+ Change::Insert(_, SyntaxElement::Node(node))
+ | Change::Replace(_, Some(SyntaxElement::Node(node))) => {
+ if node.parent().is_some() {
+ *node = node.clone_subtree().clone_for_update();
+ } else if !node.is_mutable() {
+ *node = node.clone_for_update();
+ }
+ }
+ Change::Insert(_, SyntaxElement::Token(token))
+ | Change::Replace(_, Some(SyntaxElement::Token(token))) => {
+ if let Some(parent) = token.parent() {
+ let idx = token.index();
+ let new_parent = parent.clone_subtree().clone_for_update();
+ *token = new_parent
+ .children_with_tokens()
+ .nth(idx)
+ .and_then(SyntaxElement::into_token)
+ .unwrap();
+ }
+ }
+ Change::InsertAll(_, elements)
+ | Change::ReplaceWithMany(_, elements)
+ | Change::ReplaceAll(_, elements) => {
+ for element in elements {
+ match element {
+ SyntaxElement::Node(node) => {
+ if node.parent().is_some() {
+ *node = node.clone_subtree().clone_for_update();
+ } else if !node.is_mutable() {
+ *node = node.clone_for_update();
+ }
+ }
+ SyntaxElement::Token(token) => {
+ if let Some(parent) = token.parent() {
+ let idx = token.index();
+ let new_parent = parent.clone_subtree().clone_for_update();
+ *token = new_parent
+ .children_with_tokens()
+ .nth(idx)
+ .and_then(SyntaxElement::into_token)
+ .unwrap();
+ }
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+
+ match &mut changes[index as usize] {
Change::Insert(_, element) | Change::Replace(_, Some(element)) => {
deduplicate_node(element);
}
diff --git a/crates/syntax/src/syntax_editor/edits.rs b/crates/syntax/src/syntax_editor/edits.rs
index 44f0a8038e..28e8ceed70 100644
--- a/crates/syntax/src/syntax_editor/edits.rs
+++ b/crates/syntax/src/syntax_editor/edits.rs
@@ -3,10 +3,7 @@
use crate::{
AstToken, Direction, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T,
algo::neighbor,
- ast::{
- self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make,
- syntax_factory::SyntaxFactory,
- },
+ ast::{self, AstNode, Fn, GenericParam, HasGenericParams, HasName, edit::IndentLevel, make},
syntax_editor::{Position, SyntaxEditor},
};
@@ -15,10 +12,10 @@ pub trait GetOrCreateWhereClause: ast::HasGenericParams {
fn get_or_create_where_clause(
&self,
- editor: &mut SyntaxEditor,
- make: &SyntaxFactory,
+ editor: &SyntaxEditor,
new_preds: impl Iterator<Item = ast::WherePred>,
) {
+ let make = editor.make();
let existing = self.where_clause();
let all_preds: Vec<_> =
existing.iter().flat_map(|wc| wc.predicates()).chain(new_preds).collect();
@@ -113,7 +110,7 @@ impl GetOrCreateWhereClause for ast::Enum {
impl SyntaxEditor {
/// Adds a new generic param to the function using `SyntaxEditor`
- pub fn add_generic_param(&mut self, function: &Fn, new_param: GenericParam) {
+ 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) => {
@@ -177,8 +174,8 @@ impl SyntaxEditor {
}
}
-fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> SyntaxToken {
- let make = SyntaxFactory::without_mappings();
+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())
@@ -198,7 +195,7 @@ impl ast::AssocItemList {
///
/// Attention! This function does align the first line of `item` with respect to `self`,
/// but it does _not_ change indentation of other lines (if any).
- pub fn add_items(&self, editor: &mut SyntaxEditor, items: Vec<ast::AssocItem>) {
+ pub fn add_items(&self, editor: &SyntaxEditor, items: Vec<ast::AssocItem>) {
let (indent, position, whitespace) = match self.assoc_items().last() {
Some(last_item) => (
IndentLevel::from_node(last_item.syntax()),
@@ -229,9 +226,28 @@ impl ast::AssocItemList {
}
}
+impl ast::Impl {
+ pub fn get_or_create_assoc_item_list_with_editor(
+ &self,
+ editor: &SyntaxEditor,
+ ) -> ast::AssocItemList {
+ let make = editor.make();
+ if let Some(list) = self.assoc_item_list() {
+ list
+ } else {
+ let list = make.assoc_item_list_empty();
+ editor.insert_all(
+ Position::last_child_of(self.syntax()),
+ vec![make.whitespace(" ").into(), list.syntax().clone().into()],
+ );
+ list
+ }
+ }
+}
+
impl ast::VariantList {
- pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) {
- let make = SyntaxFactory::without_mappings();
+ pub fn add_variant(&self, editor: &SyntaxEditor, variant: &ast::Variant) {
+ let make = editor.make();
let (indent, position) = match self.variants().last() {
Some(last_item) => (
IndentLevel::from_node(last_item.syntax()),
@@ -255,7 +271,7 @@ impl ast::VariantList {
}
impl ast::Fn {
- pub fn replace_or_insert_body(&self, editor: &mut SyntaxEditor, body: ast::BlockExpr) {
+ pub fn replace_or_insert_body(&self, editor: &SyntaxEditor, body: ast::BlockExpr) {
if let Some(old_body) = self.body() {
editor.replace(old_body.syntax(), body.syntax());
} else {
@@ -271,8 +287,8 @@ impl ast::Fn {
}
}
-fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) -> Option<()> {
- let make = SyntaxFactory::without_mappings();
+fn normalize_ws_between_braces(editor: &SyntaxEditor, node: &SyntaxNode) -> Option<()> {
+ let make = editor.make();
let l = node
.children_with_tokens()
.filter_map(|it| it.into_token())
@@ -285,10 +301,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) ->
let indent = IndentLevel::from_node(node);
match l.next_sibling_or_token() {
- Some(ws) if ws.kind() == SyntaxKind::WHITESPACE => {
- if ws.next_sibling_or_token()?.into_token()? == r {
- editor.replace(ws, make.whitespace(&format!("\n{indent}")));
- }
+ Some(ws)
+ if ws.kind() == SyntaxKind::WHITESPACE
+ && ws.next_sibling_or_token()?.into_token()? == r =>
+ {
+ editor.replace(ws, make.whitespace(&format!("\n{indent}")));
}
Some(ws) if ws.kind() == T!['}'] => {
editor.insert(Position::after(l), make.whitespace(&format!("\n{indent}")));
@@ -299,11 +316,11 @@ fn normalize_ws_between_braces(editor: &mut SyntaxEditor, node: &SyntaxNode) ->
}
pub trait Removable: AstNode {
- fn remove(&self, editor: &mut SyntaxEditor);
+ fn remove(&self, editor: &SyntaxEditor);
}
impl Removable for ast::TypeBoundList {
- fn remove(&self, editor: &mut SyntaxEditor) {
+ fn remove(&self, editor: &SyntaxEditor) {
match self.syntax().siblings_with_tokens(Direction::Prev).find(|it| it.kind() == T![:]) {
Some(colon) => editor.delete_all(colon..=self.syntax().clone().into()),
None => editor.delete(self.syntax()),
@@ -312,9 +329,8 @@ impl Removable for ast::TypeBoundList {
}
impl Removable for ast::Use {
- fn remove(&self, editor: &mut SyntaxEditor) {
- let make = SyntaxFactory::without_mappings();
-
+ fn remove(&self, editor: &SyntaxEditor) {
+ let make = editor.make();
let next_ws = self
.syntax()
.next_sibling_or_token()
@@ -336,7 +352,7 @@ impl Removable for ast::Use {
}
impl Removable for ast::UseTree {
- fn remove(&self, editor: &mut SyntaxEditor) {
+ fn remove(&self, editor: &SyntaxEditor) {
for dir in [Direction::Next, Direction::Prev] {
if let Some(next_use_tree) = neighbor(self, dir) {
let separators = self
@@ -360,7 +376,7 @@ mod tests {
use stdx::trim_indent;
use test_utils::assert_eq_text;
- use crate::SourceFile;
+ use crate::{SourceFile, ast::syntax_factory::SyntaxFactory};
use super::*;
@@ -473,10 +489,9 @@ enum Foo {
}
fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
- let enum_ = ast_from_text::<ast::Enum>(before);
- let mut editor = SyntaxEditor::new(enum_.syntax().clone());
+ let (editor, enum_) = SyntaxEditor::with_ast_node(&ast_from_text::<ast::Enum>(before));
if let Some(it) = enum_.variant_list() {
- it.add_variant(&mut editor, &variant)
+ it.add_variant(&editor, &variant)
}
let edit = editor.finish();
let after = edit.new_root.to_string();
diff --git a/crates/syntax/src/syntax_editor/mapping.rs b/crates/syntax/src/syntax_editor/mapping.rs
index 6257bf4e57..180c2e69fa 100644
--- a/crates/syntax/src/syntax_editor/mapping.rs
+++ b/crates/syntax/src/syntax_editor/mapping.rs
@@ -161,7 +161,7 @@ impl SyntaxMapping {
// Try to follow the mapping tree, if it exists
let input_mapping = self.upmap_node_single(input);
let input_ancestor =
- input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor));
+ input.ancestors().find(|ancestor| self.upmap_node_single(ancestor).is_some());
match (input_mapping, input_ancestor) {
(Some(input_mapping), _) => {
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs
index 485140be8f..4622590656 100644
--- a/crates/syntax/src/validation.rs
+++ b/crates/syntax/src/validation.rs
@@ -240,8 +240,16 @@ fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<Syntax
}
fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
- let path_without_in_token = vis.in_token().is_none()
- && vis.path().and_then(|p| p.as_single_name_ref()).and_then(|n| n.ident_token()).is_some();
+ let path_without_in_token = if let Some(inner) = vis.visibility_inner() {
+ inner.in_token().is_none()
+ && inner
+ .path()
+ .and_then(|p| p.as_single_name_ref())
+ .and_then(|n| n.ident_token())
+ .is_some()
+ } else {
+ false
+ };
if path_without_in_token {
errors.push(SyntaxError::new("incorrect visibility restriction", vis.syntax.text_range()));
}
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
index 50057a02d8..5fdde93c60 100644
--- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
+++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -29,7 +29,7 @@ [email protected]
@@ -60,7 +60,7 @@ [email protected]
@@ -75,7 +75,7 @@ [email protected]
@@ -104,7 +104,7 @@ [email protected]
diff --git a/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast b/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
index 90c258cd1a..2d6d4b2681 100644
--- a/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
+++ b/crates/syntax/test_data/parser/validation/0037_visibility_in_traits.rast
@@ -51,12 +51,13 @@ [email protected]
@@ -73,12 +74,13 @@ [email protected]
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index e271c32c86..f346535ca1 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -11,7 +11,7 @@ use base_db::target::TargetData;
use base_db::{
Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
- SourceRoot, Version, VfsPath,
+ SourceRoot, Version, VfsPath, all_crates,
};
use cfg::CfgOptions;
use hir_expand::{
@@ -227,7 +227,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static {
}
fn test_crate(&self) -> Crate {
- self.all_crates().iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap()
+ all_crates(self).iter().copied().find(|&krate| !krate.data(self).origin.is_lang()).unwrap()
}
}
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 86fb080732..a51698aca8 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -936,6 +936,14 @@ pub mod ops {
}
}
}
+
+ mod internal_implementation_detail {
+ #[lang = "async_fn_kind_helper"]
+ trait AsyncFnKindHelper<GoalKind> {
+ #[lang = "async_fn_kind_upvars"]
+ type Upvars<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>;
+ }
+ }
}
pub use self::async_function::{AsyncFn, AsyncFnMut, AsyncFnOnce};
// endregion:async_fn
diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs
index 428b19c50b..6465a85d2d 100644
--- a/crates/vfs-notify/src/lib.rs
+++ b/crates/vfs-notify/src/lib.rs
@@ -14,7 +14,7 @@ use std::{
};
use crossbeam_channel::{Receiver, Sender, select, unbounded};
-use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
+use notify::{Config, EventKind, RecommendedWatcher, RecursiveMode, Watcher, event::AccessKind};
use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
use rayon::iter::{IndexedParallelIterator as _, IntoParallelIterator as _, ParallelIterator};
use rustc_hash::FxHashSet;
@@ -63,6 +63,7 @@ struct NotifyActor {
sender: loader::Sender,
watched_file_entries: FxHashSet<AbsPathBuf>,
watched_dir_entries: Vec<loader::Directories>,
+ seen_paths: FxHashSet<AbsPathBuf>,
// Drop order is significant.
watcher: Option<(RecommendedWatcher, Receiver<NotifyEvent>)>,
}
@@ -79,6 +80,7 @@ impl NotifyActor {
sender,
watched_dir_entries: Vec::new(),
watched_file_entries: FxHashSet::default(),
+ seen_paths: FxHashSet::default(),
watcher: None,
}
}
@@ -120,6 +122,7 @@ impl NotifyActor {
let n_total = config.load.len();
self.watched_dir_entries.clear();
self.watched_file_entries.clear();
+ self.seen_paths.clear();
self.send(loader::Message::Progress {
n_total,
@@ -195,10 +198,12 @@ impl NotifyActor {
},
Event::NotifyEvent(event) => {
if let Some(event) = log_notify_error(event)
- && let EventKind::Create(_) | EventKind::Modify(_) | EventKind::Remove(_) =
- event.kind
+ && let EventKind::Create(_)
+ | EventKind::Modify(_)
+ | EventKind::Remove(_)
+ | EventKind::Access(AccessKind::Open(_)) = event.kind
{
- let files = event
+ let abs_paths: Vec<AbsPathBuf> = event
.paths
.into_iter()
.filter_map(|path| {
@@ -207,6 +212,28 @@ impl NotifyActor {
.expect("path is absolute"),
)
})
+ .collect();
+
+ let mut saw_new_file = false;
+ for abs_path in &abs_paths {
+ if self.seen_paths.insert(abs_path.clone()) {
+ saw_new_file = true;
+ }
+ }
+
+ // Only consider access events for files that we haven't seen
+ // before.
+ //
+ // This is important on FUSE filesystems, where we may not get a
+ // Create event. In other cases we're about to access the file, so
+ // we don't want an infinite loop where processing an Access event
+ // creates another Access event.
+ if matches!(event.kind, EventKind::Access(_)) && !saw_new_file {
+ continue;
+ }
+
+ let files = abs_paths
+ .into_iter()
.filter_map(|path| -> Option<(AbsPathBuf, Option<Vec<u8>>)> {
// Ignore events for files/directories that we're not watching.
if !(self.watched_file_entries.contains(&path)
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
index 50e388d780..d48b984407 100644
--- a/crates/vfs/src/lib.rs
+++ b/crates/vfs/src/lib.rs
@@ -157,7 +157,7 @@ pub enum Change {
}
/// Kind of [file change](ChangedFile).
-#[derive(Eq, PartialEq, Debug)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChangeKind {
/// The file was (re-)created
Create,
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 35fba5accd..da37fc1582 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -166,6 +166,14 @@ List of features to activate.
Set this to `"all"` to pass `--all-features` to cargo.
+## rust-analyzer.cargo.metadataExtraArgs {#cargo.metadataExtraArgs}
+
+Default: `[]`
+
+Extra arguments passed only to `cargo metadata`, not to other cargo invocations.
+Useful for flags like `--config` that `cargo metadata` supports.
+
+
## rust-analyzer.cargo.noDefaultFeatures {#cargo.noDefaultFeatures}
Default: `false`
@@ -618,6 +626,15 @@ The warnings will be indicated by a blue squiggly underline in code and a blue i
the `Problems Panel`.
+## rust-analyzer.disableFixtureSupport {#disableFixtureSupport}
+
+Default: `false`
+
+Disable support for `#[rust_analyzer::rust_fixture]` snippets.
+
+If you are not working on rust-analyzer itself, you should ignore this config.
+
+
## rust-analyzer.document.symbol.search.excludeLocals {#document.symbol.search.excludeLocals}
Default: `true`
@@ -1147,6 +1164,13 @@ Default: `false`
Hide inlay type hints for constructors.
+## rust-analyzer.inlayHints.typeHints.location {#inlayHints.typeHints.location}
+
+Default: `"inline"`
+
+Where to render type hints relative to their binding pattern.
+
+
## rust-analyzer.interpret.tests {#interpret.tests}
Default: `false`
@@ -1380,9 +1404,15 @@ Default: `null`
Override the command used for bench runnables.
The first element of the array should be the program to execute (for example, `cargo`).
-Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
-replace the package name, target option (such as `--bin` or `--example`), the target name and
-the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+Use the placeholders:
+- `${package}`: package name.
+- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+- `${target}`: target name (empty for `--lib`).
+- `${test_name}`: the test path filter, e.g. `module::bench_func`.
+- `${exact}`: `--exact` for single benchmarks, empty for modules.
+- `${include_ignored}`: always empty for benchmarks.
+- `${executable_args}`: all of the above binary args bundled together
+ (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
## rust-analyzer.runnables.command {#runnables.command}
@@ -1396,12 +1426,18 @@ Command to be executed instead of 'cargo' for runnables.
Default: `null`
-Override the command used for bench runnables.
+Override the command used for doc-test runnables.
The first element of the array should be the program to execute (for example, `cargo`).
-Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
-replace the package name, target option (such as `--bin` or `--example`), the target name and
-the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+Use the placeholders:
+- `${package}`: package name.
+- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+- `${target}`: target name (empty for `--lib`).
+- `${test_name}`: the test path filter, e.g. `module::func`.
+- `${exact}`: always empty for doc-tests.
+- `${include_ignored}`: always empty for doc-tests.
+- `${executable_args}`: all of the above binary args bundled together
+ (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
## rust-analyzer.runnables.extraArgs {#runnables.extraArgs}
@@ -1444,9 +1480,15 @@ Default: `null`
Override the command used for test runnables.
The first element of the array should be the program to execute (for example, `cargo`).
-Use the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically
-replace the package name, target option (such as `--bin` or `--example`), the target name and
-the arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
+Available placeholders:
+- `${package}`: package name.
+- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.
+- `${target}`: target name (empty for `--lib`).
+- `${test_name}`: the test path filter, e.g. `module::test_func`.
+- `${exact}`: `--exact` for single tests, empty for modules.
+- `${include_ignored}`: `--include-ignored` for single tests, empty otherwise.
+- `${executable_args}`: all of the above binary args bundled together
+ (includes `rust-analyzer.runnables.extraTestBinaryArgs`).
## rust-analyzer.rustc.source {#rustc.source}
diff --git a/docs/book/src/contributing/lsp-extensions.md b/docs/book/src/contributing/lsp-extensions.md
index 5d21c37806..8ba6f6ab53 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: 235f56089da3dbb5
+lsp/ext.rs hash: dc4ba5f417c74aa6
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:
@@ -236,7 +236,7 @@ fn main() {
```
The primary goal of `onEnter` is to handle automatic indentation when opening a new line.
-This is not yet implemented.
+This is partially implemented for single-line brace-delimited contents, in addition to comment continuation.
The secondary goal is to handle fixing up syntax, like continuing doc strings and comments, and escaping `\n` in string literals.
As proper cursor positioning is raison d'être for `onEnter`, it uses `SnippetTextEdit`.
@@ -653,7 +653,7 @@ Note that this functionality is intended primarily to inform the end user about
In particular, it's valid for the client to completely ignore this extension.
Clients are discouraged from but are allowed to use the `health` status to decide if it's worth sending a request to the server.
-### Controlling Flycheck
+## Controlling Flycheck
The flycheck/checkOnSave feature can be controlled via notifications sent by the client to the server.
diff --git a/docs/book/src/other_editors.md b/docs/book/src/other_editors.md
index f7116fc19a..1cb2a44063 100644
--- a/docs/book/src/other_editors.md
+++ b/docs/book/src/other_editors.md
@@ -137,24 +137,22 @@ To use the LSP server in [ale](https://github.com/dense-analysis/ale):
### nvim-lsp
-Neovim 0.5 has built-in language server support. For a quick start
-configuration of rust-analyzer, use
-[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer).
-Once `neovim/nvim-lspconfig` is installed, use
-`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`.
-
-You can also pass LSP settings to the server:
+Neovim 0.5+ added build-in support for language server with most of the heavy
+lifting happening in "framework" plugins such as
+[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig).
+Since v0.11+ Neovim has full featured LSP support. nvim-lspconfig is
+still recommended to get the
+[rust-analyzer config](https://github.com/neovim/nvim-lspconfig/blob/master/lsp/rust_analyzer.lua)
+for free.
+
+1. Install [neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig)
+2. Add `lua vim.lsp.enable('rust-analyzer')` to your `init.vim`
+3. Customize your setup.
```lua
lua << EOF
-local lspconfig = require'lspconfig'
-
-local on_attach = function(client)
- require'completion'.on_attach(client)
-end
-
-lspconfig.rust_analyzer.setup({
- on_attach = on_attach,
+-- You can pass LSP settings to the server:
+vim.lsp.config("rust_analyzer", {
settings = {
["rust-analyzer"] = {
imports = {
@@ -171,30 +169,35 @@ lspconfig.rust_analyzer.setup({
procMacro = {
enable = true
},
- }
- }
+ },
+ },
})
-EOF
-```
-
-If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`:
-```lua
-lspconfig.rust_analyzer.setup({
- on_attach = function(client, bufnr)
- vim.lsp.inlay_hint.enable(true, { bufnr = bufnr })
- end
+-- You can enable different LSP features
+vim.api.nvim_create_autocmd("LspAttach", {
+ callback = function(ev)
+ local client = assert(vim.lsp.get_client_by_id(ev.data.client_id))
+ -- Inlay hints display inferred types, etc.
+ if client:supports_method("inlayHint/resolve") then
+ vim.lsp.inlay_hint.enable(true, { bufnr = ev.buf })
+ end
+ -- Completion can be invoked via ctrl+x ctrl+o. It displays a list of
+ -- names inferred from the context (e.g. method names, variables, etc.)
+ if client:supports_method("textDocument/completion") then
+ vim.lsp.completion.enable(true, client.id, ev.buf, {})
+ end
+ end,
})
+EOF
```
-Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to
-edit the file to trigger a re-render.
-
-See <https://sharksforarms.dev/posts/neovim-rust/> for more tips on
-getting started.
+Note that the hints are only visible after `rust-analyzer` has finished loading
+**and** you have to edit the file to trigger a re-render.
-Check out <https://github.com/mrcjkb/rustaceanvim> for a batteries
-included rust-analyzer setup for Neovim.
+The instructions here use the 0.11+ API, if you're running an older version, you
+can follow this guide <https://sharksforarms.dev/posts/neovim-rust/> or check
+out <https://github.com/mrcjkb/rustaceanvim> for a batteries included
+rust-analyzer setup for Neovim.
### vim-lsp
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index b51dc4d132..3037deb056 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -17,8 +17,8 @@
"vscode-languageclient": "^9.0.1"
},
"devDependencies": {
- "@eslint/js": "^9.21.0",
- "@stylistic/eslint-plugin": "^4.1.0",
+ "@eslint/js": "^10.0.1",
+ "@stylistic/eslint-plugin": "^5.10.0",
"@stylistic/eslint-plugin-js": "^4.1.0",
"@tsconfig/strictest": "^2.0.5",
"@types/lodash": "^4.17.20",
@@ -27,16 +27,16 @@
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vscode/test-electron": "^2.4.1",
- "@vscode/vsce": "^3.6.0",
+ "@vscode/vsce": "^3.7.1",
"esbuild": "^0.25.0",
- "eslint": "^9.21.0",
- "eslint-config-prettier": "^10.0.2",
+ "eslint": "^10.2.0",
+ "eslint-config-prettier": "^10.1.8",
"eslint-define-config": "^2.1.0",
- "ovsx": "0.10.1",
- "prettier": "^3.5.2",
+ "ovsx": "0.10.10",
+ "prettier": "^3.8.1",
"tslib": "^2.8.1",
- "typescript": "^5.7.3",
- "typescript-eslint": "^8.25.0"
+ "typescript": "^6.0.2",
+ "typescript-eslint": "^8.58.0"
},
"engines": {
"vscode": "^1.93.0"
@@ -255,6 +255,40 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@emnapi/core": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz",
+ "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.2.0",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz",
+ "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz",
+ "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
@@ -681,9 +715,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
- "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -713,9 +747,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -723,137 +757,89 @@
}
},
"node_modules/@eslint/config-array": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
- "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.4.tgz",
+ "integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/object-schema": "^2.1.6",
+ "@eslint/object-schema": "^3.0.4",
"debug": "^4.3.1",
- "minimatch": "^3.1.2"
+ "minimatch": "^10.2.4"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.4.tgz",
+ "integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
+ "license": "Apache-2.0",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "@eslint/core": "^1.2.0"
},
"engines": {
- "node": "*"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/core": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
- "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.0.tgz",
+ "integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
- "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
+ "node_modules/@eslint/js": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz",
+ "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
+ "url": "https://eslint.org/donate"
},
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@eslint/js": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
- "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "peerDependencies": {
+ "eslint": "^10.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
"node_modules/@eslint/object-schema": {
- "version": "2.1.6",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
- "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.4.tgz",
+ "integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@eslint/plugin-kit": {
- "version": "0.2.7",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
- "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz",
+ "integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@eslint/core": "^0.12.0",
+ "@eslint/core": "^1.2.0",
"levn": "^0.4.1"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
}
},
"node_modules/@hpcc-js/wasm": {
@@ -952,6 +938,287 @@
"node": ">=12"
}
},
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz",
+ "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
+ "node_modules/@node-rs/crc32": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32/-/crc32-1.10.6.tgz",
+ "integrity": "sha512-+llXfqt+UzgoDzT9of5vPQPGqTAVCohU74I9zIBkNo5TH6s2P31DFJOGsJQKN207f0GHnYv5pV3wh3BCY/un/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@node-rs/crc32-android-arm-eabi": "1.10.6",
+ "@node-rs/crc32-android-arm64": "1.10.6",
+ "@node-rs/crc32-darwin-arm64": "1.10.6",
+ "@node-rs/crc32-darwin-x64": "1.10.6",
+ "@node-rs/crc32-freebsd-x64": "1.10.6",
+ "@node-rs/crc32-linux-arm-gnueabihf": "1.10.6",
+ "@node-rs/crc32-linux-arm64-gnu": "1.10.6",
+ "@node-rs/crc32-linux-arm64-musl": "1.10.6",
+ "@node-rs/crc32-linux-x64-gnu": "1.10.6",
+ "@node-rs/crc32-linux-x64-musl": "1.10.6",
+ "@node-rs/crc32-wasm32-wasi": "1.10.6",
+ "@node-rs/crc32-win32-arm64-msvc": "1.10.6",
+ "@node-rs/crc32-win32-ia32-msvc": "1.10.6",
+ "@node-rs/crc32-win32-x64-msvc": "1.10.6"
+ }
+ },
+ "node_modules/@node-rs/crc32-android-arm-eabi": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-android-arm-eabi/-/crc32-android-arm-eabi-1.10.6.tgz",
+ "integrity": "sha512-vZAMuJXm3TpWPOkkhxdrofWDv+Q+I2oO7ucLRbXyAPmXFNDhHtBxbO1rk9Qzz+M3eep8ieS4/+jCL1Q0zacNMQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-android-arm64": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-android-arm64/-/crc32-android-arm64-1.10.6.tgz",
+ "integrity": "sha512-Vl/JbjCinCw/H9gEpZveWCMjxjcEChDcDBM8S4hKay5yyoRCUHJPuKr4sjVDBeOm+1nwU3oOm6Ca8dyblwp4/w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-darwin-arm64": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-darwin-arm64/-/crc32-darwin-arm64-1.10.6.tgz",
+ "integrity": "sha512-kARYANp5GnmsQiViA5Qu74weYQ3phOHSYQf0G+U5wB3NB5JmBHnZcOc46Ig21tTypWtdv7u63TaltJQE41noyg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-darwin-x64": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-darwin-x64/-/crc32-darwin-x64-1.10.6.tgz",
+ "integrity": "sha512-Q99bevJVMfLTISpkpKBlXgtPUItrvTWKFyiqoKH5IvscZmLV++NH4V13Pa17GTBmv9n18OwzgQY4/SRq6PQNVA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-freebsd-x64": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-freebsd-x64/-/crc32-freebsd-x64-1.10.6.tgz",
+ "integrity": "sha512-66hpawbNjrgnS9EDMErta/lpaqOMrL6a6ee+nlI2viduVOmRZWm9Rg9XdGTK/+c4bQLdtC6jOd+Kp4EyGRYkAg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-linux-arm-gnueabihf": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm-gnueabihf/-/crc32-linux-arm-gnueabihf-1.10.6.tgz",
+ "integrity": "sha512-E8Z0WChH7X6ankbVm8J/Yym19Cq3otx6l4NFPS6JW/cWdjv7iw+Sps2huSug+TBprjbcEA+s4TvEwfDI1KScjg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-linux-arm64-gnu": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm64-gnu/-/crc32-linux-arm64-gnu-1.10.6.tgz",
+ "integrity": "sha512-LmWcfDbqAvypX0bQjQVPmQGazh4dLiVklkgHxpV4P0TcQ1DT86H/SWpMBMs/ncF8DGuCQ05cNyMv1iddUDugoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-linux-arm64-musl": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-arm64-musl/-/crc32-linux-arm64-musl-1.10.6.tgz",
+ "integrity": "sha512-k8ra/bmg0hwRrIEE8JL1p32WfaN9gDlUUpQRWsbxd1WhjqvXea7kKO6K4DwVxyxlPhBS9Gkb5Urq7Y4mXANzaw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-linux-x64-gnu": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-x64-gnu/-/crc32-linux-x64-gnu-1.10.6.tgz",
+ "integrity": "sha512-IfjtqcuFK7JrSZ9mlAFhb83xgium30PguvRjIMI45C3FJwu18bnLk1oR619IYb/zetQT82MObgmqfKOtgemEKw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-linux-x64-musl": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-linux-x64-musl/-/crc32-linux-x64-musl-1.10.6.tgz",
+ "integrity": "sha512-LbFYsA5M9pNunOweSt6uhxenYQF94v3bHDAQRPTQ3rnjn+mK6IC7YTAYoBjvoJP8lVzcvk9hRj8wp4Jyh6Y80g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-wasm32-wasi": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-wasm32-wasi/-/crc32-wasm32-wasi-1.10.6.tgz",
+ "integrity": "sha512-KaejdLgHMPsRaxnM+OG9L9XdWL2TabNx80HLdsCOoX9BVhEkfh39OeahBo8lBmidylKbLGMQoGfIKDjq0YMStw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^0.2.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@node-rs/crc32-win32-arm64-msvc": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-arm64-msvc/-/crc32-win32-arm64-msvc-1.10.6.tgz",
+ "integrity": "sha512-x50AXiSxn5Ccn+dCjLf1T7ZpdBiV1Sp5aC+H2ijhJO4alwznvXgWbopPRVhbp2nj0i+Gb6kkDUEyU+508KAdGQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-win32-ia32-msvc": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-ia32-msvc/-/crc32-win32-ia32-msvc-1.10.6.tgz",
+ "integrity": "sha512-DpDxQLaErJF9l36aghe1Mx+cOnYLKYo6qVPqPL9ukJ5rAGLtCdU0C+Zoi3gs9ySm8zmbFgazq/LvmsZYU42aBw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@node-rs/crc32-win32-x64-msvc": {
+ "version": "1.10.6",
+ "resolved": "https://registry.npmjs.org/@node-rs/crc32-win32-x64-msvc/-/crc32-win32-x64-msvc-1.10.6.tgz",
+ "integrity": "sha512-5B1vXosIIBw1m2Rcnw62IIfH7W9s9f7H7Ma0rRuhT8HR4Xh8QCgw6NJSI2S2MCngsGktYnAhyUvs81b7efTyQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1202,23 +1469,24 @@
}
},
"node_modules/@stylistic/eslint-plugin": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.1.0.tgz",
- "integrity": "sha512-bytbL7qiici7yPyEiId0fGPK9kjQbzcPMj2aftPfzTCyJ/CRSKdtI+iVjM0LSGzGxfunflI+MDDU9vyIIeIpoQ==",
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz",
+ "integrity": "sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/utils": "^8.23.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/types": "^8.56.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
"estraverse": "^5.3.0",
- "picomatch": "^4.0.2"
+ "picomatch": "^4.0.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
- "eslint": ">=9.0.0"
+ "eslint": "^9.0.0 || ^10.0.0"
}
},
"node_modules/@stylistic/eslint-plugin-js": {
@@ -1238,6 +1506,37 @@
"eslint": ">=9.0.0"
}
},
+ "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin/node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@textlint/ast-node-types": {
"version": "15.2.1",
"resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.2.1.tgz",
@@ -1375,10 +1674,28 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
+ "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/esrecurse": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
+ "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
- "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
@@ -1428,21 +1745,20 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.25.0.tgz",
- "integrity": "sha512-VM7bpzAe7JO/BFf40pIT1lJqS/z1F8OaSsUB3rpFJucQA4cOSuH2RVVVkFULN+En0Djgr29/jb4EQnedUo95KA==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz",
+ "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/type-utils": "8.25.0",
- "@typescript-eslint/utils": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.3.1",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/type-utils": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.0.1"
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1452,23 +1768,33 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "@typescript-eslint/parser": "^8.58.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.25.0.tgz",
- "integrity": "sha512-4gbs64bnbSzu4FpgMiQ1A+D+urxkoJk/kqlDJ2W//5SygaEiAP2B4GoS7TEdxgwol2el03gckFV9lJ4QOMiiHg==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz",
+ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/typescript-estree": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1478,19 +1804,41 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz",
+ "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.58.0",
+ "@typescript-eslint/types": "^8.58.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/scope-manager": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.25.0.tgz",
- "integrity": "sha512-6PPeiKIGbgStEyt4NNXa2ru5pMzQ8OYKO1hX1z53HMomrmiSB+R5FmChgQAP1ro8jMtNawz+TRQo/cSXrauTpg==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz",
+ "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0"
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1500,17 +1848,35 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz",
+ "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
"node_modules/@typescript-eslint/type-utils": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.25.0.tgz",
- "integrity": "sha512-d77dHgHWnxmXOPJuDWO4FDWADmGQkN5+tt6SFRZz/RtCWl4pHgFl3+WdYCn16+3teG09DY6XtEpf3gGD0a186g==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz",
+ "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.25.0",
- "@typescript-eslint/utils": "8.25.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.0.1"
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1520,14 +1886,14 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/types": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.25.0.tgz",
- "integrity": "sha512-+vUe0Zb4tkNgznQwicsvLUJgZIRs6ITeWSCclX1q85pR1iOiaj+4uZJIUp//Z27QWu5Cseiw3O3AR8hVpax7Aw==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz",
+ "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==",
"dev": true,
"license": "MIT",
"engines": {
@@ -1539,20 +1905,21 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.25.0.tgz",
- "integrity": "sha512-ZPaiAKEZ6Blt/TPAx5Ot0EIB/yGtLI2EsGoY6F7XKklfMxYQyvtL+gT/UCqkMzO0BVFHLDlzvFqQzurYahxv9Q==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz",
+ "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/visitor-keys": "8.25.0",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.0.1"
+ "@typescript-eslint/project-service": "8.58.0",
+ "@typescript-eslint/tsconfig-utils": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/visitor-keys": "8.58.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1562,20 +1929,20 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.8.0"
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.25.0.tgz",
- "integrity": "sha512-syqRbrEv0J1wywiLsK60XzHnQe/kRViI3zwFALrNEgnntn1l24Ra2KvOAWwWbWZ1lBZxZljPDGOq967dsl6fkA==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz",
+ "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.4.0",
- "@typescript-eslint/scope-manager": "8.25.0",
- "@typescript-eslint/types": "8.25.0",
- "@typescript-eslint/typescript-estree": "8.25.0"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.58.0",
+ "@typescript-eslint/types": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1585,19 +1952,19 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.25.0.tgz",
- "integrity": "sha512-kCYXKAum9CecGVHGij7muybDfTS2sD3t0L4bJsEZLkyrXUImiCTq1M3LG2SRtOhiHFwMR9wAFplpT6XHYjTkwQ==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz",
+ "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.25.0",
- "eslint-visitor-keys": "^4.2.0"
+ "@typescript-eslint/types": "8.58.0",
+ "eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -1607,6 +1974,19 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
"node_modules/@vscode/test-electron": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.4.1.tgz",
@@ -1625,17 +2005,17 @@
}
},
"node_modules/@vscode/vsce": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.6.0.tgz",
- "integrity": "sha512-u2ZoMfymRNJb14aHNawnXJtXHLXDVKc1oKZaH4VELKT/9iWKRVgtQOdwxCgtwSxJoqYvuK4hGlBWQJ05wxADhg==",
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz",
+ "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@azure/identity": "^4.1.0",
- "@secretlint/node": "^10.1.1",
- "@secretlint/secretlint-formatter-sarif": "^10.1.1",
- "@secretlint/secretlint-rule-no-dotenv": "^10.1.1",
- "@secretlint/secretlint-rule-preset-recommend": "^10.1.1",
+ "@secretlint/node": "^10.1.2",
+ "@secretlint/secretlint-formatter-sarif": "^10.1.2",
+ "@secretlint/secretlint-rule-no-dotenv": "^10.1.2",
+ "@secretlint/secretlint-rule-preset-recommend": "^10.1.2",
"@vscode/vsce-sign": "^2.0.0",
"azure-devops-node-api": "^12.5.0",
"chalk": "^4.1.2",
@@ -1652,7 +2032,7 @@
"minimatch": "^3.0.3",
"parse-semver": "^1.1.1",
"read": "^1.0.7",
- "secretlint": "^10.1.1",
+ "secretlint": "^10.1.2",
"semver": "^7.5.2",
"tmp": "^0.2.3",
"typed-rest-client": "^1.8.4",
@@ -1841,9 +2221,9 @@
}
},
"node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"dev": true,
"license": "MIT",
"bin": {
@@ -1874,9 +2254,9 @@
}
},
"node_modules/ajv": {
- "version": "6.12.6",
- "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
- "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2170,16 +2550,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/callsites": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
- "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2900,9 +3270,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2982,6 +3352,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/define-lazy-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
@@ -2995,6 +3383,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/delaunator": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
@@ -3291,33 +3697,30 @@
}
},
"node_modules/eslint": {
- "version": "9.21.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
- "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.0.tgz",
+ "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.12.1",
- "@eslint/config-array": "^0.19.2",
- "@eslint/core": "^0.12.0",
- "@eslint/eslintrc": "^3.3.0",
- "@eslint/js": "9.21.0",
- "@eslint/plugin-kit": "^0.2.7",
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@eslint/config-array": "^0.23.4",
+ "@eslint/config-helpers": "^0.5.4",
+ "@eslint/core": "^1.2.0",
+ "@eslint/plugin-kit": "^0.7.0",
"@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6",
- "@types/json-schema": "^7.0.15",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
+ "ajv": "^6.14.0",
"cross-spawn": "^7.0.6",
"debug": "^4.3.2",
"escape-string-regexp": "^4.0.0",
- "eslint-scope": "^8.2.0",
- "eslint-visitor-keys": "^4.2.0",
- "espree": "^10.3.0",
- "esquery": "^1.5.0",
+ "eslint-scope": "^9.1.2",
+ "eslint-visitor-keys": "^5.0.1",
+ "espree": "^11.2.0",
+ "esquery": "^1.7.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^8.0.0",
@@ -3327,8 +3730,7 @@
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
+ "minimatch": "^10.2.4",
"natural-compare": "^1.4.0",
"optionator": "^0.9.3"
},
@@ -3336,7 +3738,7 @@
"eslint": "bin/eslint.js"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://eslint.org/donate"
@@ -3351,13 +3753,16 @@
}
},
"node_modules/eslint-config-prettier": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz",
- "integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==",
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
"dev": true,
"license": "MIT",
"bin": {
- "eslint-config-prettier": "build/bin/cli.js"
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
},
"peerDependencies": {
"eslint": ">=7.0.0"
@@ -3367,6 +3772,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz",
"integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dev": true,
"funding": [
{
@@ -3386,17 +3792,19 @@
}
},
"node_modules/eslint-scope": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
- "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
+ "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
+ "@types/esrecurse": "^4.3.1",
+ "@types/estree": "^1.0.8",
"esrecurse": "^4.3.0",
"estraverse": "^5.2.0"
},
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -3415,17 +3823,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.13",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz",
- "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
"node_modules/eslint/node_modules/escape-string-regexp": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -3439,17 +3836,35 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/eslint/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
- "license": "ISC",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/espree": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
+ "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "acorn": "^8.16.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^5.0.1"
},
"engines": {
- "node": "*"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/espree": {
@@ -3485,9 +3900,9 @@
}
},
"node_modules/esquery": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
- "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -3639,6 +4054,24 @@
"pend": "~1.2.0"
}
},
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -3704,9 +4137,9 @@
"license": "ISC"
},
"node_modules/follow-redirects": {
- "version": "1.15.9",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
- "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"dev": true,
"funding": [
{
@@ -3884,56 +4317,21 @@
"node": ">=10.13.0"
}
},
- "node_modules/glob/node_modules/balanced-match": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
- "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/glob/node_modules/brace-expansion": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
- "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/glob/node_modules/minimatch": {
- "version": "10.2.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
- "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
},
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/globby": {
@@ -3987,13 +4385,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/graphemer": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
- "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4004,6 +4395,19 @@
"node": ">=8"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -4157,23 +4561,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/import-fresh": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
- "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "parent-module": "^1.0.0",
- "resolve-from": "^4.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
@@ -4314,6 +4701,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/is-it-type": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/is-it-type/-/is-it-type-5.1.3.tgz",
+ "integrity": "sha512-AX2uU0HW+TxagTgQXOJY7+2fbFHemC7YFBwN1XqD8qQMKdtfbOC8OC3fUb4s5NU59a3662Dzwto8tWDdZYRXxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "globalthis": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@@ -4650,9 +5050,9 @@
}
},
"node_modules/lodash": {
- "version": "4.17.23",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
- "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
@@ -4698,13 +5098,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/lodash.merge": {
- "version": "4.6.2",
- "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
- "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/lodash.once": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
@@ -4895,21 +5288,44 @@
}
},
"node_modules/minimatch": {
- "version": "9.0.9",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
- "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "version": "10.2.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^2.0.2"
+ "brace-expansion": "^5.0.5"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/minimatch/node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "5.0.5",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz",
+ "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -5065,6 +5481,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -5192,23 +5618,23 @@
}
},
"node_modules/ovsx": {
- "version": "0.10.1",
- "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.1.tgz",
- "integrity": "sha512-8i7+MJMMeq73m1zPEIClSFe17SNuuzU5br7G77ZIfOC24elB4pGQs0N1qRd+gnnbyhL5Qu96G21nFOVOBa2OBg==",
+ "version": "0.10.10",
+ "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.10.10.tgz",
+ "integrity": "sha512-/X5J4VLKPUGGaMynW9hgvsGg9jmwsK/3RhODeA2yzdeDbb8PUSNcg5GQ9aPDJW/znlqNvAwQcXAyE+Cq0RRvAQ==",
"dev": true,
"license": "EPL-2.0",
"dependencies": {
- "@vscode/vsce": "^3.2.1",
+ "@vscode/vsce": "^3.7.1",
"commander": "^6.2.1",
"follow-redirects": "^1.14.6",
"is-ci": "^2.0.0",
"leven": "^3.1.0",
"semver": "^7.6.0",
"tmp": "^0.2.3",
- "yauzl": "^3.1.3"
+ "yauzl-promise": "^4.0.0"
},
"bin": {
- "ovsx": "lib/ovsx"
+ "ovsx": "bin/ovsx"
},
"engines": {
"node": ">= 20"
@@ -5224,20 +5650,6 @@
"node": ">= 6"
}
},
- "node_modules/ovsx/node_modules/yauzl": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz",
- "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "pend": "~1.2.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -5297,19 +5709,6 @@
"dev": true,
"license": "(MIT AND Zlib)"
},
- "node_modules/parent-module": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
- "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "callsites": "^3.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/parse-json": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz",
@@ -5524,9 +5923,9 @@
}
},
"node_modules/prettier": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
- "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
"dev": true,
"license": "MIT",
"bin": {
@@ -5744,16 +6143,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/resolve-from": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
- "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/restore-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
@@ -5895,9 +6284,9 @@
}
},
"node_modules/semver": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
- "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -6074,6 +6463,16 @@
"simple-concat": "^1.0.0"
}
},
+ "node_modules/simple-invariant": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/simple-invariant/-/simple-invariant-2.0.1.tgz",
+ "integrity": "sha512-1sbhsxqI+I2tqlmjbz99GXNmZtr6tKIyEgGGnJw/MKGblalqk/XoOYYFJlBzTKZCxx8kLaD3FD5s9BEEjx5Pyg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/slash": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz",
@@ -6296,19 +6695,6 @@
"node": ">=8"
}
},
- "node_modules/strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/structured-source": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz",
@@ -6562,6 +6948,23 @@
"url": "https://bevry.me/fund"
}
},
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
"node_modules/tmp": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz",
@@ -6586,9 +6989,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
- "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
+ "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6668,9 +7071,9 @@
}
},
"node_modules/typescript": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
- "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
+ "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -6682,15 +7085,16 @@
}
},
"node_modules/typescript-eslint": {
- "version": "8.25.0",
- "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.25.0.tgz",
- "integrity": "sha512-TxRdQQLH4g7JkoFlYG3caW5v1S6kEkz8rqt80iQJZUYPq1zD1Ra7HfQBJJ88ABRaMvHAXnwRvRB4V+6sQ9xN5Q==",
+ "version": "8.58.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz",
+ "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/eslint-plugin": "8.25.0",
- "@typescript-eslint/parser": "8.25.0",
- "@typescript-eslint/utils": "8.25.0"
+ "@typescript-eslint/eslint-plugin": "8.58.0",
+ "@typescript-eslint/parser": "8.58.0",
+ "@typescript-eslint/typescript-estree": "8.58.0",
+ "@typescript-eslint/utils": "8.58.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6700,8 +7104,8 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.8.0"
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/uc.micro": {
@@ -7138,6 +7542,21 @@
"fd-slicer": "~1.1.0"
}
},
+ "node_modules/yauzl-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yauzl-promise/-/yauzl-promise-4.0.0.tgz",
+ "integrity": "sha512-/HCXpyHXJQQHvFq9noqrjfa/WpQC2XYs3vI7tBiAi4QiIU1knvYhZGaO1QPjwIVMdqflxbmwgMXtYeaRiAE0CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@node-rs/crc32": "^1.7.0",
+ "is-it-type": "^5.1.2",
+ "simple-invariant": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/yazl": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
diff --git a/editors/code/package.json b/editors/code/package.json
index 1dd513c9de..67570cd067 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -31,6 +31,9 @@
"vscode": "^1.93.0"
},
"enabledApiProposals": [],
+ "extensionKind": [
+ "workspace"
+ ],
"scripts": {
"vscode:prepublish": "npm run build-base -- --minify",
"package": "vsce package -o rust-analyzer.vsix",
@@ -54,8 +57,8 @@
"vscode-languageclient": "^9.0.1"
},
"devDependencies": {
- "@eslint/js": "^9.21.0",
- "@stylistic/eslint-plugin": "^4.1.0",
+ "@eslint/js": "^10.0.1",
+ "@stylistic/eslint-plugin": "^5.10.0",
"@stylistic/eslint-plugin-js": "^4.1.0",
"@tsconfig/strictest": "^2.0.5",
"@types/lodash": "^4.17.20",
@@ -64,16 +67,16 @@
"@typescript-eslint/eslint-plugin": "^8.25.0",
"@typescript-eslint/parser": "^8.25.0",
"@vscode/test-electron": "^2.4.1",
- "@vscode/vsce": "^3.6.0",
+ "@vscode/vsce": "^3.7.1",
"esbuild": "^0.25.0",
- "eslint": "^9.21.0",
- "eslint-config-prettier": "^10.0.2",
+ "eslint": "^10.2.0",
+ "eslint-config-prettier": "^10.1.8",
"eslint-define-config": "^2.1.0",
- "ovsx": "0.10.1",
- "prettier": "^3.5.2",
+ "ovsx": "0.10.10",
+ "prettier": "^3.8.1",
"tslib": "^2.8.1",
- "typescript": "^5.7.3",
- "typescript-eslint": "^8.25.0"
+ "typescript": "^6.0.2",
+ "typescript-eslint": "^8.58.0"
},
"activationEvents": [
"workspaceContains:Cargo.toml",
@@ -993,6 +996,19 @@
{
"title": "Cargo",
"properties": {
+ "rust-analyzer.cargo.metadataExtraArgs": {
+ "markdownDescription": "Extra arguments passed only to `cargo metadata`, not to other cargo invocations.\nUseful for flags like `--config` that `cargo metadata` supports.",
+ "default": [],
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "title": "Cargo",
+ "properties": {
"rust-analyzer.cargo.noDefaultFeatures": {
"markdownDescription": "Whether to pass `--no-default-features` to cargo.",
"default": false,
@@ -1592,6 +1608,16 @@
}
},
{
+ "title": "rust-analyzer",
+ "properties": {
+ "rust-analyzer.disableFixtureSupport": {
+ "markdownDescription": "Disable support for `#[rust_analyzer::rust_fixture]` snippets.\n\nIf you are not working on rust-analyzer itself, you should ignore this config.",
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ {
"title": "Document",
"properties": {
"rust-analyzer.document.symbol.search.excludeLocals": {
@@ -2502,6 +2528,24 @@
}
},
{
+ "title": "Inlay Hints",
+ "properties": {
+ "rust-analyzer.inlayHints.typeHints.location": {
+ "markdownDescription": "Where to render type hints relative to their binding pattern.",
+ "default": "inline",
+ "type": "string",
+ "enum": [
+ "inline",
+ "end_of_line"
+ ],
+ "enumDescriptions": [
+ "Render type hints directly after the binding identifier.",
+ "Render type hints after the end of the containing `let` statement when possible."
+ ]
+ }
+ }
+ },
+ {
"title": "Interpret",
"properties": {
"rust-analyzer.interpret.tests": {
@@ -2865,7 +2909,7 @@
"title": "Runnables",
"properties": {
"rust-analyzer.runnables.bench.overrideCommand": {
- "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
+ "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::bench_func`.\n- `${exact}`: `--exact` for single benchmarks, empty for modules.\n- `${include_ignored}`: always empty for benchmarks.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
"default": null,
"type": [
"null",
@@ -2894,7 +2938,7 @@
"title": "Runnables",
"properties": {
"rust-analyzer.runnables.doctest.overrideCommand": {
- "markdownDescription": "Override the command used for bench runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
+ "markdownDescription": "Override the command used for doc-test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::func`.\n- `${exact}`: always empty for doc-tests.\n- `${include_ignored}`: always empty for doc-tests.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
"default": null,
"type": [
"null",
@@ -2948,7 +2992,7 @@
"title": "Runnables",
"properties": {
"rust-analyzer.runnables.test.overrideCommand": {
- "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nUse the placeholders `${package}`, `${target_arg}`, `${target}`, `${executable_args}` to dynamically\nreplace the package name, target option (such as `--bin` or `--example`), the target name and\nthe arguments passed to test binary args (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
+ "markdownDescription": "Override the command used for test runnables.\nThe first element of the array should be the program to execute (for example, `cargo`).\n\nAvailable placeholders:\n- `${package}`: package name.\n- `${target_arg}`: target option such as `--bin`, `--test`, `--lib`, etc.\n- `${target}`: target name (empty for `--lib`).\n- `${test_name}`: the test path filter, e.g. `module::test_func`.\n- `${exact}`: `--exact` for single tests, empty for modules.\n- `${include_ignored}`: `--include-ignored` for single tests, empty otherwise.\n- `${executable_args}`: all of the above binary args bundled together\n (includes `rust-analyzer.runnables.extraTestBinaryArgs`).",
"default": null,
"type": [
"null",
@@ -3252,6 +3296,13 @@
}
],
"configurationDefaults": {
+ "[rust]": {
+ "editor.quickSuggestions": {
+ "other": true,
+ "comments": false,
+ "strings": true
+ }
+ },
"explorer.fileNesting.patterns": {
"Cargo.toml": "Cargo.lock"
}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 5b358e3211..e265cff391 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -1,4 +1,4 @@
-import * as anser from "anser";
+import anser from "anser";
import * as lc from "vscode-languageclient/node";
import * as vscode from "vscode";
import * as ra from "../src/lsp_ext";
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index c1b6f31030..302f51dee4 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -1194,9 +1194,8 @@ export function runSingle(ctx: CtxInit): Cmd {
}
export function copyRunCommandLine(ctx: CtxInit) {
- let prevRunnable: RunnableQuickPick | undefined;
return async () => {
- const item = await selectRunnable(ctx, prevRunnable);
+ const item = await selectRunnable(ctx, undefined);
if (!item || !isCargoRunnableArgs(item.runnable.args)) return;
const args = createCargoArgs(item.runnable.args);
const commandLine = ["cargo", ...args].join(" ");
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 5dc2c419ef..d65f011c75 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -485,7 +485,7 @@ export function substituteVariablesInEnv(env: Env): Env {
Object.entries(env).map(([key, value]) => {
const deps = new Set<string>();
if (value) {
- let match = undefined;
+ let match;
while ((match = depRe.exec(value))) {
const depName = unwrapUndefinable(match.groups?.["depName"]);
deps.add(depName);
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts
index 24f8d90873..9bc3adad2f 100644
--- a/editors/code/src/debug.ts
+++ b/editors/code/src/debug.ts
@@ -48,7 +48,7 @@ export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<
}
export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> {
- let debugConfig: vscode.DebugConfiguration | undefined = undefined;
+ let debugConfig: vscode.DebugConfiguration | undefined;
let message = "";
const wsLaunchSection = vscode.workspace.getConfiguration("launch");
diff --git a/editors/code/src/dependencies_provider.ts b/editors/code/src/dependencies_provider.ts
index 203ef5cc85..3c04f2ef64 100644
--- a/editors/code/src/dependencies_provider.ts
+++ b/editors/code/src/dependencies_provider.ts
@@ -6,9 +6,9 @@ import * as ra from "./lsp_ext";
import type { FetchDependencyListResult } from "./lsp_ext";
import { unwrapUndefinable } from "./util";
-export class RustDependenciesProvider
- implements vscode.TreeDataProvider<Dependency | DependencyFile>
-{
+export class RustDependenciesProvider implements vscode.TreeDataProvider<
+ Dependency | DependencyFile
+> {
dependenciesMap: { [id: string]: Dependency | DependencyFile };
ctx: CtxInit;
diff --git a/editors/code/src/diagnostics.ts b/editors/code/src/diagnostics.ts
index cd0e43b212..32a41745ed 100644
--- a/editors/code/src/diagnostics.ts
+++ b/editors/code/src/diagnostics.ts
@@ -1,4 +1,4 @@
-import * as anser from "anser";
+import anser from "anser";
import * as vscode from "vscode";
import {
type ProviderResult,
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 9712bd4b7b..cf190ea3ce 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -300,14 +300,14 @@ export type SsrParams = {
};
export type RecursiveMemoryLayoutNode = {
- item_name: string;
+ itemName: string;
typename: string;
size: number;
alignment: number;
offset: number;
- parent_idx: number;
- children_start: number;
- children_len: number;
+ parentIdx: number;
+ childrenStart: number;
+ childrenLen: number;
};
export type RecursiveMemoryLayout = {
nodes: RecursiveMemoryLayoutNode[];
diff --git a/editors/code/src/snippets.ts b/editors/code/src/snippets.ts
index a469a9cd1f..6d75428eaa 100644
--- a/editors/code/src/snippets.ts
+++ b/editors/code/src/snippets.ts
@@ -53,7 +53,7 @@ export async function applySnippetTextEdits(editor: vscode.TextEditor, edits: vs
}
function hasSnippet(snip: string): boolean {
- const m = snip.match(/\$\d+|\{\d+:[^}]*\}/);
+ const m = snip.match(/\$\d+|\$\{\d+:[^}]*\}/);
return m != null;
}
diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts
index 06f75a8f8d..76946d1510 100644
--- a/editors/code/src/toolchain.ts
+++ b/editors/code/src/toolchain.ts
@@ -100,7 +100,7 @@ export class Cargo {
);
} catch (err) {
log.error(`Cargo invocation has failed: ${err}`);
- throw new Error(`Cargo invocation has failed: ${err}`);
+ throw new Error(`Cargo invocation has failed: ${err}`, { cause: err });
}
return spec.filter?.(artifacts) ?? artifacts;
diff --git a/editors/code/tsconfig.json b/editors/code/tsconfig.json
index a13afab170..380acec59d 100644
--- a/editors/code/tsconfig.json
+++ b/editors/code/tsconfig.json
@@ -1,7 +1,6 @@
{
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
- "esModuleInterop": false,
"module": "NodeNext",
"moduleResolution": "nodenext",
"target": "ES2024",
diff --git a/lib/smol_str/Cargo.toml b/lib/smol_str/Cargo.toml
index 22068fe841..719462bc9a 100644
--- a/lib/smol_str/Cargo.toml
+++ b/lib/smol_str/Cargo.toml
@@ -22,7 +22,7 @@ proptest = "1.5"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
criterion = "0.7"
-rand = "0.9.2"
+rand = "0.9.3"
[features]
default = ["std"]
diff --git a/rust-version b/rust-version
index 68f38716db..e9fc6c4cd0 100644
--- a/rust-version
+++ b/rust-version
@@ -1 +1 @@
-1174f784096deb8e4ba93f7e4b5ccb7bb4ba2c55
+e22c616e4e87914135c1db261a03e0437255335e
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs
index bc7eb88f3a..482b31e306 100644
--- a/xtask/src/codegen.rs
+++ b/xtask/src/codegen.rs
@@ -130,16 +130,17 @@ impl fmt::Display for Location {
fn reformat(text: String) -> String {
let sh = Shell::new().unwrap();
let rustfmt_toml = project_root().join("rustfmt.toml");
- let version = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap_or_default();
+ let toolchain = &std::env::var("RUSTFMT_TOOLCHAIN").unwrap_or("stable".to_owned());
+ let version = cmd!(sh, "rustup run {toolchain} rustfmt --version").read().unwrap_or_default();
// First try explicitly requesting the stable channel via rustup in case nightly is being used by default,
// then plain rustfmt in case rustup isn't being used to manage the compiler (e.g. when using Nix).
- let mut stdout = if !version.contains("stable") {
+ let mut stdout = if !version.contains(toolchain) {
let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default();
- if !version.contains("stable") {
+ if !version.contains(toolchain) {
panic!(
- "Failed to run rustfmt from toolchain 'stable'. \
- Please run `rustup component add rustfmt --toolchain stable` to install it.",
+ "Failed to run rustfmt from toolchain '{toolchain}'. \
+ Please run `rustup component add rustfmt --toolchain {toolchain}` to install it.",
);
} else {
cmd!(sh, "rustfmt --config-path {rustfmt_toml} --config fn_single_line=true")
@@ -150,7 +151,7 @@ fn reformat(text: String) -> String {
} else {
cmd!(
sh,
- "rustup run stable rustfmt --config-path {rustfmt_toml} --config fn_single_line=true"
+ "rustup run {toolchain} rustfmt --config-path {rustfmt_toml} --config fn_single_line=true"
)
.stdin(text)
.read()
diff --git a/xtask/src/codegen/grammar.rs b/xtask/src/codegen/grammar.rs
index 4e980bb3d9..257429c426 100644
--- a/xtask/src/codegen/grammar.rs
+++ b/xtask/src/codegen/grammar.rs
@@ -780,7 +780,7 @@ impl Field {
}
fn token_kind(&self) -> Option<proc_macro2::TokenStream> {
match self {
- Field::Token(token) => {
+ Field::Token { token, .. } => {
let token: proc_macro2::TokenStream = token.parse().unwrap();
Some(quote! { T![#token] })
}
@@ -789,8 +789,11 @@ impl Field {
}
fn method_name(&self) -> String {
match self {
- Field::Token(name) => {
- let name = match name.as_str() {
+ Field::Token { name, token, .. } => {
+ if let Some(name) = name {
+ return name.clone();
+ }
+ let name = match token.as_str() {
";" => "semicolon",
"->" => "thin_arrow",
"'{'" => "l_curly",
@@ -820,7 +823,7 @@ impl Field {
"," => "comma",
"|" => "pipe",
"~" => "tilde",
- _ => name,
+ _ => token,
};
format!("{name}_token",)
}
@@ -835,7 +838,7 @@ impl Field {
}
fn ty(&self) -> proc_macro2::Ident {
match self {
- Field::Token(_) => format_ident!("SyntaxToken"),
+ Field::Token { .. } => format_ident!("SyntaxToken"),
Field::Node { ty, .. } => format_ident!("{}", ty),
}
}
@@ -885,7 +888,7 @@ fn lower(grammar: &Grammar) -> AstSrc {
res.nodes.iter_mut().for_each(|it| {
it.traits.sort();
it.fields.sort_by_key(|it| match it {
- Field::Token(name) => (true, name.clone()),
+ Field::Token { token, .. } => (true, token.clone()),
Field::Node { name, .. } => (false, name.clone()),
});
});
@@ -925,12 +928,11 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
acc.push(field);
}
Rule::Token(token) => {
- assert!(label.is_none());
- let mut name = clean_token_name(&grammar[*token].name);
- if "[]{}()".contains(&name) {
- name = format!("'{name}'");
+ let mut token = clean_token_name(&grammar[*token].name);
+ if "[]{}()".contains(&token) {
+ token = format!("'{token}'");
}
- let field = Field::Token(name);
+ let field = Field::Token { name: label.cloned(), token };
acc.push(field);
}
Rule::Rep(inner) => {
@@ -1018,8 +1020,8 @@ fn lower_separated_list(
}
match nt {
Either::Right(token) => {
- let name = clean_token_name(&grammar[*token].name);
- let field = Field::Token(name);
+ let token = clean_token_name(&grammar[*token].name);
+ let field = Field::Token { token, name: None };
acc.push(field);
}
Either::Left(node) => {
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index 564d9cc24e..a0abdf09d3 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -111,8 +111,19 @@ const RESERVED: &[&str] = &[
];
// keywords that are keywords only in specific parse contexts
#[doc(alias = "WEAK_KEYWORDS")]
-const CONTEXTUAL_KEYWORDS: &[&str] =
- &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"];
+const CONTEXTUAL_KEYWORDS: &[&str] = &[
+ "macro_rules",
+ "union",
+ "default",
+ "raw",
+ "dyn",
+ "auto",
+ "yeet",
+ "safe",
+ "bikeshed",
+ "cfg_attr",
+ "cfg",
+];
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",
@@ -261,7 +272,7 @@ pub(crate) struct AstNodeSrc {
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum Field {
- Token(String),
+ Token { name: Option<String>, token: String },
Node { name: String, ty: String, cardinality: Cardinality },
}
diff --git a/xtask/src/codegen/lints.rs b/xtask/src/codegen/lints.rs
index 3b4c2e8da3..c09092e74e 100644
--- a/xtask/src/codegen/lints.rs
+++ b/xtask/src/codegen/lints.rs
@@ -3,7 +3,7 @@
#![allow(clippy::disallowed_types)]
use std::{
- collections::{HashMap, hash_map},
+ collections::{HashMap, HashSet, hash_map},
fs,
path::Path,
str::FromStr,
@@ -83,7 +83,7 @@ pub struct LintGroup {
let lints_json = project_root().join("./target/clippy_lints.json");
cmd!(
sh,
- "curl https://rust-lang.github.io/rust-clippy/stable/lints.json --output {lints_json}"
+ "curl -f https://raw.githubusercontent.com/rust-lang/rust-clippy/21fd71e3fe6eb063cfb619ecc37b1023f5283894/beta/lints.json --output {lints_json}"
)
.run()
.unwrap();
@@ -348,10 +348,12 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) {
buf.push_str(r#"pub const DEFAULT_LINTS: &[Lint] = &["#);
buf.push('\n');
+ let mut known_lints: HashSet<&String> = HashSet::with_capacity(lints.len() + lint_groups.len());
for (name, lint) in &lints {
push_lint_completion(buf, name, lint);
+ known_lints.insert(name);
}
- for (name, (group, _)) in &lint_groups {
+ for (name, (group, _)) in lint_groups.iter().filter(|(name, _)| !known_lints.contains(name)) {
push_lint_completion(buf, name, group);
}
buf.push_str("];\n\n");
@@ -372,10 +374,15 @@ fn generate_lint_descriptor(sh: &Shell, buf: &mut String) {
buf.push_str(r#"pub const RUSTDOC_LINTS: &[Lint] = &["#);
buf.push('\n');
+ let mut known_rustdoc_lints: HashSet<&String> =
+ HashSet::with_capacity(lints_rustdoc.len() + lint_groups_rustdoc.len());
for (name, lint) in &lints_rustdoc {
push_lint_completion(buf, name, lint);
+ known_rustdoc_lints.insert(name);
}
- for (name, (group, _)) in &lint_groups_rustdoc {
+ for (name, (group, _)) in
+ lint_groups_rustdoc.iter().filter(|(name, _)| !known_rustdoc_lints.contains(name))
+ {
push_lint_completion(buf, name, group);
}
buf.push_str("];\n\n");
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 05528505f2..122cf4887e 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -100,6 +100,10 @@ fn check_cargo_toml(path: &Path, text: String) {
if !text.contains("path=") {
continue;
}
+ #[expect(
+ clippy::collapsible_match,
+ reason = "this changes meaning, as `dev-dependencies` includes `dependencies`"
+ )]
match section {
Some(s) if s.contains("dev-dependencies") => {
if text.contains("version") {