summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:44 +0000
commitc23a457e72abe608715ac76f076f47dc42af07a5 (patch)
tree2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /src/tools/rust-analyzer/crates
parentReleasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz
rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates')
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs14
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs6
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs26
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs67
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr.rs187
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs37
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs476
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs89
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs184
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/generics.rs85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs502
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/import_map.rs78
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs477
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs91
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs44
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lower.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs78
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs61
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs74
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs438
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs85
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs52
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/path.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs82
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/pretty.rs72
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs230
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/src.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs170
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs25
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs53
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs67
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs101
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs132
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs121
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs145
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs98
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs116
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs246
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs219
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs60
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs104
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/attrs.rs260
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs68
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs133
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs477
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs40
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs105
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs190
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs159
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs93
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs166
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs37
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs205
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs43
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs64
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs57
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs71
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs87
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs145
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/item.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs294
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/documentation.rs281
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/helpers.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs44
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs169
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt203
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs108
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs101
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/annotations.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs63
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/expand_macro.rs47
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/extend_selection.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs54
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs127
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs32
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/rename.rs29
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/signature_help.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html58
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/typing.rs248
-rw-r--r--src/tools/rust-analyzer/crates/intern/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/intern/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs88
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs1
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/shortcuts.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs19
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast6
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast90
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast15
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast17
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast9
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast105
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs5
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-test/build.rs4
-rw-r--r--src/tools/rust-analyzer/crates/profile/src/stop_watch.rs20
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs91
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs11
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs100
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt8
-rw-r--r--src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt44
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs20
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs37
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs42
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs61
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs23
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs155
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs30
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs29
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs)0
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs)146
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs (renamed from src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs)4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs173
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs165
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram24
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs171
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs19
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs12
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs19
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs20
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs103
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/lib.rs5
262 files changed, 9281 insertions, 3899 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
index 323ee4260..3f5ccb621 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -130,6 +130,7 @@ impl ChangeFixture {
let mut default_crate_root: Option<FileId> = None;
let mut default_target_data_layout: Option<String> = None;
let mut default_cfg = CfgOptions::default();
+ let mut default_env = Env::new_for_test_fixture();
let mut file_set = FileSet::default();
let mut current_source_root_kind = SourceRootKind::Local;
@@ -178,8 +179,8 @@ impl ChangeFixture {
meta.edition,
Some(crate_name.clone().into()),
version,
- meta.cfg,
- Default::default(),
+ meta.cfg.clone(),
+ Some(meta.cfg),
meta.env,
false,
origin,
@@ -199,7 +200,8 @@ impl ChangeFixture {
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
- default_cfg = meta.cfg;
+ default_cfg.extend(meta.cfg.into_iter());
+ default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout;
}
@@ -218,9 +220,9 @@ impl ChangeFixture {
Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()),
None,
- default_cfg,
- Default::default(),
- Env::new_for_test_fixture(),
+ default_cfg.clone(),
+ Some(default_cfg),
+ default_env,
false,
CrateOrigin::Local { repo: None, name: None },
default_target_data_layout
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index c47799f13..b75c7079b 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -686,6 +686,12 @@ impl fmt::Display for Edition {
}
}
+impl Extend<(String, String)> for Env {
+ fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
+ self.entries.extend(iter);
+ }
+}
+
impl FromIterator<(String, String)> for Env {
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
Env { entries: FromIterator::from_iter(iter) }
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index 183b9b7d2..0aeb0b050 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -86,6 +86,32 @@ impl CfgOptions {
}
}
+impl Extend<CfgAtom> for CfgOptions {
+ fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
+ iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag));
+ }
+}
+
+impl IntoIterator for CfgOptions {
+ type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;
+
+ type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ <FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
+ }
+}
+
+impl<'a> IntoIterator for &'a CfgOptions {
+ type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;
+
+ type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ <&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
+ }
+}
+
#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index fbb943ccb..2de719af9 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -5,7 +5,9 @@
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
use std::{
+ ffi::OsString,
fmt, io,
+ path::PathBuf,
process::{ChildStderr, ChildStdout, Command, Stdio},
time::Duration,
};
@@ -168,7 +170,7 @@ struct FlycheckActor {
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
- cargo_handle: Option<CargoHandle>,
+ command_handle: Option<CommandHandle>,
}
enum Event {
@@ -184,7 +186,7 @@ impl FlycheckActor {
workspace_root: AbsPathBuf,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
- FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
+ FlycheckActor { id, sender, config, root: workspace_root, command_handle: None }
}
fn report_progress(&self, progress: Progress) {
@@ -192,7 +194,7 @@ impl FlycheckActor {
}
fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
- let check_chan = self.cargo_handle.as_ref().map(|cargo| &cargo.receiver);
+ let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
if let Ok(msg) = inbox.try_recv() {
// give restarts a preference so check outputs don't block a restart or stop
return Some(Event::RequestStateChange(msg));
@@ -221,21 +223,19 @@ impl FlycheckActor {
}
let command = self.check_command();
+ let formatted_command = format!("{:?}", command);
+
tracing::debug!(?command, "will restart flycheck");
- match CargoHandle::spawn(command) {
- Ok(cargo_handle) => {
- tracing::debug!(
- command = ?self.check_command(),
- "did restart flycheck"
- );
- self.cargo_handle = Some(cargo_handle);
+ match CommandHandle::spawn(command) {
+ Ok(command_handle) => {
+ tracing::debug!(command = formatted_command, "did restart flycheck");
+ self.command_handle = Some(command_handle);
self.report_progress(Progress::DidStart);
}
Err(error) => {
self.report_progress(Progress::DidFailToRestart(format!(
- "Failed to run the following command: {:?} error={}",
- self.check_command(),
- error
+ "Failed to run the following command: {} error={}",
+ formatted_command, error
)));
}
}
@@ -244,12 +244,14 @@ impl FlycheckActor {
tracing::debug!(flycheck_id = self.id, "flycheck finished");
// Watcher finished
- let cargo_handle = self.cargo_handle.take().unwrap();
- let res = cargo_handle.join();
+ let command_handle = self.command_handle.take().unwrap();
+ let formatted_handle = format!("{:?}", command_handle);
+
+ let res = command_handle.join();
if res.is_err() {
tracing::error!(
- "Flycheck failed to run the following command: {:?}",
- self.check_command()
+ "Flycheck failed to run the following command: {}",
+ formatted_handle
);
}
self.report_progress(Progress::DidFinish(res));
@@ -284,12 +286,12 @@ impl FlycheckActor {
}
fn cancel_check_process(&mut self) {
- if let Some(cargo_handle) = self.cargo_handle.take() {
+ if let Some(command_handle) = self.command_handle.take() {
tracing::debug!(
- command = ?self.check_command(),
+ command = ?command_handle,
"did cancel flycheck"
);
- cargo_handle.cancel();
+ command_handle.cancel();
self.report_progress(Progress::DidCancel);
}
}
@@ -391,19 +393,36 @@ impl Drop for JodGroupChild {
}
/// A handle to a cargo process used for fly-checking.
-struct CargoHandle {
+struct CommandHandle {
/// The handle to the actual cargo process. As we cannot cancel directly from with
/// a read syscall dropping and therefore terminating the process is our best option.
child: JodGroupChild,
thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
receiver: Receiver<CargoMessage>,
+ program: OsString,
+ arguments: Vec<OsString>,
+ current_dir: Option<PathBuf>,
}
-impl CargoHandle {
- fn spawn(mut command: Command) -> std::io::Result<CargoHandle> {
+impl fmt::Debug for CommandHandle {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CommandHandle")
+ .field("program", &self.program)
+ .field("arguments", &self.arguments)
+ .field("current_dir", &self.current_dir)
+ .finish()
+ }
+}
+
+impl CommandHandle {
+ fn spawn(mut command: Command) -> std::io::Result<CommandHandle> {
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
let mut child = command.group_spawn().map(JodGroupChild)?;
+ let program = command.get_program().into();
+ let arguments = command.get_args().map(|arg| arg.into()).collect::<Vec<OsString>>();
+ let current_dir = command.get_current_dir().map(|arg| arg.to_path_buf());
+
let stdout = child.0.inner().stdout.take().unwrap();
let stderr = child.0.inner().stderr.take().unwrap();
@@ -413,7 +432,7 @@ impl CargoHandle {
.name("CargoHandle".to_owned())
.spawn(move || actor.run())
.expect("failed to spawn thread");
- Ok(CargoHandle { child, thread, receiver })
+ Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
}
fn cancel(mut self) {
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index 30307deb7..8cf61ee04 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -31,8 +31,10 @@ smallvec.workspace = true
hashbrown.workspace = true
triomphe.workspace = true
-rustc_abi = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_abi", default-features = false }
-rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
+rustc_abi.workspace = true
+rustc_index.workspace = true
+rustc_parse_format.workspace = true
+
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
index fae071118..c6454eb9e 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr.rs
@@ -5,7 +5,7 @@ pub mod builtin;
#[cfg(test)]
mod tests;
-use std::{hash::Hash, ops};
+use std::{hash::Hash, ops, slice::Iter as SliceIter};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -14,12 +14,11 @@ use hir_expand::{
attrs::{collect_attrs, Attr, AttrId, RawAttrs},
HirFileId, InFile,
};
-use itertools::Itertools;
use la_arena::{ArenaMap, Idx, RawIdx};
use mbe::DelimiterKind;
use syntax::{
- ast::{self, HasAttrs, IsString},
- AstPtr, AstToken, SmolStr, TextRange, TextSize,
+ ast::{self, HasAttrs},
+ AstPtr, SmolStr,
};
use triomphe::Arc;
@@ -33,26 +32,6 @@ use crate::{
LocalFieldId, Lookup, MacroId, VariantId,
};
-/// Holds documentation
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Documentation(String);
-
-impl Documentation {
- pub fn new(s: String) -> Self {
- Documentation(s)
- }
-
- pub fn as_str(&self) -> &str {
- &self.0
- }
-}
-
-impl From<Documentation> for String {
- fn from(Documentation(string): Documentation) -> Self {
- string
- }
-}
-
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Attrs(RawAttrs);
@@ -221,33 +200,6 @@ impl Attrs {
self.by_key("lang").string_value().and_then(|it| LangItem::from_str(it))
}
- pub fn docs(&self) -> Option<Documentation> {
- let docs = self.by_key("doc").attrs().filter_map(|attr| attr.string_value());
- let indent = doc_indent(self);
- let mut buf = String::new();
- for doc in docs {
- // str::lines doesn't yield anything for the empty string
- if !doc.is_empty() {
- buf.extend(Itertools::intersperse(
- doc.lines().map(|line| {
- line.char_indices()
- .nth(indent)
- .map_or(line, |(offset, _)| &line[offset..])
- .trim_end()
- }),
- "\n",
- ));
- }
- buf.push('\n');
- }
- buf.pop();
- if buf.is_empty() {
- None
- } else {
- Some(Documentation(buf))
- }
- }
-
pub fn has_doc_hidden(&self) -> bool {
self.by_key("doc").tt_values().any(|tt| {
tt.delimiter.kind == DelimiterKind::Parenthesis &&
@@ -299,7 +251,6 @@ impl Attrs {
}
}
-use std::slice::Iter as SliceIter;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum DocAtom {
/// eg. `#[doc(hidden)]`
@@ -313,7 +264,6 @@ pub enum DocAtom {
// Adapted from `CfgExpr` parsing code
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
pub enum DocExpr {
Invalid,
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
@@ -431,12 +381,10 @@ impl AttrsWithOwner {
.item_tree(db)
.raw_attrs(AttrOwner::ModItem(definition_tree_id.value.into()))
.clone(),
- ModuleOrigin::BlockExpr { block } => RawAttrs::from_attrs_owner(
- db.upcast(),
- InFile::new(block.file_id, block.to_node(db.upcast()))
- .as_ref()
- .map(|it| it as &dyn ast::HasAttrs),
- ),
+ ModuleOrigin::BlockExpr { id, .. } => {
+ let tree = db.block_item_tree_query(id);
+ tree.raw_attrs(AttrOwner::TopLevel).clone()
+ }
}
}
AttrDefId::FieldId(it) => {
@@ -576,62 +524,6 @@ impl AttrsWithOwner {
AttrSourceMap::new(owner.as_ref().map(|node| node as &dyn HasAttrs))
}
-
- pub fn docs_with_rangemap(
- &self,
- db: &dyn DefDatabase,
- ) -> Option<(Documentation, DocsRangeMap)> {
- let docs =
- self.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
- let indent = doc_indent(self);
- let mut buf = String::new();
- let mut mapping = Vec::new();
- for (doc, idx) in docs {
- if !doc.is_empty() {
- let mut base_offset = 0;
- for raw_line in doc.split('\n') {
- let line = raw_line.trim_end();
- let line_len = line.len();
- let (offset, line) = match line.char_indices().nth(indent) {
- Some((offset, _)) => (offset, &line[offset..]),
- None => (0, line),
- };
- let buf_offset = buf.len();
- buf.push_str(line);
- mapping.push((
- TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
- idx,
- TextRange::at(
- (base_offset + offset).try_into().ok()?,
- line_len.try_into().ok()?,
- ),
- ));
- buf.push('\n');
- base_offset += raw_line.len() + 1;
- }
- } else {
- buf.push('\n');
- }
- }
- buf.pop();
- if buf.is_empty() {
- None
- } else {
- Some((Documentation(buf), DocsRangeMap { mapping, source_map: self.source_map(db) }))
- }
- }
-}
-
-fn doc_indent(attrs: &Attrs) -> usize {
- attrs
- .by_key("doc")
- .attrs()
- .filter_map(|attr| attr.string_value())
- .flat_map(|s| s.lines())
- .filter(|line| !line.chars().all(|c| c.is_whitespace()))
- .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
- .min()
- .unwrap_or(0)
}
#[derive(Debug)]
@@ -675,7 +567,7 @@ impl AttrSourceMap {
self.source_of_id(attr.id)
}
- fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
+ pub fn source_of_id(&self, id: AttrId) -> InFile<&Either<ast::Attr, ast::Comment>> {
let ast_idx = id.ast_index();
let file_id = match self.mod_def_site_file_id {
Some((file_id, def_site_cut)) if def_site_cut <= ast_idx => file_id,
@@ -689,69 +581,6 @@ impl AttrSourceMap {
}
}
-/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
-#[derive(Debug)]
-pub struct DocsRangeMap {
- source_map: AttrSourceMap,
- // (docstring-line-range, attr_index, attr-string-range)
- // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
- // the original (untrimmed) syntax doc line
- mapping: Vec<(TextRange, AttrId, TextRange)>,
-}
-
-impl DocsRangeMap {
- /// Maps a [`TextRange`] relative to the documentation string back to its AST range
- pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
- let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
- let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
- if !line_docs_range.contains_range(range) {
- return None;
- }
-
- let relative_range = range - line_docs_range.start();
-
- let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
- match source {
- Either::Left(attr) => {
- let string = get_doc_string_in_attr(attr)?;
- let text_range = string.open_quote_text_range()?;
- let range = TextRange::at(
- text_range.end() + original_line_src_range.start() + relative_range.start(),
- string.syntax().text_range().len().min(range.len()),
- );
- Some(InFile { file_id, value: range })
- }
- Either::Right(comment) => {
- let text_range = comment.syntax().text_range();
- let range = TextRange::at(
- text_range.start()
- + TextSize::try_from(comment.prefix().len()).ok()?
- + original_line_src_range.start()
- + relative_range.start(),
- text_range.len().min(range.len()),
- );
- Some(InFile { file_id, value: range })
- }
- }
- }
-}
-
-fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
- match it.expr() {
- // #[doc = lit]
- Some(ast::Expr::Literal(lit)) => match lit.kind() {
- ast::LiteralKind::String(it) => Some(it),
- _ => None,
- },
- // #[cfg_attr(..., doc = "", ...)]
- None => {
- // FIXME: See highlight injection for what to do here
- None
- }
- _ => None,
- }
-}
-
#[derive(Debug, Clone, Copy)]
pub struct AttrQuery<'attr> {
attrs: &'attr Attrs,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
index cead64a33..152f05b2c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/attr/builtin.rs
@@ -8,7 +8,8 @@
//! name resolution, and `BUILTIN_ATTRIBUTES` is almost entirely unchanged from the original, to
//! ease updating.
-use once_cell::sync::OnceCell;
+use std::sync::OnceLock;
+
use rustc_hash::FxHashMap;
/// Ignored attribute namespaces used by tools.
@@ -29,7 +30,7 @@ pub struct AttributeTemplate {
}
pub fn find_builtin_attr_idx(name: &str) -> Option<usize> {
- static BUILTIN_LOOKUP_TABLE: OnceCell<FxHashMap<&'static str, usize>> = OnceCell::new();
+ static BUILTIN_LOOKUP_TABLE: OnceLock<FxHashMap<&'static str, usize>> = OnceLock::new();
BUILTIN_LOOKUP_TABLE
.get_or_init(|| {
INERT_ATTRIBUTES.iter().map(|attr| attr.name).enumerate().map(|(a, b)| (b, a)).collect()
@@ -239,7 +240,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
template!(List: "address, kcfi, memory, thread"), DuplicatesOk,
experimental!(no_sanitize)
),
- gated!(no_coverage, Normal, template!(Word), WarnFollowing, experimental!(no_coverage)),
+ gated!(coverage, Normal, template!(Word, List: "on|off"), WarnFollowing, experimental!(coverage)),
ungated!(
doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string"), DuplicatesOk
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
index f8d492d0e..c0baf6011 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -65,6 +65,8 @@ pub type LabelSource = InFile<LabelPtr>;
pub type FieldPtr = AstPtr<ast::RecordExprField>;
pub type FieldSource = InFile<FieldPtr>;
+pub type PatFieldPtr = AstPtr<ast::RecordPatField>;
+pub type PatFieldSource = InFile<PatFieldPtr>;
/// An item body together with the mapping from syntax nodes to HIR expression
/// IDs. This is needed to go from e.g. a position in a file to the HIR
@@ -90,8 +92,8 @@ pub struct BodySourceMap {
/// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
/// Instead, we use id of expression (`92`) to identify the field.
- field_map: FxHashMap<FieldSource, ExprId>,
field_map_back: FxHashMap<ExprId, FieldSource>,
+ pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>,
@@ -164,9 +166,10 @@ impl Body {
};
let module = def.module(db);
let expander = Expander::new(db, file_id, module);
- let (mut body, source_map) =
+ let (mut body, mut source_map) =
Body::new(db, def, expander, params, body, module.krate, is_async_fn);
body.shrink_to_fit();
+ source_map.shrink_to_fit();
(Arc::new(body), Arc::new(source_map))
}
@@ -375,9 +378,8 @@ impl BodySourceMap {
self.field_map_back[&expr].clone()
}
- pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> {
- let src = node.map(AstPtr::new);
- self.field_map.get(&src).cloned()
+ pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
+ self.pat_field_map_back[&pat].clone()
}
pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprId> {
@@ -389,4 +391,29 @@ impl BodySourceMap {
pub fn diagnostics(&self) -> &[BodyDiagnostic] {
&self.diagnostics
}
+
+ fn shrink_to_fit(&mut self) {
+ let Self {
+ expr_map,
+ expr_map_back,
+ pat_map,
+ pat_map_back,
+ label_map,
+ label_map_back,
+ field_map_back,
+ pat_field_map_back,
+ expansions,
+ diagnostics,
+ } = self;
+ expr_map.shrink_to_fit();
+ expr_map_back.shrink_to_fit();
+ pat_map.shrink_to_fit();
+ pat_map_back.shrink_to_fit();
+ label_map.shrink_to_fit();
+ label_map_back.shrink_to_fit();
+ field_map_back.shrink_to_fit();
+ pat_field_map_back.shrink_to_fit();
+ expansions.shrink_to_fit();
+ diagnostics.shrink_to_fit();
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 3853a6ab3..cc02df80a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -25,13 +25,20 @@ use triomphe::Arc;
use crate::{
body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, LabelPtr, PatPtr},
+ builtin_type::BuiltinUint,
data::adt::StructKind,
db::DefDatabase,
expander::Expander,
hir::{
- dummy_expr_id, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy,
- ClosureKind, Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
- Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+ dummy_expr_id,
+ format_args::{
+ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
+ FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
+ FormatPlaceholder, FormatSign, FormatTrait,
+ },
+ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
+ Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
+ OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
lang_item::LangItem,
@@ -42,6 +49,8 @@ use crate::{
AdtId, BlockId, BlockLoc, ConstBlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro,
};
+type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
+
pub(super) fn lower(
db: &dyn DefDatabase,
owner: DefWithBodyId,
@@ -437,7 +446,6 @@ impl ExprCollector<'_> {
None => self.missing_expr(),
};
let src = self.expander.to_source(AstPtr::new(&field));
- self.source_map.field_map.insert(src.clone(), expr);
self.source_map.field_map_back.insert(expr, src);
Some(RecordLitField { name, expr })
})
@@ -505,6 +513,9 @@ impl ExprCollector<'_> {
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
+ let num_params = pl.params().count();
+ args.reserve_exact(num_params);
+ arg_types.reserve_exact(num_params);
for param in pl.params() {
let pat = this.collect_pat_top(param.pat());
let type_ref =
@@ -576,11 +587,6 @@ impl ExprCollector<'_> {
syntax_ptr,
)
}
- ast::Expr::BoxExpr(e) => {
- let expr = self.collect_expr_opt(e.expr());
- self.alloc_expr(Expr::Box { expr }, syntax_ptr)
- }
-
ast::Expr::ArrayExpr(e) => {
let kind = e.kind();
@@ -650,6 +656,16 @@ impl ExprCollector<'_> {
}
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
+ ast::Expr::AsmExpr(e) => {
+ let e = self.collect_expr_opt(e.expr());
+ self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
+ }
+ ast::Expr::OffsetOfExpr(e) => {
+ let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
+ let fields = e.fields().map(|it| it.as_name()).collect();
+ self.alloc_expr(Expr::OffsetOf(OffsetOf { container, fields }), syntax_ptr)
+ }
+ ast::Expr::FormatArgsExpr(f) => self.collect_format_args(f, syntax_ptr),
})
}
@@ -660,6 +676,7 @@ impl ExprCollector<'_> {
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
let prev_binding_owner = self.current_binding_owner.take();
self.current_binding_owner = Some(result_expr_id);
+
(result_expr_id, prev_binding_owner)
}
@@ -741,7 +758,27 @@ impl ExprCollector<'_> {
fn collect_while_loop(&mut self, syntax_ptr: AstPtr<ast::Expr>, e: ast::WhileExpr) -> ExprId {
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_labelled_block_opt(label, e.loop_body());
- let condition = self.collect_expr_opt(e.condition());
+
+ // Labels can also be used in the condition expression, like this:
+ // ```
+ // fn main() {
+ // let mut optional = Some(0);
+ // 'my_label: while let Some(a) = match optional {
+ // None => break 'my_label,
+ // Some(val) => Some(val),
+ // } {
+ // println!("{}", a);
+ // optional = None;
+ // }
+ // }
+ // ```
+ let condition = match label {
+ Some(label) => {
+ self.with_labeled_rib(label, |this| this.collect_expr_opt(e.condition()))
+ }
+ None => self.collect_expr_opt(e.condition()),
+ };
+
let break_expr =
self.alloc_expr(Expr::Break { expr: None, label: None }, syntax_ptr.clone());
let if_expr = self.alloc_expr(
@@ -1100,7 +1137,9 @@ impl ExprCollector<'_> {
ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
_ => false,
});
- statement_has_item || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+ statement_has_item
+ || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
+ || (block.may_carry_attributes() && block.attrs().next().is_some())
};
let block_id = if block_has_items {
@@ -1290,23 +1329,21 @@ impl ExprCollector<'_> {
ast::Pat::RecordPat(p) => {
let path =
p.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new);
- let args = p
- .record_pat_field_list()
- .expect("every struct should have a field list")
+ let record_pat_field_list =
+ &p.record_pat_field_list().expect("every struct should have a field list");
+ let args = record_pat_field_list
.fields()
.filter_map(|f| {
let ast_pat = f.pat()?;
let pat = self.collect_pat(ast_pat, binding_list);
let name = f.field_name()?.as_name();
+ let src = self.expander.to_source(AstPtr::new(&f));
+ self.source_map.pat_field_map_back.insert(pat, src);
Some(RecordFieldPat { name, pat })
})
.collect();
- let ellipsis = p
- .record_pat_field_list()
- .expect("every struct should have a field list")
- .rest_pat()
- .is_some();
+ let ellipsis = record_pat_field_list.rest_pat().is_some();
Pat::Record { path, args, ellipsis }
}
@@ -1526,6 +1563,401 @@ impl ExprCollector<'_> {
}
}
// endregion: labels
+
+ // region: format
+ fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
+ let m = match expr {
+ ast::Expr::MacroExpr(m) => m,
+ ast::Expr::Literal(l) => {
+ return match l.kind() {
+ ast::LiteralKind::String(s) => Some((s, true)),
+ _ => None,
+ }
+ }
+ _ => return None,
+ };
+ let e = m.macro_call()?;
+ let macro_ptr = AstPtr::new(&e);
+ let (exp, _) = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
+ expansion.and_then(|it| this.expand_macros_to_string(it))
+ })?;
+ Some((exp, false))
+ }
+
+ fn collect_format_args(
+ &mut self,
+ f: ast::FormatArgsExpr,
+ syntax_ptr: AstPtr<ast::Expr>,
+ ) -> ExprId {
+ let mut args = FormatArgumentsCollector::new();
+ f.args().for_each(|arg| {
+ args.add(FormatArgument {
+ kind: match arg.name() {
+ Some(name) => FormatArgumentKind::Named(name.as_name()),
+ None => FormatArgumentKind::Normal,
+ },
+ expr: self.collect_expr_opt(arg.expr()),
+ });
+ });
+ let template = f.template();
+ let fmt_snippet = template.as_ref().map(ToString::to_string);
+ let fmt = match template.and_then(|it| self.expand_macros_to_string(it)) {
+ Some((s, is_direct_literal)) => {
+ format_args::parse(&s, fmt_snippet, args, is_direct_literal, |name| {
+ self.alloc_expr_desugared(Expr::Path(Path::from(name)))
+ })
+ }
+ None => FormatArgs { template: Default::default(), arguments: args.finish() },
+ };
+
+ // Create a list of all _unique_ (argument, format trait) combinations.
+ // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
+ let mut argmap = FxIndexSet::default();
+ for piece in fmt.template.iter() {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
+ if let Ok(index) = placeholder.argument.index {
+ argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
+ }
+ }
+
+ let lit_pieces =
+ fmt.template
+ .iter()
+ .enumerate()
+ .filter_map(|(i, piece)| {
+ match piece {
+ FormatArgsPiece::Literal(s) => Some(
+ self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))),
+ ),
+ &FormatArgsPiece::Placeholder(_) => {
+ // Inject empty string before placeholders when not already preceded by a literal piece.
+ if i == 0
+ || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_))
+ {
+ Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(
+ "".into(),
+ ))))
+ } else {
+ None
+ }
+ }
+ }
+ })
+ .collect();
+ let lit_pieces = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: lit_pieces,
+ is_assignee_expr: false,
+ }));
+ let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
+ expr: lit_pieces,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ });
+ let format_options = {
+ // Generate:
+ // &[format_spec_0, format_spec_1, format_spec_2]
+ let elements = fmt
+ .template
+ .iter()
+ .filter_map(|piece| {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
+ Some(self.make_format_spec(placeholder, &mut argmap))
+ })
+ .collect();
+ let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements,
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr: array,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ };
+ let arguments = &*fmt.arguments.arguments;
+
+ let args = if arguments.is_empty() {
+ let expr = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: Box::default(),
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ } else {
+ // Generate:
+ // &match (&arg0, &arg1, &…) {
+ // args => [
+ // <core::fmt::Argument>::new_display(args.0),
+ // <core::fmt::Argument>::new_lower_hex(args.1),
+ // <core::fmt::Argument>::new_debug(args.0),
+ // …
+ // ]
+ // }
+ let args = argmap
+ .iter()
+ .map(|&(arg_index, ty)| {
+ let arg = self.alloc_expr_desugared(Expr::Ref {
+ expr: arguments[arg_index].expr,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ });
+ self.make_argument(arg, ty)
+ })
+ .collect();
+ let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList {
+ elements: args,
+ is_assignee_expr: false,
+ }));
+ self.alloc_expr_desugared(Expr::Ref {
+ expr: array,
+ rawness: Rawness::Ref,
+ mutability: Mutability::Shared,
+ })
+ };
+
+ // Generate:
+ // <core::fmt::Arguments>::new_v1_formatted(
+ // lit_pieces,
+ // args,
+ // format_options,
+ // unsafe { ::core::fmt::UnsafeArg::new() }
+ // )
+
+ let Some(new_v1_formatted) =
+ LangItem::FormatArguments.ty_rel_path(self.db, self.krate, name![new_v1_formatted])
+ else {
+ return self.missing_expr();
+ };
+ let Some(unsafe_arg_new) =
+ LangItem::FormatUnsafeArg.ty_rel_path(self.db, self.krate, name![new])
+ else {
+ return self.missing_expr();
+ };
+ let new_v1_formatted = self.alloc_expr_desugared(Expr::Path(new_v1_formatted));
+
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call {
+ callee: unsafe_arg_new,
+ args: Box::default(),
+ is_assignee_expr: false,
+ });
+ let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
+ id: None,
+ statements: Box::default(),
+ tail: Some(unsafe_arg_new),
+ });
+
+ self.alloc_expr(
+ Expr::Call {
+ callee: new_v1_formatted,
+ args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
+ is_assignee_expr: false,
+ },
+ syntax_ptr,
+ )
+ }
+
+ /// Generate a hir expression for a format_args placeholder specification.
+ ///
+ /// Generates
+ ///
+ /// ```text
+ /// <core::fmt::rt::Placeholder::new(
+ /// …usize, // position
+ /// '…', // fill
+ /// <core::fmt::rt::Alignment>::…, // alignment
+ /// …u32, // flags
+ /// <core::fmt::rt::Count::…>, // width
+ /// <core::fmt::rt::Count::…>, // precision
+ /// )
+ /// ```
+ fn make_format_spec(
+ &mut self,
+ placeholder: &FormatPlaceholder,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> ExprId {
+ let position = match placeholder.argument.index {
+ Ok(arg_index) => {
+ let (i, _) =
+ argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
+ self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ i as u128,
+ Some(BuiltinUint::Usize),
+ )))
+ }
+ Err(_) => self.missing_expr(),
+ };
+ let &FormatOptions {
+ ref width,
+ ref precision,
+ alignment,
+ fill,
+ sign,
+ alternate,
+ zero_pad,
+ debug_hex,
+ } = &placeholder.format_options;
+ let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
+
+ let align = {
+ let align = LangItem::FormatAlignment.ty_rel_path(
+ self.db,
+ self.krate,
+ match alignment {
+ Some(FormatAlignment::Left) => name![Left],
+ Some(FormatAlignment::Right) => name![Right],
+ Some(FormatAlignment::Center) => name![Center],
+ None => name![Unknown],
+ },
+ );
+ match align {
+ Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
+ None => self.missing_expr(),
+ }
+ };
+ // This needs to match `Flag` in library/core/src/fmt/rt.rs.
+ let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
+ | ((sign == Some(FormatSign::Minus)) as u32) << 1
+ | (alternate as u32) << 2
+ | (zero_pad as u32) << 3
+ | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
+ | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
+ let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ flags as u128,
+ Some(BuiltinUint::U32),
+ )));
+ let precision = self.make_count(&precision, argmap);
+ let width = self.make_count(&width, argmap);
+
+ let format_placeholder_new = {
+ let format_placeholder_new =
+ LangItem::FormatPlaceholder.ty_rel_path(self.db, self.krate, name![new]);
+ match format_placeholder_new {
+ Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
+ None => self.missing_expr(),
+ }
+ };
+
+ self.alloc_expr_desugared(Expr::Call {
+ callee: format_placeholder_new,
+ args: Box::new([position, fill, align, flags, precision, width]),
+ is_assignee_expr: false,
+ })
+ }
+
+ /// Generate a hir expression for a format_args Count.
+ ///
+ /// Generates:
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Is(…)
+ /// ```
+ ///
+ /// or
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Param(…)
+ /// ```
+ ///
+ /// or
+ ///
+ /// ```text
+ /// <core::fmt::rt::Count>::Implied
+ /// ```
+ fn make_count(
+ &mut self,
+ count: &Option<FormatCount>,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+ ) -> ExprId {
+ match count {
+ Some(FormatCount::Literal(n)) => {
+ match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
+ Some(count_is) => {
+ let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
+ let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ *n as u128,
+ Some(BuiltinUint::Usize),
+ )));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: count_is,
+ args: Box::new([args]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ }
+ Some(FormatCount::Argument(arg)) => {
+ if let Ok(arg_index) = arg.index {
+ let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
+
+ match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
+ Some(count_param) => {
+ let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
+ let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+ i as u128,
+ Some(BuiltinUint::Usize),
+ )));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: count_param,
+ args: Box::new([args]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ } else {
+ self.missing_expr()
+ }
+ }
+ None => match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Implied]) {
+ Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
+ None => self.missing_expr(),
+ },
+ }
+ }
+
+ /// Generate a hir expression representing an argument to a format_args invocation.
+ ///
+ /// Generates:
+ ///
+ /// ```text
+ /// <core::fmt::Argument>::new_…(arg)
+ /// ```
+ fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
+ use ArgumentType::*;
+ use FormatTrait::*;
+ match LangItem::FormatArgument.ty_rel_path(
+ self.db,
+ self.krate,
+ match ty {
+ Format(Display) => name![new_display],
+ Format(Debug) => name![new_debug],
+ Format(LowerExp) => name![new_lower_exp],
+ Format(UpperExp) => name![new_upper_exp],
+ Format(Octal) => name![new_octal],
+ Format(Pointer) => name![new_pointer],
+ Format(Binary) => name![new_binary],
+ Format(LowerHex) => name![new_lower_hex],
+ Format(UpperHex) => name![new_upper_hex],
+ Usize => name![from_usize],
+ },
+ ) {
+ Some(new_fn) => {
+ let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
+ self.alloc_expr_desugared(Expr::Call {
+ callee: new_fn,
+ args: Box::new([arg]),
+ is_assignee_expr: false,
+ })
+ }
+ None => self.missing_expr(),
+ }
+ }
+ // endregion: format
}
fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {
@@ -1601,3 +2033,9 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
(|| syntax::algo::skip_trivia_token(t?.next_token()?, syntax::Direction::Next))()
.map_or(false, |it| it.kind() == syntax::T![,])
}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+ Format(FormatTrait),
+ Usize,
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
index 5d71abe37..fad4d7a4d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs
@@ -2,7 +2,7 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
+use itertools::Itertools;
use syntax::ast::HasName;
use crate::{
@@ -51,8 +51,7 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
}
};
- let mut p =
- Printer { db: db.upcast(), body, buf: header, indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false };
if let DefWithBodyId::FunctionId(it) = owner {
p.buf.push('(');
body.params.iter().zip(&db.function_data(it).params).for_each(|(&param, ty)| {
@@ -76,8 +75,7 @@ pub(super) fn print_expr_hir(
_owner: DefWithBodyId,
expr: ExprId,
) -> String {
- let mut p =
- Printer { db: db.upcast(), body, buf: String::new(), indent_level: 0, needs_indent: false };
+ let mut p = Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false };
p.print_expr(expr);
p.buf
}
@@ -98,7 +96,7 @@ macro_rules! wln {
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
body: &'a Body,
buf: String,
indent_level: usize,
@@ -142,9 +140,14 @@ impl Printer<'_> {
}
fn newline(&mut self) {
- match self.buf.chars().rev().find(|ch| *ch != ' ') {
- Some('\n') | None => {}
- _ => writeln!(self).unwrap(),
+ match self.buf.chars().rev().find_position(|ch| *ch != ' ') {
+ Some((_, '\n')) | None => {}
+ Some((idx, _)) => {
+ if idx != 0 {
+ self.buf.drain(self.buf.len() - idx..);
+ }
+ writeln!(self).unwrap()
+ }
}
}
@@ -154,6 +157,19 @@ impl Printer<'_> {
match expr {
Expr::Missing => w!(self, "�"),
Expr::Underscore => w!(self, "_"),
+ Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"),
+ Expr::OffsetOf(offset_of) => {
+ w!(self, "builtin#offset_of(");
+ self.print_type_ref(&offset_of.container);
+ w!(
+ self,
+ ", {})",
+ offset_of
+ .fields
+ .iter()
+ .format_with(".", |field, f| f(&field.display(self.db.upcast())))
+ );
+ }
Expr::Path(path) => self.print_path(path),
Expr::If { condition, then_branch, else_branch } => {
w!(self, "if ");
@@ -173,7 +189,7 @@ impl Printer<'_> {
}
Expr::Loop { body, label } => {
if let Some(lbl) = label {
- w!(self, "{}: ", self.body[*lbl].name.display(self.db));
+ w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast()));
}
w!(self, "loop ");
self.print_expr(*body);
@@ -193,7 +209,7 @@ impl Printer<'_> {
}
Expr::MethodCall { receiver, method_name, args, generic_args } => {
self.print_expr(*receiver);
- w!(self, ".{}", method_name.display(self.db));
+ w!(self, ".{}", method_name.display(self.db.upcast()));
if let Some(args) = generic_args {
w!(self, "::<");
print_generic_args(self.db, args, self).unwrap();
@@ -231,13 +247,13 @@ impl Printer<'_> {
Expr::Continue { label } => {
w!(self, "continue");
if let Some(lbl) = label {
- w!(self, " {}", self.body[*lbl].name.display(self.db));
+ w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
}
}
Expr::Break { expr, label } => {
w!(self, "break");
if let Some(lbl) = label {
- w!(self, " {}", self.body[*lbl].name.display(self.db));
+ w!(self, " {}", self.body[*lbl].name.display(self.db.upcast()));
}
if let Some(expr) = expr {
self.whitespace();
@@ -276,7 +292,7 @@ impl Printer<'_> {
w!(self, "{{");
self.indented(|p| {
for field in &**fields {
- w!(p, "{}: ", field.name.display(self.db));
+ w!(p, "{}: ", field.name.display(self.db.upcast()));
p.print_expr(field.expr);
wln!(p, ",");
}
@@ -293,7 +309,7 @@ impl Printer<'_> {
}
Expr::Field { expr, name } => {
self.print_expr(*expr);
- w!(self, ".{}", name.display(self.db));
+ w!(self, ".{}", name.display(self.db.upcast()));
}
Expr::Await { expr } => {
self.print_expr(*expr);
@@ -431,7 +447,8 @@ impl Printer<'_> {
}
Expr::Literal(lit) => self.print_literal(lit),
Expr::Block { id: _, statements, tail, label } => {
- let label = label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db)));
+ let label =
+ label.map(|lbl| format!("{}: ", self.body[lbl].name.display(self.db.upcast())));
self.print_block(label.as_deref(), statements, tail);
}
Expr::Unsafe { id: _, statements, tail } => {
@@ -507,7 +524,7 @@ impl Printer<'_> {
w!(self, " {{");
self.indented(|p| {
for arg in args.iter() {
- w!(p, "{}: ", arg.name.display(self.db));
+ w!(p, "{}: ", arg.name.display(self.db.upcast()));
p.print_pat(arg.pat);
wln!(p, ",");
}
@@ -666,6 +683,6 @@ impl Printer<'_> {
BindingAnnotation::Ref => "ref ",
BindingAnnotation::RefMut => "ref mut ",
};
- w!(self, "{}{}", mode, name.display(self.db));
+ w!(self, "{}{}", mode, name.display(self.db.upcast()));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
index d55820116..1658757d2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
@@ -1,13 +1,13 @@
mod block;
use base_db::{fixture::WithFixture, SourceDatabase};
-use expect_test::Expect;
+use expect_test::{expect, Expect};
use crate::{test_db::TestDB, ModuleDefId};
use super::*;
-fn lower(ra_fixture: &str) -> Arc<Body> {
+fn lower(ra_fixture: &str) -> (TestDB, Arc<Body>, DefWithBodyId) {
let db = TestDB::with_files(ra_fixture);
let krate = db.crate_graph().iter().next().unwrap();
@@ -21,8 +21,10 @@ fn lower(ra_fixture: &str) -> Arc<Body> {
}
}
}
+ let fn_def = fn_def.unwrap().into();
- db.body(fn_def.unwrap().into())
+ let body = db.body(fn_def);
+ (db, body, fn_def)
}
fn def_map_at(ra_fixture: &str) -> String {
@@ -138,3 +140,84 @@ mod m {
"#,
);
}
+
+#[test]
+fn desugar_builtin_format_args() {
+ // Regression test for a path resolution bug introduced with inner item handling.
+ let (db, body, def) = lower(
+ r#"
+//- minicore: fmt
+fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+}
+"#,
+ );
+
+ expect![[r#"
+ fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#lang(Arguments::new_v1_formatted)(
+ &[
+ "\"hello ", " ", " friends, we ", " ", "", "\"",
+ ],
+ &[
+ builtin#lang(Argument::new_display)(
+ &count,
+ ), builtin#lang(Argument::new_display)(
+ &"fancy",
+ ), builtin#lang(Argument::new_debug)(
+ &are,
+ ), builtin#lang(Argument::new_display)(
+ &"!",
+ ),
+ ],
+ &[
+ builtin#lang(Placeholder::new)(
+ 0usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 8u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Is)(
+ 2usize,
+ ),
+ ), builtin#lang(Placeholder::new)(
+ 1usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 2usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 1usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ), builtin#lang(Placeholder::new)(
+ 3usize,
+ ' ',
+ builtin#lang(Alignment::Unknown),
+ 0u32,
+ builtin#lang(Count::Implied),
+ builtin#lang(Count::Implied),
+ ),
+ ],
+ unsafe {
+ builtin#lang(UnsafeArg::new)()
+ },
+ );
+ }"#]]
+ .assert_eq(&body.pretty_print(&db, def))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
index 4e015a7fb..44eeed9e3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs
@@ -38,9 +38,9 @@ fn outer() {
"#,
expect![[r#"
block scope
- CrateStruct: t
- PlainStruct: t v
- SelfStruct: t
+ CrateStruct: ti
+ PlainStruct: ti vi
+ SelfStruct: ti
Struct: v
SuperStruct: _
@@ -66,7 +66,7 @@ fn outer() {
"#,
expect![[r#"
block scope
- imported: t v
+ imported: ti vi
name: v
crate
@@ -92,9 +92,9 @@ fn outer() {
"#,
expect![[r#"
block scope
- inner1: t
+ inner1: ti
inner2: v
- outer: v
+ outer: vi
block scope
inner: v
@@ -121,7 +121,7 @@ struct Struct {}
"#,
expect![[r#"
block scope
- Struct: t
+ Struct: ti
crate
Struct: t
@@ -153,7 +153,7 @@ fn outer() {
"#,
expect![[r#"
block scope
- ResolveMe: t
+ ResolveMe: ti
block scope
m2: t
@@ -214,7 +214,7 @@ fn f() {
"#,
expect![[r#"
block scope
- ResolveMe: t
+ ResolveMe: ti
block scope
h: v
@@ -292,7 +292,7 @@ pub mod cov_mark {
nested: v
crate
- cov_mark: t
+ cov_mark: ti
f: v
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data.rs b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
index 91db68058..68defa385 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -487,7 +487,7 @@ impl ExternCrateDeclData {
db.crate_def_map(loc.container.krate())
.extern_prelude()
.find(|&(prelude_name, ..)| *prelude_name == name)
- .map(|(_, root)| root.krate())
+ .map(|(_, (root, _))| root.krate())
};
Arc::new(Self {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index c8df3f3f9..224f7328f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -447,6 +447,7 @@ impl VariantData {
}
}
+ // FIXME: Linear lookup
pub fn field(&self, name: &Name) -> Option<LocalFieldId> {
self.fields().iter().find_map(|(id, data)| if &data.name == name { Some(id) } else { None })
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/db.rs b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
index e34a6768f..31c1a7130 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -82,6 +82,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(ItemTree::file_item_tree_query)]
fn file_item_tree(&self, file_id: HirFileId) -> Arc<ItemTree>;
+ #[salsa::invoke(ItemTree::block_item_tree_query)]
+ fn block_item_tree_query(&self, block_id: BlockId) -> Arc<ItemTree>;
+
#[salsa::invoke(crate_def_map_wait)]
#[salsa::transparent]
fn crate_def_map(&self, krate: CrateId) -> Arc<DefMap>;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index df2af4c89..b9c5ff727 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -11,7 +11,7 @@ use crate::{
nameres::DefMap,
path::{ModPath, PathKind},
visibility::Visibility,
- ModuleDefId, ModuleId,
+ CrateRootModuleId, ModuleDefId, ModuleId,
};
/// Find a path that can be used to refer to a certain item. This can depend on
@@ -37,6 +37,20 @@ pub fn find_path_prefixed(
find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
}
+#[derive(Copy, Clone, Debug)]
+enum Stability {
+ Unstable,
+ Stable,
+}
+use Stability::*;
+
+fn zip_stability(a: Stability, b: Stability) -> Stability {
+ match (a, b) {
+ (Stable, Stable) => Stable,
+ _ => Unstable,
+ }
+}
+
const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -81,7 +95,7 @@ fn find_path_inner(
}
let def_map = from.def_map(db);
- let crate_root = def_map.crate_root().into();
+ let crate_root = def_map.crate_root();
// - if the item is a module, jump straight to module search
if let ItemInNs::Types(ModuleDefId::ModuleId(module_id)) = item {
let mut visited_modules = FxHashSet::default();
@@ -95,7 +109,8 @@ fn find_path_inner(
MAX_PATH_LEN,
prefixed,
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
- );
+ )
+ .map(|(item, _)| item);
}
// - if the item is already in scope, return the name under which it is
@@ -143,19 +158,20 @@ fn find_path_inner(
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
scope_name,
)
+ .map(|(item, _)| item)
}
fn find_path_for_module(
db: &dyn DefDatabase,
def_map: &DefMap,
visited_modules: &mut FxHashSet<ModuleId>,
- crate_root: ModuleId,
+ crate_root: CrateRootModuleId,
from: ModuleId,
module_id: ModuleId,
max_len: usize,
prefixed: Option<PrefixKind>,
prefer_no_std: bool,
-) -> Option<ModPath> {
+) -> Option<(ModPath, Stability)> {
if max_len == 0 {
return None;
}
@@ -165,25 +181,25 @@ fn find_path_for_module(
let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
if prefixed.is_none() {
if let Some(scope_name) = scope_name {
- return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
+ return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable));
}
}
// - if the item is the crate root, return `crate`
if module_id == crate_root {
- return Some(ModPath::from_segments(PathKind::Crate, None));
+ return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
}
// - if relative paths are fine, check if we are searching for a parent
if prefixed.filter(PrefixKind::is_absolute).is_none() {
if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
- return modpath;
+ return modpath.zip(Some(Stable));
}
}
// - if the item is the crate root of a dependency crate, return the name from the extern prelude
let root_def_map = crate_root.def_map(db);
- for (name, def_id) in root_def_map.extern_prelude() {
+ for (name, (def_id, _extern_crate)) in root_def_map.extern_prelude() {
if module_id == def_id {
let name = scope_name.unwrap_or_else(|| name.clone());
@@ -192,7 +208,7 @@ fn find_path_for_module(
def_map[local_id]
.scope
.type_(&name)
- .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+ .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id.into()))
})
.is_some();
let kind = if name_already_occupied_in_type_ns {
@@ -201,14 +217,14 @@ fn find_path_for_module(
} else {
PathKind::Plain
};
- return Some(ModPath::from_segments(kind, Some(name)));
+ return Some((ModPath::from_segments(kind, Some(name)), Stable));
}
}
if let value @ Some(_) =
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
{
- return value;
+ return value.zip(Some(Stable));
}
calculate_best_path(
db,
@@ -224,6 +240,7 @@ fn find_path_for_module(
)
}
+// FIXME: Do we still need this now that we record import origins, and hence aliases?
fn find_in_scope(
db: &dyn DefDatabase,
def_map: &DefMap,
@@ -244,7 +261,7 @@ fn find_in_prelude(
item: ItemInNs,
from: ModuleId,
) -> Option<ModPath> {
- let prelude_module = root_def_map.prelude()?;
+ let (prelude_module, _) = root_def_map.prelude()?;
// Preludes in block DefMaps are ignored, only the crate DefMap is searched
let prelude_def_map = prelude_module.def_map(db);
let prelude_scope = &prelude_def_map[prelude_module.local_id].scope;
@@ -293,18 +310,26 @@ fn calculate_best_path(
db: &dyn DefDatabase,
def_map: &DefMap,
visited_modules: &mut FxHashSet<ModuleId>,
- crate_root: ModuleId,
+ crate_root: CrateRootModuleId,
max_len: usize,
item: ItemInNs,
from: ModuleId,
mut prefixed: Option<PrefixKind>,
prefer_no_std: bool,
scope_name: Option<Name>,
-) -> Option<ModPath> {
+) -> Option<(ModPath, Stability)> {
if max_len <= 1 {
return None;
}
let mut best_path = None;
+ let update_best_path =
+ |best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
+ Some((old_path, old_stability)) => {
+ *old_path = new_path.0;
+ *old_stability = zip_stability(*old_stability, new_path.1);
+ }
+ None => *best_path = Some(new_path),
+ };
// Recursive case:
// - otherwise, look for modules containing (reexporting) it and import it from one of those
if item.krate(db) == Some(from.krate) {
@@ -327,14 +352,14 @@ fn calculate_best_path(
prefixed,
prefer_no_std,
) {
- path.push_segment(name);
+ path.0.push_segment(name);
- let new_path = match best_path {
+ let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
- best_path_len = new_path.len();
- best_path = Some(new_path);
+ best_path_len = new_path.0.len();
+ update_best_path(&mut best_path, new_path);
}
}
} else {
@@ -346,9 +371,14 @@ fn calculate_best_path(
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
let import_map = db.import_map(dep.crate_id);
import_map.import_info_for(item).and_then(|info| {
+ if info.is_doc_hidden {
+ // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
+ return None;
+ }
+
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
- let mut path = find_path_for_module(
+ let (mut path, path_stability) = find_path_for_module(
db,
def_map,
visited_modules,
@@ -361,16 +391,19 @@ fn calculate_best_path(
)?;
cov_mark::hit!(partially_imported);
path.push_segment(info.name.clone());
- Some(path)
+ Some((
+ path,
+ zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
+ ))
})
});
for path in extern_paths {
- let new_path = match best_path {
+ let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
- best_path = Some(new_path);
+ update_best_path(&mut best_path, new_path);
}
}
if let Some(module) = item.module(db) {
@@ -381,15 +414,24 @@ fn calculate_best_path(
}
match prefixed.map(PrefixKind::prefix) {
Some(prefix) => best_path.or_else(|| {
- scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
+ scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable))
}),
None => best_path,
}
}
-fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
+fn select_best_path(
+ old_path: (ModPath, Stability),
+ new_path: (ModPath, Stability),
+ prefer_no_std: bool,
+) -> (ModPath, Stability) {
+ match (old_path.1, new_path.1) {
+ (Stable, Unstable) => return old_path,
+ (Unstable, Stable) => return new_path,
+ _ => {}
+ }
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
- match (old_path.segments().first(), new_path.segments().first()) {
+ match (old_path.0.segments().first(), new_path.0.segments().first()) {
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
let rank = match prefer_no_std {
false => |name: &Name| match name {
@@ -410,7 +452,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
match nrank.cmp(&orank) {
Ordering::Less => old_path,
Ordering::Equal => {
- if new_path.len() < old_path.len() {
+ if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@@ -420,7 +462,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
}
}
_ => {
- if new_path.len() < old_path.len() {
+ if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@@ -1293,4 +1335,90 @@ pub mod prelude {
"None",
);
}
+
+ #[test]
+ fn different_crate_renamed_through_dep() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+pub extern crate std as std_renamed;
+//- /std.rs crate:std
+pub struct S;
+ "#,
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ "intermediate::std_renamed::S",
+ );
+ }
+
+ #[test]
+ fn different_crate_doc_hidden() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:intermediate
+$0
+//- /intermediate.rs crate:intermediate deps:std
+#[doc(hidden)]
+pub extern crate std;
+pub extern crate std as longer;
+//- /std.rs crate:std
+pub struct S;
+ "#,
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ "intermediate::longer::S",
+ );
+ }
+
+ #[test]
+ fn respect_doc_hidden() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std,lazy_static
+$0
+//- /lazy_static.rs crate:lazy_static deps:core
+#[doc(hidden)]
+pub use core::ops::Deref as __Deref;
+//- /std.rs crate:std deps:core
+pub use core::ops;
+//- /core.rs crate:core
+pub mod ops {
+ pub trait Deref {}
+}
+ "#,
+ "std::ops::Deref",
+ "std::ops::Deref",
+ "std::ops::Deref",
+ "std::ops::Deref",
+ );
+ }
+
+ #[test]
+ fn respect_unstable_modules() {
+ check_found_path(
+ r#"
+//- /main.rs crate:main deps:std,core
+#![no_std]
+extern crate std;
+$0
+//- /longer.rs crate:std deps:core
+pub mod error {
+ pub use core::error::Error;
+}
+//- /core.rs crate:core
+pub mod error {
+ #![unstable(feature = "error_in_core", issue = "103765")]
+ pub trait Error {}
+}
+"#,
+ "std::error::Error",
+ "std::error::Error",
+ "std::error::Error",
+ "std::error::Error",
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
index d7d44e413..1e2535a8a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs
@@ -21,10 +21,11 @@ use crate::{
db::DefDatabase,
dyn_map::{keys, DynMap},
expander::Expander,
+ item_tree::{AttrOwner, ItemTree},
lower::LowerCtx,
nameres::{DefMap, MacroSubNs},
src::{HasChildSource, HasSource},
- type_ref::{LifetimeRef, TypeBound, TypeRef},
+ type_ref::{ConstRef, LifetimeRef, TypeBound, TypeRef},
AdtId, ConstParamId, GenericDefId, HasModule, LifetimeParamId, LocalLifetimeParamId,
LocalTypeOrConstParamId, Lookup, TypeOrConstParamId, TypeParamId,
};
@@ -48,7 +49,7 @@ pub struct LifetimeParamData {
pub struct ConstParamData {
pub name: Name,
pub ty: Interned<TypeRef>,
- pub has_default: bool,
+ pub default: Option<ConstRef>,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -75,7 +76,7 @@ impl TypeOrConstParamData {
pub fn has_default(&self) -> bool {
match self {
TypeOrConstParamData::TypeParamData(it) => it.default.is_some(),
- TypeOrConstParamData::ConstParamData(it) => it.has_default,
+ TypeOrConstParamData::ConstParamData(it) => it.default.is_some(),
}
}
@@ -154,12 +155,58 @@ impl GenericParams {
def: GenericDefId,
) -> Interned<GenericParams> {
let _p = profile::span("generic_params_query");
+
+ let krate = def.module(db).krate;
+ let cfg_options = db.crate_graph();
+ let cfg_options = &cfg_options[krate].cfg_options;
+
+ // Returns the generic parameters that are enabled under the current `#[cfg]` options
+ let enabled_params = |params: &Interned<GenericParams>, item_tree: &ItemTree| {
+ let enabled = |param| item_tree.attrs(db, krate, param).is_cfg_enabled(cfg_options);
+
+ // In the common case, no parameters will by disabled by `#[cfg]` attributes.
+ // Therefore, make a first pass to check if all parameters are enabled and, if so,
+ // clone the `Interned<GenericParams>` instead of recreating an identical copy.
+ let all_type_or_consts_enabled =
+ params.type_or_consts.iter().all(|(idx, _)| enabled(idx.into()));
+ let all_lifetimes_enabled = params.lifetimes.iter().all(|(idx, _)| enabled(idx.into()));
+
+ if all_type_or_consts_enabled && all_lifetimes_enabled {
+ params.clone()
+ } else {
+ Interned::new(GenericParams {
+ type_or_consts: all_type_or_consts_enabled
+ .then(|| params.type_or_consts.clone())
+ .unwrap_or_else(|| {
+ params
+ .type_or_consts
+ .iter()
+ .filter_map(|(idx, param)| {
+ enabled(idx.into()).then(|| param.clone())
+ })
+ .collect()
+ }),
+ lifetimes: all_lifetimes_enabled
+ .then(|| params.lifetimes.clone())
+ .unwrap_or_else(|| {
+ params
+ .lifetimes
+ .iter()
+ .filter_map(|(idx, param)| {
+ enabled(idx.into()).then(|| param.clone())
+ })
+ .collect()
+ }),
+ where_predicates: params.where_predicates.clone(),
+ })
+ }
+ };
macro_rules! id_to_generics {
($id:ident) => {{
let id = $id.lookup(db).id;
let tree = id.item_tree(db);
let item = &tree[id.value];
- item.generic_params.clone()
+ enabled_params(&item.generic_params, &tree)
}};
}
@@ -169,7 +216,8 @@ impl GenericParams {
let tree = loc.id.item_tree(db);
let item = &tree[loc.id.value];
- let mut generic_params = GenericParams::clone(&item.explicit_generic_params);
+ let enabled_params = enabled_params(&item.explicit_generic_params, &tree);
+ let mut generic_params = GenericParams::clone(&enabled_params);
let module = loc.container.module(db);
let func_data = db.function_data(id);
@@ -198,9 +246,14 @@ impl GenericParams {
}
}
- pub(crate) fn fill(&mut self, lower_ctx: &LowerCtx<'_>, node: &dyn HasGenericParams) {
+ pub(crate) fn fill(
+ &mut self,
+ lower_ctx: &LowerCtx<'_>,
+ node: &dyn HasGenericParams,
+ add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+ ) {
if let Some(params) = node.generic_param_list() {
- self.fill_params(lower_ctx, params)
+ self.fill_params(lower_ctx, params, add_param_attrs)
}
if let Some(where_clause) = node.where_clause() {
self.fill_where_predicates(lower_ctx, where_clause);
@@ -218,7 +271,12 @@ impl GenericParams {
}
}
- fn fill_params(&mut self, lower_ctx: &LowerCtx<'_>, params: ast::GenericParamList) {
+ fn fill_params(
+ &mut self,
+ lower_ctx: &LowerCtx<'_>,
+ params: ast::GenericParamList,
+ mut add_param_attrs: impl FnMut(AttrOwner, ast::GenericParam),
+ ) {
for type_or_const_param in params.type_or_const_params() {
match type_or_const_param {
ast::TypeOrConstParam::Type(type_param) => {
@@ -232,13 +290,14 @@ impl GenericParams {
default,
provenance: TypeParamProvenance::TypeParamList,
};
- self.type_or_consts.alloc(param.into());
+ let idx = self.type_or_consts.alloc(param.into());
let type_ref = TypeRef::Path(name.into());
self.fill_bounds(
lower_ctx,
type_param.type_bound_list(),
Either::Left(type_ref),
);
+ add_param_attrs(idx.into(), ast::GenericParam::TypeParam(type_param));
}
ast::TypeOrConstParam::Const(const_param) => {
let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
@@ -248,9 +307,10 @@ impl GenericParams {
let param = ConstParamData {
name,
ty: Interned::new(ty),
- has_default: const_param.default_val().is_some(),
+ default: ConstRef::from_const_param(lower_ctx, &const_param),
};
- self.type_or_consts.alloc(param.into());
+ let idx = self.type_or_consts.alloc(param.into());
+ add_param_attrs(idx.into(), ast::GenericParam::ConstParam(const_param));
}
}
}
@@ -258,13 +318,14 @@ impl GenericParams {
let name =
lifetime_param.lifetime().map_or_else(Name::missing, |lt| Name::new_lifetime(&lt));
let param = LifetimeParamData { name: name.clone() };
- self.lifetimes.alloc(param);
+ let idx = self.lifetimes.alloc(param);
let lifetime_ref = LifetimeRef::new_name(name);
self.fill_bounds(
lower_ctx,
lifetime_param.type_bound_list(),
Either::Right(lifetime_ref),
);
+ add_param_attrs(idx.into(), ast::GenericParam::LifetimeParam(lifetime_param));
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 6591c92ac..591ee77c7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -13,6 +13,7 @@
//! See also a neighboring `body` module.
pub mod type_ref;
+pub mod format_args;
use std::fmt;
@@ -117,7 +118,6 @@ impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
use ast::LiteralKind;
match ast_lit_kind {
- // FIXME: these should have actual values filled in, but unsure on perf impact
LiteralKind::IntNumber(lit) => {
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
Literal::Float(
@@ -281,6 +281,19 @@ pub enum Expr {
Array(Array),
Literal(Literal),
Underscore,
+ OffsetOf(OffsetOf),
+ InlineAsm(InlineAsm),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct OffsetOf {
+ pub container: Interned<TypeRef>,
+ pub fields: Box<[Name]>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct InlineAsm {
+ pub e: ExprId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -341,7 +354,8 @@ impl Expr {
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
match self {
Expr::Missing => {}
- Expr::Path(_) => {}
+ Expr::Path(_) | Expr::OffsetOf(_) => {}
+ Expr::InlineAsm(it) => f(it.e),
Expr::If { condition, then_branch, else_branch } => {
f(*condition);
f(*then_branch);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
new file mode 100644
index 000000000..75025a984
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs
@@ -0,0 +1,502 @@
+//! Parses `format_args` input.
+use std::mem;
+
+use hir_expand::name::Name;
+use rustc_parse_format as parse;
+use syntax::{
+ ast::{self, IsString},
+ AstToken, SmolStr, TextRange,
+};
+
+use crate::hir::ExprId;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgs {
+ pub template: Box<[FormatArgsPiece]>,
+ pub arguments: FormatArguments,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArguments {
+ pub arguments: Box<[FormatArgument]>,
+ pub num_unnamed_args: usize,
+ pub num_explicit_args: usize,
+ pub names: Box<[(Name, usize)]>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FormatArgsPiece {
+ Literal(Box<str>),
+ Placeholder(FormatPlaceholder),
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub struct FormatPlaceholder {
+ /// Index into [`FormatArgs::arguments`].
+ pub argument: FormatArgPosition,
+ /// The span inside the format string for the full `{…}` placeholder.
+ pub span: Option<TextRange>,
+ /// `{}`, `{:?}`, or `{:x}`, etc.
+ pub format_trait: FormatTrait,
+ /// `{}` or `{:.5}` or `{:-^20}`, etc.
+ pub format_options: FormatOptions,
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgPosition {
+ /// Which argument this position refers to (Ok),
+ /// or would've referred to if it existed (Err).
+ pub index: Result<usize, usize>,
+ /// What kind of position this is. See [`FormatArgPositionKind`].
+ pub kind: FormatArgPositionKind,
+ /// The span of the name or number.
+ pub span: Option<TextRange>,
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub enum FormatArgPositionKind {
+ /// `{}` or `{:.*}`
+ Implicit,
+ /// `{1}` or `{:1$}` or `{:.1$}`
+ Number,
+ /// `{a}` or `{:a$}` or `{:.a$}`
+ Named,
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+pub enum FormatTrait {
+ /// `{}`
+ Display,
+ /// `{:?}`
+ Debug,
+ /// `{:e}`
+ LowerExp,
+ /// `{:E}`
+ UpperExp,
+ /// `{:o}`
+ Octal,
+ /// `{:p}`
+ Pointer,
+ /// `{:b}`
+ Binary,
+ /// `{:x}`
+ LowerHex,
+ /// `{:X}`
+ UpperHex,
+}
+
+#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
+pub struct FormatOptions {
+ /// The width. E.g. `{:5}` or `{:width$}`.
+ pub width: Option<FormatCount>,
+ /// The precision. E.g. `{:.5}` or `{:.precision$}`.
+ pub precision: Option<FormatCount>,
+ /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
+ pub alignment: Option<FormatAlignment>,
+ /// The fill character. E.g. the `.` in `{:.>10}`.
+ pub fill: Option<char>,
+ /// The `+` or `-` flag.
+ pub sign: Option<FormatSign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag. E.g. the `0` in `{:02x}`.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
+ pub debug_hex: Option<FormatDebugHex>,
+}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatSign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatDebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatAlignment {
+ /// `{:<}`
+ Left,
+ /// `{:>}`
+ Right,
+ /// `{:^}`
+ Center,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum FormatCount {
+ /// `{:5}` or `{:.5}`
+ Literal(usize),
+ /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
+ Argument(FormatArgPosition),
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgument {
+ pub kind: FormatArgumentKind,
+ pub expr: ExprId,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub enum FormatArgumentKind {
+ /// `format_args(…, arg)`
+ Normal,
+ /// `format_args(…, arg = 1)`
+ Named(Name),
+ /// `format_args("… {arg} …")`
+ Captured(Name),
+}
+
+// Only used in parse_args and report_invalid_references,
+// to indicate how a referred argument was used.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum PositionUsedAs {
+ Placeholder(Option<TextRange>),
+ Precision,
+ Width,
+}
+use PositionUsedAs::*;
+
+pub(crate) fn parse(
+ s: &ast::String,
+ fmt_snippet: Option<String>,
+ mut args: FormatArgumentsCollector,
+ is_direct_literal: bool,
+ mut synth: impl FnMut(Name) -> ExprId,
+) -> FormatArgs {
+ let text = s.text();
+ let str_style = match s.quote_offsets() {
+ Some(offsets) => {
+ let raw = u32::from(offsets.quotes.0.len()) - 1;
+ (raw != 0).then_some(raw as usize)
+ }
+ None => None,
+ };
+ let mut parser =
+ parse::Parser::new(text, str_style, fmt_snippet, false, parse::ParseMode::Format);
+
+ let mut pieces = Vec::new();
+ while let Some(piece) = parser.next() {
+ if !parser.errors.is_empty() {
+ break;
+ } else {
+ pieces.push(piece);
+ }
+ }
+ let is_source_literal = parser.is_source_literal;
+ if !parser.errors.is_empty() {
+ // FIXME: Diagnose
+ return FormatArgs { template: Default::default(), arguments: args.finish() };
+ }
+
+ let to_span = |inner_span: parse::InnerSpan| {
+ is_source_literal.then(|| {
+ TextRange::new(inner_span.start.try_into().unwrap(), inner_span.end.try_into().unwrap())
+ })
+ };
+
+ let mut used = vec![false; args.explicit_args().len()];
+ let mut invalid_refs = Vec::new();
+ let mut numeric_refences_to_named_arg = Vec::new();
+
+ enum ArgRef<'a> {
+ Index(usize),
+ Name(&'a str, Option<TextRange>),
+ }
+ let mut lookup_arg = |arg: ArgRef<'_>,
+ span: Option<TextRange>,
+ used_as: PositionUsedAs,
+ kind: FormatArgPositionKind|
+ -> FormatArgPosition {
+ let index = match arg {
+ ArgRef::Index(index) => {
+ if let Some(arg) = args.by_index(index) {
+ used[index] = true;
+ if arg.kind.ident().is_some() {
+ // This was a named argument, but it was used as a positional argument.
+ numeric_refences_to_named_arg.push((index, span, used_as));
+ }
+ Ok(index)
+ } else {
+ // Doesn't exist as an explicit argument.
+ invalid_refs.push((index, span, used_as, kind));
+ Err(index)
+ }
+ }
+ ArgRef::Name(name, _span) => {
+ let name = Name::new_text_dont_use(SmolStr::new(name));
+ if let Some((index, _)) = args.by_name(&name) {
+ // Name found in `args`, so we resolve it to its index.
+ if index < args.explicit_args().len() {
+ // Mark it as used, if it was an explicit argument.
+ used[index] = true;
+ }
+ Ok(index)
+ } else {
+ // Name not found in `args`, so we add it as an implicitly captured argument.
+ if !is_direct_literal {
+ // For the moment capturing variables from format strings expanded from macros is
+ // disabled (see RFC #2795)
+ // FIXME: Diagnose
+ }
+ Ok(args.add(FormatArgument {
+ kind: FormatArgumentKind::Captured(name.clone()),
+ // FIXME: This is problematic, we might want to synthesize a dummy
+ // expression proper and/or desugar these.
+ expr: synth(name),
+ }))
+ }
+ }
+ };
+ FormatArgPosition { index, kind, span }
+ };
+
+ let mut template = Vec::new();
+ let mut unfinished_literal = String::new();
+ let mut placeholder_index = 0;
+
+ for piece in pieces {
+ match piece {
+ parse::Piece::String(s) => {
+ unfinished_literal.push_str(s);
+ }
+ parse::Piece::NextArgument(arg) => {
+ let parse::Argument { position, position_span, format } = *arg;
+ if !unfinished_literal.is_empty() {
+ template.push(FormatArgsPiece::Literal(
+ mem::take(&mut unfinished_literal).into_boxed_str(),
+ ));
+ }
+
+ let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s));
+ placeholder_index += 1;
+
+ let position_span = to_span(position_span);
+ let argument = match position {
+ parse::ArgumentImplicitlyIs(i) => lookup_arg(
+ ArgRef::Index(i),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Implicit,
+ ),
+ parse::ArgumentIs(i) => lookup_arg(
+ ArgRef::Index(i),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Number,
+ ),
+ parse::ArgumentNamed(name) => lookup_arg(
+ ArgRef::Name(name, position_span),
+ position_span,
+ Placeholder(span),
+ FormatArgPositionKind::Named,
+ ),
+ };
+
+ let alignment = match format.align {
+ parse::AlignUnknown => None,
+ parse::AlignLeft => Some(FormatAlignment::Left),
+ parse::AlignRight => Some(FormatAlignment::Right),
+ parse::AlignCenter => Some(FormatAlignment::Center),
+ };
+
+ let format_trait = match format.ty {
+ "" => FormatTrait::Display,
+ "?" => FormatTrait::Debug,
+ "e" => FormatTrait::LowerExp,
+ "E" => FormatTrait::UpperExp,
+ "o" => FormatTrait::Octal,
+ "p" => FormatTrait::Pointer,
+ "b" => FormatTrait::Binary,
+ "x" => FormatTrait::LowerHex,
+ "X" => FormatTrait::UpperHex,
+ _ => {
+ // FIXME: Diagnose
+ FormatTrait::Display
+ }
+ };
+
+ let precision_span = format.precision_span.and_then(to_span);
+ let precision = match format.precision {
+ parse::CountIs(n) => Some(FormatCount::Literal(n)),
+ parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Name(name, to_span(name_span)),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Named,
+ ))),
+ parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Number,
+ ))),
+ parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ precision_span,
+ Precision,
+ FormatArgPositionKind::Implicit,
+ ))),
+ parse::CountImplied => None,
+ };
+
+ let width_span = format.width_span.and_then(to_span);
+ let width = match format.width {
+ parse::CountIs(n) => Some(FormatCount::Literal(n)),
+ parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Name(name, to_span(name_span)),
+ width_span,
+ Width,
+ FormatArgPositionKind::Named,
+ ))),
+ parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg(
+ ArgRef::Index(i),
+ width_span,
+ Width,
+ FormatArgPositionKind::Number,
+ ))),
+ parse::CountIsStar(_) => unreachable!(),
+ parse::CountImplied => None,
+ };
+
+ template.push(FormatArgsPiece::Placeholder(FormatPlaceholder {
+ argument,
+ span,
+ format_trait,
+ format_options: FormatOptions {
+ fill: format.fill,
+ alignment,
+ sign: format.sign.map(|s| match s {
+ parse::Sign::Plus => FormatSign::Plus,
+ parse::Sign::Minus => FormatSign::Minus,
+ }),
+ alternate: format.alternate,
+ zero_pad: format.zero_pad,
+ debug_hex: format.debug_hex.map(|s| match s {
+ parse::DebugHex::Lower => FormatDebugHex::Lower,
+ parse::DebugHex::Upper => FormatDebugHex::Upper,
+ }),
+ precision,
+ width,
+ },
+ }));
+ }
+ }
+ }
+
+ if !unfinished_literal.is_empty() {
+ template.push(FormatArgsPiece::Literal(unfinished_literal.into_boxed_str()));
+ }
+
+ if !invalid_refs.is_empty() {
+ // FIXME: Diagnose
+ }
+
+ let unused = used
+ .iter()
+ .enumerate()
+ .filter(|&(_, used)| !used)
+ .map(|(i, _)| {
+ let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
+ (args.explicit_args()[i].expr, named)
+ })
+ .collect::<Vec<_>>();
+
+ if !unused.is_empty() {
+ // FIXME: Diagnose
+ }
+
+ FormatArgs { template: template.into_boxed_slice(), arguments: args.finish() }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct FormatArgumentsCollector {
+ arguments: Vec<FormatArgument>,
+ num_unnamed_args: usize,
+ num_explicit_args: usize,
+ names: Vec<(Name, usize)>,
+}
+
+impl FormatArgumentsCollector {
+ pub(crate) fn finish(self) -> FormatArguments {
+ FormatArguments {
+ arguments: self.arguments.into_boxed_slice(),
+ num_unnamed_args: self.num_unnamed_args,
+ num_explicit_args: self.num_explicit_args,
+ names: self.names.into_boxed_slice(),
+ }
+ }
+
+ pub fn new() -> Self {
+ Self { arguments: vec![], names: vec![], num_unnamed_args: 0, num_explicit_args: 0 }
+ }
+
+ pub fn add(&mut self, arg: FormatArgument) -> usize {
+ let index = self.arguments.len();
+ if let Some(name) = arg.kind.ident() {
+ self.names.push((name.clone(), index));
+ } else if self.names.is_empty() {
+ // Only count the unnamed args before the first named arg.
+ // (Any later ones are errors.)
+ self.num_unnamed_args += 1;
+ }
+ if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
+ // This is an explicit argument.
+ // Make sure that all arguments so far are explicit.
+ assert_eq!(
+ self.num_explicit_args,
+ self.arguments.len(),
+ "captured arguments must be added last"
+ );
+ self.num_explicit_args += 1;
+ }
+ self.arguments.push(arg);
+ index
+ }
+
+ pub fn by_name(&self, name: &Name) -> Option<(usize, &FormatArgument)> {
+ let &(_, i) = self.names.iter().find(|(n, _)| n == name)?;
+ Some((i, &self.arguments[i]))
+ }
+
+ pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
+ (i < self.num_explicit_args).then(|| &self.arguments[i])
+ }
+
+ pub fn unnamed_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_unnamed_args]
+ }
+
+ pub fn named_args(&self) -> &[FormatArgument] {
+ &self.arguments[self.num_unnamed_args..self.num_explicit_args]
+ }
+
+ pub fn explicit_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_explicit_args]
+ }
+
+ pub fn all_args(&self) -> &[FormatArgument] {
+ &self.arguments[..]
+ }
+
+ pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
+ &mut self.arguments
+ }
+}
+
+impl FormatArgumentKind {
+ pub fn ident(&self) -> Option<&Name> {
+ match self {
+ Self::Normal => None,
+ Self::Named(id) => Some(id),
+ Self::Captured(id) => Some(id),
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index 57f023ef3..75adf21ab 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -393,6 +393,17 @@ impl ConstRef {
Self::Scalar(LiteralConstRef::Unknown)
}
+ pub(crate) fn from_const_param(
+ lower_ctx: &LowerCtx<'_>,
+ param: &ast::ConstParam,
+ ) -> Option<Self> {
+ let default = param.default_val();
+ match default {
+ Some(_) => Some(Self::from_const_arg(lower_ctx, default)),
+ None => None,
+ }
+ }
+
pub fn display<'a>(&'a self, db: &'a dyn ExpandDatabase) -> impl fmt::Display + 'a {
struct Display<'a>(&'a dyn ExpandDatabase, &'a ConstRef);
impl fmt::Display for Display<'_> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
index 4b2e5041a..44b7f1b4f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs
@@ -11,6 +11,7 @@ use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
use triomphe::Arc;
+use crate::item_scope::ImportOrExternCrate;
use crate::{
db::DefDatabase, item_scope::ItemInNs, nameres::DefMap, visibility::Visibility, AssocItemId,
ModuleDefId, ModuleId, TraitId,
@@ -29,6 +30,10 @@ pub struct ImportInfo {
pub container: ModuleId,
/// Whether the import is a trait associated item or not.
pub is_trait_assoc_item: bool,
+ /// Whether this item is annotated with `#[doc(hidden)]`.
+ pub is_doc_hidden: bool,
+ /// Whether this item is annotated with `#[unstable(..)]`.
+ pub is_unstable: bool,
}
/// A map from publicly exported items to its name.
@@ -109,23 +114,71 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
});
for (name, per_ns) in visible_items {
- for item in per_ns.iter_items() {
+ for (item, import) in per_ns.iter_items() {
+ let attr_id = if let Some(import) = import {
+ match import {
+ ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
+ ImportOrExternCrate::Import(id) => Some(id.import.into()),
+ }
+ } else {
+ match item {
+ ItemInNs::Types(id) | ItemInNs::Values(id) => id.try_into().ok(),
+ ItemInNs::Macros(id) => Some(id.into()),
+ }
+ };
+ let status @ (is_doc_hidden, is_unstable) =
+ attr_id.map_or((false, false), |attr_id| {
+ let attrs = db.attrs(attr_id);
+ (attrs.has_doc_hidden(), attrs.is_unstable())
+ });
+
let import_info = ImportInfo {
name: name.clone(),
container: module,
is_trait_assoc_item: false,
+ is_doc_hidden,
+ is_unstable,
};
match depth_map.entry(item) {
- Entry::Vacant(entry) => {
- entry.insert(depth);
- }
+ Entry::Vacant(entry) => _ = entry.insert((depth, status)),
Entry::Occupied(mut entry) => {
- if depth < *entry.get() {
- entry.insert(depth);
- } else {
+ let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get();
+ (depth, occ_depth);
+ let overwrite = match (
+ is_doc_hidden,
+ occ_is_doc_hidden,
+ is_unstable,
+ occ_is_unstable,
+ ) {
+ // no change of hiddeness or unstableness
+ (true, true, true, true)
+ | (true, true, false, false)
+ | (false, false, true, true)
+ | (false, false, false, false) => depth < occ_depth,
+
+ // either less hidden or less unstable, accept
+ (true, true, false, true)
+ | (false, true, true, true)
+ | (false, true, false, true)
+ | (false, true, false, false)
+ | (false, false, false, true) => true,
+ // more hidden or unstable, discard
+ (true, true, true, false)
+ | (true, false, true, true)
+ | (true, false, true, false)
+ | (true, false, false, false)
+ | (false, false, true, false) => false,
+
+ // exchanges doc(hidden) for unstable (and vice-versa),
+ (true, false, false, true) | (false, true, true, false) => {
+ depth < occ_depth
+ }
+ };
+ if !overwrite {
continue;
}
+ entry.insert((depth, status));
}
}
@@ -150,7 +203,7 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
}
}
}
-
+ map.shrink_to_fit();
map
}
@@ -162,10 +215,10 @@ fn collect_trait_assoc_items(
trait_import_info: &ImportInfo,
) {
let _p = profile::span("collect_trait_assoc_items");
- for (assoc_item_name, item) in &db.trait_data(tr).items {
+ for &(ref assoc_item_name, item) in &db.trait_data(tr).items {
let module_def_id = match item {
- AssocItemId::FunctionId(f) => ModuleDefId::from(*f),
- AssocItemId::ConstId(c) => ModuleDefId::from(*c),
+ AssocItemId::FunctionId(f) => ModuleDefId::from(f),
+ AssocItemId::ConstId(c) => ModuleDefId::from(c),
// cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias`
// qualifier, ergo no need to store it for imports in import_map
AssocItemId::TypeAliasId(_) => {
@@ -179,10 +232,13 @@ fn collect_trait_assoc_items(
ItemInNs::Values(module_def_id)
};
+ let attrs = &db.attrs(item.into());
let assoc_item_info = ImportInfo {
container: trait_import_info.container,
name: assoc_item_name.clone(),
is_trait_assoc_item: true,
+ is_doc_hidden: attrs.has_doc_hidden(),
+ is_unstable: attrs.is_unstable(),
};
map.insert(assoc_item, assoc_item_info);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
index 873accafb..7c11fb9d1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs
@@ -6,6 +6,7 @@ use std::collections::hash_map::Entry;
use base_db::CrateId;
use hir_expand::{attrs::AttrId, db::ExpandDatabase, name::Name, AstId, MacroCallId};
use itertools::Itertools;
+use la_arena::Idx;
use once_cell::sync::Lazy;
use profile::Count;
use rustc_hash::{FxHashMap, FxHashSet};
@@ -15,16 +16,10 @@ use syntax::ast;
use crate::{
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ConstId,
- ExternCrateId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
- UseId,
+ ExternCrateId, HasModule, ImplId, LocalModuleId, Lookup, MacroId, ModuleDefId, ModuleId,
+ TraitId, UseId,
};
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum ImportType {
- Glob,
- Named,
-}
-
#[derive(Debug, Default)]
pub struct PerNsGlobImports {
types: FxHashSet<(LocalModuleId, Name)>,
@@ -32,15 +27,50 @@ pub struct PerNsGlobImports {
macros: FxHashSet<(LocalModuleId, Name)>,
}
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrExternCrate {
+ Import(ImportId),
+ ExternCrate(ExternCrateId),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub(crate) enum ImportType {
+ Import(ImportId),
+ Glob(UseId),
+ ExternCrate(ExternCrateId),
+}
+
+impl ImportOrExternCrate {
+ pub fn into_import(self) -> Option<ImportId> {
+ match self {
+ ImportOrExternCrate::Import(it) => Some(it),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum ImportOrDef {
+ Import(ImportId),
+ ExternCrate(ExternCrateId),
+ Def(ModuleDefId),
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub struct ImportId {
+ pub import: UseId,
+ pub idx: Idx<ast::UseTree>,
+}
+
#[derive(Debug, Default, PartialEq, Eq)]
pub struct ItemScope {
_c: Count<Self>,
/// Defs visible in this scope. This includes `declarations`, but also
- /// imports.
- types: FxHashMap<Name, (ModuleDefId, Visibility)>,
- values: FxHashMap<Name, (ModuleDefId, Visibility)>,
- macros: FxHashMap<Name, (MacroId, Visibility)>,
+ /// imports. The imports belong to this module and can be resolved by using them on
+ /// the `use_imports_*` fields.
+ types: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+ values: FxHashMap<Name, (ModuleDefId, Visibility, Option<ImportId>)>,
+ macros: FxHashMap<Name, (MacroId, Visibility, Option<ImportId>)>,
unresolved: FxHashSet<Name>,
/// The defs declared in this scope. Each def has a single scope where it is
@@ -50,7 +80,14 @@ pub struct ItemScope {
impls: Vec<ImplId>,
unnamed_consts: Vec<ConstId>,
/// Traits imported via `use Trait as _;`.
- unnamed_trait_imports: FxHashMap<TraitId, Visibility>,
+ unnamed_trait_imports: FxHashMap<TraitId, (Visibility, Option<ImportId>)>,
+
+ // the resolutions of the imports of this scope
+ use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
+ use_imports_values: FxHashMap<ImportId, ImportOrDef>,
+ use_imports_macros: FxHashMap<ImportId, ImportOrDef>,
+
+ use_decls: Vec<UseId>,
extern_crate_decls: Vec<ExternCrateId>,
/// Macros visible in current module in legacy textual scope
///
@@ -82,7 +119,7 @@ struct DeriveMacroInvocation {
pub(crate) static BUILTIN_SCOPE: Lazy<FxHashMap<Name, PerNs>> = Lazy::new(|| {
BuiltinType::ALL
.iter()
- .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public)))
+ .map(|(name, ty)| (name.clone(), PerNs::types((*ty).into(), Visibility::Public, None)))
.collect()
});
@@ -105,11 +142,77 @@ impl ItemScope {
.chain(self.values.keys())
.chain(self.macros.keys())
.chain(self.unresolved.iter())
- .sorted()
.unique()
+ .sorted()
.map(move |name| (name, self.get(name)))
}
+ pub fn imports(&self) -> impl Iterator<Item = ImportId> + '_ {
+ self.use_imports_types
+ .keys()
+ .copied()
+ .filter_map(ImportOrExternCrate::into_import)
+ .chain(self.use_imports_values.keys().copied())
+ .chain(self.use_imports_macros.keys().copied())
+ .unique()
+ .sorted()
+ }
+
+ pub fn fully_resolve_import(&self, db: &dyn DefDatabase, mut import: ImportId) -> PerNs {
+ let mut res = PerNs::none();
+
+ let mut def_map;
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_macros.get(&import) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(ModuleDefId::MacroId(def)) => {
+ res.macros = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(def) => {
+ res.types = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ let mut scope = self;
+ while let Some(&m) = scope.use_imports_values.get(&import) {
+ match m {
+ ImportOrDef::Import(i) => {
+ let module_id = i.import.lookup(db).container;
+ def_map = module_id.def_map(db);
+ scope = &def_map[module_id.local_id].scope;
+ import = i;
+ }
+ ImportOrDef::Def(def) => {
+ res.values = Some((def, Visibility::Public, None));
+ break;
+ }
+ _ => break,
+ }
+ }
+ res
+ }
+
pub fn declarations(&self) -> impl Iterator<Item = ModuleDefId> + '_ {
self.declarations.iter().copied()
}
@@ -121,8 +224,7 @@ impl ItemScope {
}
pub fn use_decls(&self) -> impl Iterator<Item = UseId> + ExactSizeIterator + '_ {
- // FIXME: to be implemented
- std::iter::empty()
+ self.use_decls.iter().copied()
}
pub fn impls(&self) -> impl Iterator<Item = ImplId> + ExactSizeIterator + '_ {
@@ -132,13 +234,13 @@ impl ItemScope {
pub fn values(
&self,
) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
- self.values.values().copied()
+ self.values.values().copied().map(|(a, b, _)| (a, b))
}
- pub fn types(
+ pub(crate) fn types(
&self,
) -> impl Iterator<Item = (ModuleDefId, Visibility)> + ExactSizeIterator + '_ {
- self.types.values().copied()
+ self.types.values().copied().map(|(def, vis, _)| (def, vis))
}
pub fn unnamed_consts(&self) -> impl Iterator<Item = ConstId> + '_ {
@@ -165,33 +267,55 @@ impl ItemScope {
}
pub(crate) fn type_(&self, name: &Name) -> Option<(ModuleDefId, Visibility)> {
- self.types.get(name).copied()
+ self.types.get(name).copied().map(|(a, b, _)| (a, b))
}
/// XXX: this is O(N) rather than O(1), try to not introduce new usages.
pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility)> {
- let (def, mut iter) = match item {
- ItemInNs::Macros(def) => {
- return self.macros.iter().find_map(|(name, &(other_def, vis))| {
- (other_def == def).then_some((name, vis))
- });
- }
- ItemInNs::Types(def) => (def, self.types.iter()),
- ItemInNs::Values(def) => (def, self.values.iter()),
- };
- iter.find_map(|(name, &(other_def, vis))| (other_def == def).then_some((name, vis)))
+ match item {
+ ItemInNs::Macros(def) => self
+ .macros
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+ ItemInNs::Types(def) => self
+ .types
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+
+ ItemInNs::Values(def) => self
+ .values
+ .iter()
+ .find_map(|(name, &(other_def, vis, _))| (other_def == def).then_some((name, vis))),
+ }
}
pub(crate) fn traits(&self) -> impl Iterator<Item = TraitId> + '_ {
self.types
.values()
- .filter_map(|&(def, _)| match def {
+ .filter_map(|&(def, _, _)| match def {
ModuleDefId::TraitId(t) => Some(t),
_ => None,
})
.chain(self.unnamed_trait_imports.keys().copied())
}
+ pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
+ self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
+ self.unnamed_trait_imports.iter().map(|(tr, (vis, i))| {
+ (
+ None,
+ PerNs::types(
+ ModuleDefId::TraitId(*tr),
+ *vis,
+ i.map(ImportOrExternCrate::Import),
+ ),
+ )
+ }),
+ )
+ }
+}
+
+impl ItemScope {
pub(crate) fn declare(&mut self, def: ModuleDefId) {
self.declarations.push(def)
}
@@ -277,12 +401,14 @@ impl ItemScope {
})
}
+ // FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
- self.unnamed_trait_imports.get(&tr).copied()
+ self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a)
}
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
- self.unnamed_trait_imports.insert(tr, vis);
+ // FIXME: import
+ self.unnamed_trait_imports.insert(tr, (vis, None));
}
pub(crate) fn push_res_with_import(
@@ -290,51 +416,187 @@ impl ItemScope {
glob_imports: &mut PerNsGlobImports,
lookup: (LocalModuleId, Name),
def: PerNs,
- def_import_type: ImportType,
+ import: Option<ImportType>,
) -> bool {
let mut changed = false;
- macro_rules! check_changed {
- (
- $changed:ident,
- ( $this:ident / $def:ident ) . $field:ident,
- $glob_imports:ident [ $lookup:ident ],
- $def_import_type:ident
- ) => {{
- if let Some(fld) = $def.$field {
- let existing = $this.$field.entry($lookup.1.clone());
- match existing {
- Entry::Vacant(entry) => {
- match $def_import_type {
- ImportType::Glob => {
- $glob_imports.$field.insert($lookup.clone());
+ // FIXME: Document and simplify this
+
+ if let Some(mut fld) = def.types {
+ let existing = self.types.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.types.insert(lookup.clone());
+ }
+ _ => _ = glob_imports.types.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::ExternCrate(extern_crate)) => {
+ Some(ImportOrExternCrate::ExternCrate(extern_crate))
+ }
+ Some(ImportType::Import(import)) => {
+ Some(ImportOrExternCrate::Import(import))
+ }
+ None | Some(ImportType::Glob(_)) => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_types.insert(
+ import,
+ match prev {
+ Some(ImportOrExternCrate::Import(import)) => {
+ ImportOrDef::Import(import)
}
- ImportType::Named => {
- $glob_imports.$field.remove(&$lookup);
+ Some(ImportOrExternCrate::ExternCrate(import)) => {
+ ImportOrDef::ExternCrate(import)
}
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.types.remove(&lookup) {
+ let import = match import {
+ Some(ImportType::ExternCrate(extern_crate)) => {
+ Some(ImportOrExternCrate::ExternCrate(extern_crate))
+ }
+ Some(ImportType::Import(import)) => {
+ Some(ImportOrExternCrate::Import(import))
}
+ None | Some(ImportType::Glob(_)) => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_types.insert(
+ import,
+ match prev {
+ Some(ImportOrExternCrate::Import(import)) => {
+ ImportOrDef::Import(import)
+ }
+ Some(ImportOrExternCrate::ExternCrate(import)) => {
+ ImportOrDef::ExternCrate(import)
+ }
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ cov_mark::hit!(import_shadowed);
+ entry.insert(fld);
+ changed = true;
+ }
+ }
+ _ => {}
+ }
+ }
- entry.insert(fld);
- $changed = true;
+ if let Some(mut fld) = def.values {
+ let existing = self.values.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.values.insert(lookup.clone());
}
- Entry::Occupied(mut entry)
- if matches!($def_import_type, ImportType::Named) =>
- {
- if $glob_imports.$field.remove(&$lookup) {
- cov_mark::hit!(import_shadowed);
- entry.insert(fld);
- $changed = true;
- }
+ _ => _ = glob_imports.values.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_values.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.values.remove(&lookup) {
+ cov_mark::hit!(import_shadowed);
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_values.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0),
+ },
+ );
}
- _ => {}
+ entry.insert(fld);
+ changed = true;
}
}
- }};
+ _ => {}
+ }
}
- check_changed!(changed, (self / def).types, glob_imports[lookup], def_import_type);
- check_changed!(changed, (self / def).values, glob_imports[lookup], def_import_type);
- check_changed!(changed, (self / def).macros, glob_imports[lookup], def_import_type);
+ if let Some(mut fld) = def.macros {
+ let existing = self.macros.entry(lookup.1.clone());
+ match existing {
+ Entry::Vacant(entry) => {
+ match import {
+ Some(ImportType::Glob(_)) => {
+ glob_imports.macros.insert(lookup.clone());
+ }
+ _ => _ = glob_imports.macros.remove(&lookup),
+ }
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_macros.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0.into()),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
+ if glob_imports.macros.remove(&lookup) {
+ cov_mark::hit!(import_shadowed);
+ let import = match import {
+ Some(ImportType::Import(import)) => Some(import),
+ _ => None,
+ };
+ let prev = std::mem::replace(&mut fld.2, import);
+ if let Some(import) = import {
+ self.use_imports_macros.insert(
+ import,
+ match prev {
+ Some(import) => ImportOrDef::Import(import),
+ None => ImportOrDef::Def(fld.0.into()),
+ },
+ );
+ }
+ entry.insert(fld);
+ changed = true;
+ }
+ }
+ _ => {}
+ }
+ }
if def.is_none() && self.unresolved.insert(lookup.1) {
changed = true;
@@ -343,27 +605,18 @@ impl ItemScope {
changed
}
- pub(crate) fn resolutions(&self) -> impl Iterator<Item = (Option<Name>, PerNs)> + '_ {
- self.entries().map(|(name, res)| (Some(name.clone()), res)).chain(
- self.unnamed_trait_imports
- .iter()
- .map(|(tr, vis)| (None, PerNs::types(ModuleDefId::TraitId(*tr), *vis))),
- )
- }
-
/// Marks everything that is not a procedural macro as private to `this_module`.
pub(crate) fn censor_non_proc_macros(&mut self, this_module: ModuleId) {
self.types
.values_mut()
- .chain(self.values.values_mut())
+ .map(|(def, vis, _)| (def, vis))
+ .chain(self.values.values_mut().map(|(def, vis, _)| (def, vis)))
.map(|(_, v)| v)
- .chain(self.unnamed_trait_imports.values_mut())
+ .chain(self.unnamed_trait_imports.values_mut().map(|(vis, _)| vis))
.for_each(|vis| *vis = Visibility::Module(this_module));
- for (mac, vis) in self.macros.values_mut() {
- if let MacroId::ProcMacroId(_) = mac {
- // FIXME: Technically this is insufficient since reexports of proc macros are also
- // forbidden. Practically nobody does that.
+ for (mac, vis, import) in self.macros.values_mut() {
+ if matches!(mac, MacroId::ProcMacroId(_) if import.is_none()) {
continue;
}
@@ -382,14 +635,25 @@ impl ItemScope {
name.map_or("_".to_string(), |name| name.display(db).to_string())
);
- if def.types.is_some() {
+ if let Some((.., i)) = def.types {
buf.push_str(" t");
+ match i {
+ Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
+ Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
+ None => (),
+ }
}
- if def.values.is_some() {
+ if let Some((.., i)) = def.values {
buf.push_str(" v");
+ if i.is_some() {
+ buf.push('i');
+ }
}
- if def.macros.is_some() {
+ if let Some((.., i)) = def.macros {
buf.push_str(" m");
+ if i.is_some() {
+ buf.push('i');
+ }
}
if def.is_none() {
buf.push_str(" _");
@@ -415,10 +679,17 @@ impl ItemScope {
attr_macros,
derive_macros,
extern_crate_decls,
+ use_decls,
+ use_imports_values,
+ use_imports_types,
+ use_imports_macros,
} = self;
types.shrink_to_fit();
values.shrink_to_fit();
macros.shrink_to_fit();
+ use_imports_types.shrink_to_fit();
+ use_imports_values.shrink_to_fit();
+ use_imports_macros.shrink_to_fit();
unresolved.shrink_to_fit();
declarations.shrink_to_fit();
impls.shrink_to_fit();
@@ -428,32 +699,44 @@ impl ItemScope {
attr_macros.shrink_to_fit();
derive_macros.shrink_to_fit();
extern_crate_decls.shrink_to_fit();
+ use_decls.shrink_to_fit();
}
}
impl PerNs {
- pub(crate) fn from_def(def: ModuleDefId, v: Visibility, has_constructor: bool) -> PerNs {
+ pub(crate) fn from_def(
+ def: ModuleDefId,
+ v: Visibility,
+ has_constructor: bool,
+ import: Option<ImportOrExternCrate>,
+ ) -> PerNs {
match def {
- ModuleDefId::ModuleId(_) => PerNs::types(def, v),
- ModuleDefId::FunctionId(_) => PerNs::values(def, v),
+ ModuleDefId::ModuleId(_) => PerNs::types(def, v, import),
+ ModuleDefId::FunctionId(_) => {
+ PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+ }
ModuleDefId::AdtId(adt) => match adt {
- AdtId::UnionId(_) => PerNs::types(def, v),
- AdtId::EnumId(_) => PerNs::types(def, v),
+ AdtId::UnionId(_) => PerNs::types(def, v, import),
+ AdtId::EnumId(_) => PerNs::types(def, v, import),
AdtId::StructId(_) => {
if has_constructor {
- PerNs::both(def, def, v)
+ PerNs::both(def, def, v, import)
} else {
- PerNs::types(def, v)
+ PerNs::types(def, v, import)
}
}
},
- ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v),
- ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => PerNs::values(def, v),
- ModuleDefId::TraitId(_) => PerNs::types(def, v),
- ModuleDefId::TraitAliasId(_) => PerNs::types(def, v),
- ModuleDefId::TypeAliasId(_) => PerNs::types(def, v),
- ModuleDefId::BuiltinType(_) => PerNs::types(def, v),
- ModuleDefId::MacroId(mac) => PerNs::macros(mac, v),
+ ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import),
+ ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => {
+ PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
+ }
+ ModuleDefId::TraitId(_) => PerNs::types(def, v, import),
+ ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
+ ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
+ ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
+ ModuleDefId::MacroId(mac) => {
+ PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import))
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
index c9b0f75f1..4c812b62a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs
@@ -64,11 +64,11 @@ use triomphe::Arc;
use crate::{
attr::Attrs,
db::DefDatabase,
- generics::GenericParams,
+ generics::{GenericParams, LifetimeParamData, TypeOrConstParamData},
path::{path, AssociatedTypeBinding, GenericArgs, ImportAlias, ModPath, Path, PathKind},
type_ref::{Mutability, TraitRef, TypeBound, TypeRef},
visibility::RawVisibility,
- BlockId,
+ BlockId, Lookup,
};
#[derive(Copy, Clone, Eq, PartialEq)]
@@ -143,6 +143,16 @@ impl ItemTree {
Arc::new(item_tree)
}
+ pub(crate) fn block_item_tree_query(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
+ let loc = block.lookup(db);
+ let block = loc.ast_id.to_node(db.upcast());
+
+ let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
+ let mut item_tree = ctx.lower_block(&block);
+ item_tree.shrink_to_fit();
+ Arc::new(item_tree)
+ }
+
/// Returns an iterator over all items located at the top level of the `HirFileId` this
/// `ItemTree` was created from.
pub fn top_level_items(&self) -> &[ModItem] {
@@ -167,7 +177,7 @@ impl ItemTree {
}
pub fn pretty_print(&self, db: &dyn DefDatabase) -> String {
- pretty::print_item_tree(db.upcast(), self)
+ pretty::print_item_tree(db, self)
}
fn data(&self) -> &ItemTreeData {
@@ -178,13 +188,6 @@ impl ItemTree {
self.data.get_or_insert_with(Box::default)
}
- fn block_item_tree(db: &dyn DefDatabase, block: BlockId) -> Arc<ItemTree> {
- let loc = db.lookup_intern_block(block);
- let block = loc.ast_id.to_node(db.upcast());
- let ctx = lower::Ctx::new(db, loc.ast_id.file_id);
- Arc::new(ctx.lower_block(&block))
- }
-
fn shrink_to_fit(&mut self) {
if let Some(data) = &mut self.data {
let ItemTreeData {
@@ -296,10 +299,12 @@ pub enum AttrOwner {
Variant(Idx<Variant>),
Field(Idx<Field>),
Param(Idx<Param>),
+ TypeOrConstParamData(Idx<TypeOrConstParamData>),
+ LifetimeParamData(Idx<LifetimeParamData>),
}
macro_rules! from_attrs {
- ( $( $var:ident($t:ty) ),+ ) => {
+ ( $( $var:ident($t:ty) ),+ $(,)? ) => {
$(
impl From<$t> for AttrOwner {
fn from(t: $t) -> AttrOwner {
@@ -310,7 +315,14 @@ macro_rules! from_attrs {
};
}
-from_attrs!(ModItem(ModItem), Variant(Idx<Variant>), Field(Idx<Field>), Param(Idx<Param>));
+from_attrs!(
+ ModItem(ModItem),
+ Variant(Idx<Variant>),
+ Field(Idx<Field>),
+ Param(Idx<Param>),
+ TypeOrConstParamData(Idx<TypeOrConstParamData>),
+ LifetimeParamData(Idx<LifetimeParamData>),
+);
/// Trait implemented by all item nodes in the item tree.
pub trait ItemTreeNode: Clone {
@@ -373,7 +385,7 @@ impl TreeId {
pub(crate) fn item_tree(&self, db: &dyn DefDatabase) -> Arc<ItemTree> {
match self.block {
- Some(block) => ItemTree::block_item_tree(db, block),
+ Some(block) => db.block_item_tree_query(block),
None => db.file_item_tree(self.file),
}
}
@@ -761,6 +773,19 @@ impl Use {
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
source_map[index].clone()
}
+ /// Maps a `UseTree` contained in this import back to its AST node.
+ pub fn use_tree_source_map(
+ &self,
+ db: &dyn DefDatabase,
+ file_id: HirFileId,
+ ) -> Arena<ast::UseTree> {
+ // Re-lower the AST item and get the source map.
+ // Note: The AST unwraps are fine, since if they fail we should have never obtained `index`.
+ let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
+ let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
+ let hygiene = Hygiene::new(db.upcast(), file_id);
+ lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -785,7 +810,7 @@ impl UseTree {
fn expand_impl(
&self,
prefix: Option<ModPath>,
- cb: &mut dyn FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
+ cb: &mut impl FnMut(Idx<ast::UseTree>, ModPath, ImportKind, Option<ImportAlias>),
) {
fn concat_mod_paths(
prefix: Option<ModPath>,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index 7b898e62d..e4702c113 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -77,6 +77,9 @@ impl<'a> Ctx<'a> {
}
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
+ self.tree
+ .attrs
+ .insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.hygiene()));
self.tree.top_level = block
.statements()
.filter_map(|stmt| match stmt {
@@ -602,7 +605,21 @@ impl<'a> Ctx<'a> {
generics.fill_bounds(&self.body_ctx, bounds, Either::Left(self_param));
}
- generics.fill(&self.body_ctx, node);
+ let add_param_attrs = |item, param| {
+ let attrs = RawAttrs::new(self.db.upcast(), &param, self.body_ctx.hygiene());
+ // This is identical to the body of `Ctx::add_attrs()` but we can't call that here
+ // because it requires `&mut self` and the call to `generics.fill()` below also
+ // references `self`.
+ match self.tree.attrs.entry(item) {
+ Entry::Occupied(mut entry) => {
+ *entry.get_mut() = entry.get().merge(attrs);
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(attrs);
+ }
+ }
+ };
+ generics.fill(&self.body_ctx, node, add_param_attrs);
generics.shrink_to_fit();
Interned::new(generics)
@@ -763,7 +780,7 @@ impl UseTreeLowering<'_> {
}
}
-pub(super) fn lower_use_tree(
+pub(crate) fn lower_use_tree(
db: &dyn DefDatabase,
hygiene: &Hygiene,
tree: ast::UseTree,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
index da30830fe..417bd37c8 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs
@@ -2,8 +2,6 @@
use std::fmt::{self, Write};
-use hir_expand::db::ExpandDatabase;
-
use crate::{
generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget},
pretty::{print_path, print_type_bounds, print_type_ref},
@@ -12,11 +10,11 @@ use crate::{
use super::*;
-pub(super) fn print_item_tree(db: &dyn ExpandDatabase, tree: &ItemTree) -> String {
+pub(super) fn print_item_tree(db: &dyn DefDatabase, tree: &ItemTree) -> String {
let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true };
if let Some(attrs) = tree.attrs.get(&AttrOwner::TopLevel) {
- p.print_attrs(attrs, true);
+ p.print_attrs(attrs, true, "\n");
}
p.blank();
@@ -45,7 +43,7 @@ macro_rules! wln {
}
struct Printer<'a> {
- db: &'a dyn ExpandDatabase,
+ db: &'a dyn DefDatabase,
tree: &'a ItemTree,
buf: String,
indent_level: usize,
@@ -84,28 +82,29 @@ impl Printer<'_> {
}
}
- fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool) {
+ fn print_attrs(&mut self, attrs: &RawAttrs, inner: bool, separated_by: &str) {
let inner = if inner { "!" } else { "" };
for attr in &**attrs {
- wln!(
+ w!(
self,
- "#{}[{}{}]",
+ "#{}[{}{}]{}",
inner,
- attr.path.display(self.db),
+ attr.path.display(self.db.upcast()),
attr.input.as_ref().map(|it| it.to_string()).unwrap_or_default(),
+ separated_by,
);
}
}
- fn print_attrs_of(&mut self, of: impl Into<AttrOwner>) {
+ fn print_attrs_of(&mut self, of: impl Into<AttrOwner>, separated_by: &str) {
if let Some(attrs) = self.tree.attrs.get(&of.into()) {
- self.print_attrs(attrs, false);
+ self.print_attrs(attrs, false, separated_by);
}
}
fn print_visibility(&mut self, vis: RawVisibilityId) {
match &self.tree[vis] {
- RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db)),
+ RawVisibility::Module(path) => w!(self, "pub({}) ", path.display(self.db.upcast())),
RawVisibility::Public => w!(self, "pub "),
};
}
@@ -118,9 +117,9 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
- this.print_attrs_of(field);
+ this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -132,9 +131,9 @@ impl Printer<'_> {
self.indented(|this| {
for field in fields.clone() {
let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
- this.print_attrs_of(field);
+ this.print_attrs_of(field, "\n");
this.print_visibility(*visibility);
- w!(this, "{}: ", name.display(self.db));
+ w!(this, "{}: ", name.display(self.db.upcast()));
this.print_type_ref(type_ref);
wln!(this, ",");
}
@@ -167,20 +166,20 @@ impl Printer<'_> {
fn print_use_tree(&mut self, use_tree: &UseTree) {
match &use_tree.kind {
UseTreeKind::Single { path, alias } => {
- w!(self, "{}", path.display(self.db));
+ w!(self, "{}", path.display(self.db.upcast()));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
}
UseTreeKind::Glob { path } => {
if let Some(path) = path {
- w!(self, "{}::", path.display(self.db));
+ w!(self, "{}::", path.display(self.db.upcast()));
}
w!(self, "*");
}
UseTreeKind::Prefixed { prefix, list } => {
if let Some(prefix) = prefix {
- w!(self, "{}::", prefix.display(self.db));
+ w!(self, "{}::", prefix.display(self.db.upcast()));
}
w!(self, "{{");
for (i, tree) in list.iter().enumerate() {
@@ -195,7 +194,7 @@ impl Printer<'_> {
}
fn print_mod_item(&mut self, item: ModItem) {
- self.print_attrs_of(item);
+ self.print_attrs_of(item, "\n");
match item {
ModItem::Use(it) => {
@@ -208,7 +207,7 @@ impl Printer<'_> {
ModItem::ExternCrate(it) => {
let ExternCrate { name, alias, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "extern crate {}", name.display(self.db));
+ w!(self, "extern crate {}", name.display(self.db.upcast()));
if let Some(alias) = alias {
w!(self, " as {}", alias);
}
@@ -255,13 +254,13 @@ impl Printer<'_> {
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
- w!(self, "fn {}", name.display(self.db));
+ w!(self, "fn {}", name.display(self.db.upcast()));
self.print_generic_params(explicit_generic_params);
w!(self, "(");
if !params.is_empty() {
self.indented(|this| {
for param in params.clone() {
- this.print_attrs_of(param);
+ this.print_attrs_of(param, "\n");
match &this.tree[param] {
Param::Normal(ty) => {
if flags.contains(FnFlags::HAS_SELF_PARAM) {
@@ -289,7 +288,7 @@ impl Printer<'_> {
ModItem::Struct(it) => {
let Struct { visibility, name, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "struct {}", name.display(self.db));
+ w!(self, "struct {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -301,7 +300,7 @@ impl Printer<'_> {
ModItem::Union(it) => {
let Union { name, visibility, fields, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "union {}", name.display(self.db));
+ w!(self, "union {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_fields_and_where_clause(fields, generic_params);
if matches!(fields, Fields::Record(_)) {
@@ -313,14 +312,14 @@ impl Printer<'_> {
ModItem::Enum(it) => {
let Enum { name, visibility, variants, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "enum {}", name.display(self.db));
+ w!(self, "enum {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
let Variant { name, fields, ast_id: _ } = &this.tree[variant];
- this.print_attrs_of(variant);
- w!(this, "{}", name.display(self.db));
+ this.print_attrs_of(variant, "\n");
+ w!(this, "{}", name.display(self.db.upcast()));
this.print_fields(fields);
wln!(this, ",");
}
@@ -332,7 +331,7 @@ impl Printer<'_> {
self.print_visibility(*visibility);
w!(self, "const ");
match name {
- Some(name) => w!(self, "{}", name.display(self.db)),
+ Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_"),
}
w!(self, ": ");
@@ -346,7 +345,7 @@ impl Printer<'_> {
if *mutable {
w!(self, "mut ");
}
- w!(self, "{}: ", name.display(self.db));
+ w!(self, "{}: ", name.display(self.db.upcast()));
self.print_type_ref(type_ref);
w!(self, " = _;");
wln!(self);
@@ -368,7 +367,7 @@ impl Printer<'_> {
if *is_auto {
w!(self, "auto ");
}
- w!(self, "trait {}", name.display(self.db));
+ w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
@@ -381,7 +380,7 @@ impl Printer<'_> {
ModItem::TraitAlias(it) => {
let TraitAlias { name, visibility, generic_params, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "trait {}", name.display(self.db));
+ w!(self, "trait {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
w!(self, " = ");
self.print_where_clause(generic_params);
@@ -414,7 +413,7 @@ impl Printer<'_> {
let TypeAlias { name, visibility, bounds, type_ref, generic_params, ast_id: _ } =
&self.tree[it];
self.print_visibility(*visibility);
- w!(self, "type {}", name.display(self.db));
+ w!(self, "type {}", name.display(self.db.upcast()));
self.print_generic_params(generic_params);
if !bounds.is_empty() {
w!(self, ": ");
@@ -431,7 +430,7 @@ impl Printer<'_> {
ModItem::Mod(it) => {
let Mod { name, visibility, kind, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- w!(self, "mod {}", name.display(self.db));
+ w!(self, "mod {}", name.display(self.db.upcast()));
match kind {
ModKind::Inline { items } => {
w!(self, " {{");
@@ -449,16 +448,16 @@ impl Printer<'_> {
}
ModItem::MacroCall(it) => {
let MacroCall { path, ast_id: _, expand_to: _ } = &self.tree[it];
- wln!(self, "{}!(...);", path.display(self.db));
+ wln!(self, "{}!(...);", path.display(self.db.upcast()));
}
ModItem::MacroRules(it) => {
let MacroRules { name, ast_id: _ } = &self.tree[it];
- wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db));
+ wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
}
ModItem::MacroDef(it) => {
let MacroDef { name, visibility, ast_id: _ } = &self.tree[it];
self.print_visibility(*visibility);
- wln!(self, "macro {} {{ ... }}", name.display(self.db));
+ wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
}
}
@@ -484,25 +483,27 @@ impl Printer<'_> {
w!(self, "<");
let mut first = true;
- for (_, lt) in params.lifetimes.iter() {
+ for (idx, lt) in params.lifetimes.iter() {
if !first {
w!(self, ", ");
}
first = false;
- w!(self, "{}", lt.name.display(self.db));
+ self.print_attrs_of(idx, " ");
+ w!(self, "{}", lt.name.display(self.db.upcast()));
}
for (idx, x) in params.type_or_consts.iter() {
if !first {
w!(self, ", ");
}
first = false;
+ self.print_attrs_of(idx, " ");
match x {
TypeOrConstParamData::TypeParamData(ty) => match &ty.name {
- Some(name) => w!(self, "{}", name.display(self.db)),
+ Some(name) => w!(self, "{}", name.display(self.db.upcast())),
None => w!(self, "_anon_{}", idx.into_raw()),
},
TypeOrConstParamData::ConstParamData(konst) => {
- w!(self, "const {}: ", konst.name.display(self.db));
+ w!(self, "const {}: ", konst.name.display(self.db.upcast()));
self.print_type_ref(&konst.ty);
}
}
@@ -537,8 +538,8 @@ impl Printer<'_> {
wln!(
this,
"{}: {},",
- target.name.display(self.db),
- bound.name.display(self.db)
+ target.name.display(self.db.upcast()),
+ bound.name.display(self.db.upcast())
);
continue;
}
@@ -548,7 +549,7 @@ impl Printer<'_> {
if i != 0 {
w!(this, ", ");
}
- w!(this, "{}", lt.display(self.db));
+ w!(this, "{}", lt.display(self.db.upcast()));
}
w!(this, "> ");
(target, bound)
@@ -559,7 +560,7 @@ impl Printer<'_> {
WherePredicateTypeTarget::TypeRef(ty) => this.print_type_ref(ty),
WherePredicateTypeTarget::TypeOrConstParam(id) => {
match &params.type_or_consts[*id].name() {
- Some(name) => w!(this, "{}", name.display(self.db)),
+ Some(name) => w!(this, "{}", name.display(self.db.upcast())),
None => w!(this, "_anon_{}", id.into_raw()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index 5ded4b6b2..4180f8172 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -358,3 +358,15 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {}
"#]],
)
}
+
+#[test]
+fn generics_with_attributes() {
+ check(
+ r#"
+struct S<#[cfg(never)] T>;
+ "#,
+ expect![[r#"
+ pub(self) struct S<#[cfg(never)] T>;
+ "#]],
+ )
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 627479bb7..1ae6bd4c9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -2,6 +2,7 @@
//!
//! This attribute to tell the compiler about semi built-in std library
//! features, such as Fn family of traits.
+use hir_expand::name::Name;
use rustc_hash::FxHashMap;
use syntax::SmolStr;
use triomphe::Arc;
@@ -238,7 +239,17 @@ impl LangItem {
pub fn path(&self, db: &dyn DefDatabase, start_crate: CrateId) -> Option<Path> {
let t = db.lang_item(start_crate, *self)?;
- Some(Path::LangItem(t))
+ Some(Path::LangItem(t, None))
+ }
+
+ pub fn ty_rel_path(
+ &self,
+ db: &dyn DefDatabase,
+ start_crate: CrateId,
+ seg: Name,
+ ) -> Option<Path> {
+ let t = db.lang_item(start_crate, *self)?;
+ Some(Path::LangItem(t, Some(seg)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 1901db8a0..3f87fe62b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -109,6 +109,17 @@ impl CrateRootModuleId {
}
}
+impl PartialEq<ModuleId> for CrateRootModuleId {
+ fn eq(&self, other: &ModuleId) -> bool {
+ other.block.is_none() && other.local_id == DefMap::ROOT && self.krate == other.krate
+ }
+}
+impl PartialEq<CrateRootModuleId> for ModuleId {
+ fn eq(&self, other: &CrateRootModuleId) -> bool {
+ other == self
+ }
+}
+
impl From<CrateRootModuleId> for ModuleId {
fn from(CrateRootModuleId { krate }: CrateRootModuleId) -> Self {
ModuleId { krate, block: None, local_id: DefMap::ROOT }
@@ -854,14 +865,36 @@ impl_from!(
ConstId,
FunctionId,
TraitId,
+ TraitAliasId,
TypeAliasId,
MacroId(Macro2Id, MacroRulesId, ProcMacroId),
ImplId,
GenericParamId,
- ExternCrateId
+ ExternCrateId,
+ UseId
for AttrDefId
);
+impl TryFrom<ModuleDefId> for AttrDefId {
+ type Error = ();
+
+ fn try_from(value: ModuleDefId) -> Result<Self, Self::Error> {
+ match value {
+ ModuleDefId::ModuleId(it) => Ok(it.into()),
+ ModuleDefId::FunctionId(it) => Ok(it.into()),
+ ModuleDefId::AdtId(it) => Ok(it.into()),
+ ModuleDefId::EnumVariantId(it) => Ok(it.into()),
+ ModuleDefId::ConstId(it) => Ok(it.into()),
+ ModuleDefId::StaticId(it) => Ok(it.into()),
+ ModuleDefId::TraitId(it) => Ok(it.into()),
+ ModuleDefId::TypeAliasId(it) => Ok(it.into()),
+ ModuleDefId::TraitAliasId(id) => Ok(id.into()),
+ ModuleDefId::MacroId(id) => Ok(id.into()),
+ ModuleDefId::BuiltinType(_) => Err(()),
+ }
+ }
+}
+
impl From<ItemContainerId> for AttrDefId {
fn from(acid: ItemContainerId) -> Self {
match acid {
@@ -872,6 +905,15 @@ impl From<ItemContainerId> for AttrDefId {
}
}
}
+impl From<AssocItemId> for AttrDefId {
+ fn from(assoc: AssocItemId) -> Self {
+ match assoc {
+ AssocItemId::FunctionId(it) => AttrDefId::FunctionId(it),
+ AssocItemId::ConstId(it) => AttrDefId::ConstId(it),
+ AssocItemId::TypeAliasId(it) => AttrDefId::TypeAliasId(it),
+ }
+ }
+}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum VariantId {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
index e523c2291..52781d988 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lower.rs
@@ -1,10 +1,11 @@
//! Context for lowering paths.
+use std::cell::OnceCell;
+
use hir_expand::{
ast_id_map::{AstIdMap, AstIdNode},
hygiene::Hygiene,
AstId, HirFileId, InFile,
};
-use once_cell::unsync::OnceCell;
use syntax::ast;
use triomphe::Arc;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 1250cbb74..4aedb22c6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -23,6 +23,45 @@ fn main() { 0 as u32; }
}
#[test]
+fn test_asm_expand() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+macro_rules! asm {() => {}}
+
+fn main() {
+ let i: u64 = 3;
+ let o: u64;
+ unsafe {
+ asm!(
+ "mov {0}, {1}",
+ "add {0}, 5",
+ out(reg) o,
+ in(reg) i,
+ );
+ }
+}
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+macro_rules! asm {() => {}}
+
+fn main() {
+ let i: u64 = 3;
+ let o: u64;
+ unsafe {
+ builtin #asm ( {
+ $crate::format_args!("mov {0}, {1}");
+ $crate::format_args!("add {0}, 5");
+ }
+ );
+ }
+}
+"##]],
+ );
+}
+
+#[test]
fn test_line_expand() {
check(
r#"
@@ -201,7 +240,7 @@ macro_rules! format_args {
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(arg1(a, b, c)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(arg2), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args ("{} {:?}", arg1(a, b, c), arg2);
}
"##]],
);
@@ -219,10 +258,10 @@ macro_rules! format_args {
fn main() {
format_args!(x = 2);
- format_args!(x =);
- format_args!(x =, x = 2);
- format_args!("{}", x =);
- format_args!(=, "{}", x =);
+ format_args!/*+errors*/(x =);
+ format_args!/*+errors*/(x =, x = 2);
+ format_args!/*+errors*/("{}", x =);
+ format_args!/*+errors*/(=, "{}", x =);
format_args!(x = 2, "{}", 5);
}
"#,
@@ -234,12 +273,19 @@ macro_rules! format_args {
}
fn main() {
- /* error: no rule matches input tokens */;
- /* error: expected expression */;
- /* error: expected expression, expected COMMA */;
- /* error: expected expression */::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(), ::core::fmt::Display::fmt), ]);
- /* error: expected expression, expected expression */;
- ::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(5), ::core::fmt::Display::fmt), ]);
+ builtin #format_args (x = 2);
+ /* parse error: expected expression */
+builtin #format_args (x = );
+ /* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected expression, item or let statement */
+builtin #format_args (x = , x = 2);
+ /* parse error: expected expression */
+builtin #format_args ("{}", x = );
+ /* parse error: expected expression */
+/* parse error: expected expression */
+builtin #format_args ( = , "{}", x = );
+ builtin #format_args (x = 2, "{}", 5);
}
"##]],
);
@@ -267,7 +313,7 @@ macro_rules! format_args {
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a::<A, B>()), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args ("{} {:?}", a::<A, B>(), b);
}
"##]],
);
@@ -300,7 +346,7 @@ macro_rules! format_args {
}
fn main() {
- ::core::fmt::Arguments::new_v1(&[r#""#, r#",mismatch,""#, r#"",""#, r#"""#, ], &[::core::fmt::ArgumentV1::new(&(location_csv_pat(db, &analysis, vfs, &sm, pat_id)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.expected.display(db)), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(mismatch.actual.display(db)), ::core::fmt::Display::fmt), ]);
+ builtin #format_args (r#"{},mismatch,"{}","{}""#, location_csv_pat(db, &analysis, vfs, &sm, pat_id), mismatch.expected.display(db), mismatch.actual.display(db));
}
"##]],
);
@@ -334,7 +380,7 @@ macro_rules! format_args {
}
fn main() {
- ::core::fmt::Arguments::new_v1(&["xxx", "y", "zzz", ], &[::core::fmt::ArgumentV1::new(&(2), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(b), ::core::fmt::Debug::fmt), ]);
+ builtin #format_args (concat!("xxx{}y", "{:?}zzz"), 2, b);
}
"##]],
);
@@ -364,8 +410,8 @@ macro_rules! format_args {
fn main() {
let _ =
- /* error: expected field name or number *//* parse error: expected field name or number */
-::core::fmt::Arguments::new_v1(&["", " ", ], &[::core::fmt::ArgumentV1::new(&(a.), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(), ::core::fmt::Debug::fmt), ]);
+ /* parse error: expected field name or number */
+builtin #format_args ("{} {:?}", a.);
}
"##]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 2170cadcf..d09062132 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -117,7 +117,7 @@ fn main(foo: ()) {
macro_rules! format_args {}
fn main(foo: ()) {
- /* error: unresolved macro identity */::core::fmt::Arguments::new_v1(&["", " ", " ", ], &[::core::fmt::ArgumentV1::new(&(::core::fmt::Arguments::new_v1(&["", ], &[::core::fmt::ArgumentV1::new(&(0), ::core::fmt::Display::fmt), ])), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(foo), ::core::fmt::Display::fmt), ::core::fmt::ArgumentV1::new(&(identity!(10)), ::core::fmt::Display::fmt), ])
+ builtin #format_args ("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
}
"##]],
);
@@ -150,8 +150,8 @@ macro_rules! identity {
}
fn main(foo: ()) {
- // format_args/*+tokenids*/!("{} {} {}"#1,#3 format_args!("{}", 0#10),#12 foo#13,#14 identity!(10#18),#21 "bar"#22)
-::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(&#4294967295[#4294967295""#4294967295,#4294967295 " "#4294967295,#4294967295 " "#4294967295,#4294967295 ]#4294967295,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(::core#4294967295::fmt#4294967295::Arguments#4294967295::new_v1#4294967295(&#4294967295[#4294967295""#4294967295,#4294967295 ]#4294967295,#4294967295 &#4294967295[::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#42949672950#10)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#4294967295foo#13)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::ArgumentV1#4294967295::new#4294967295(&#4294967295(#429496729510#18)#4294967295,#4294967295 ::core#4294967295::fmt#4294967295::Display#4294967295::fmt#4294967295)#4294967295,#4294967295 ]#4294967295)#4294967295
+ // format_args/*+tokenids*/!("{} {} {}"#1,#2 format_args#3!#4("{}"#6,#7 0#8),#9 foo#10,#11 identity#12!#13(10#15),#16 "bar"#17)
+builtin#4294967295 ##4294967295format_args#4294967295 (#0"{} {} {}"#1,#2 format_args#3!#4(#5"{}"#6,#7 0#8)#5,#9 foo#10,#11 identity#12!#13(#1410#15)#14,#16 "bar"#17)#0
}
"##]],
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
index d8e4a4dcc..b416f45ff 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs
@@ -909,3 +909,64 @@ macro_rules! with_std {
"##]],
)
}
+
+#[test]
+fn eager_regression_15403() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ format_args /* +errors */ !("{}", line.1.);
+}
+
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ /* parse error: expected field name or number */
+builtin #format_args ("{}", line.1.);
+}
+
+"##]],
+ );
+}
+
+#[test]
+fn eager_regression_154032() {
+ check(
+ r#"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ format_args /* +errors */ !("{}", &[0 2]);
+}
+
+"#,
+ expect![[r##"
+#[rustc_builtin_macro]
+#[macro_export]
+macro_rules! format_args {}
+
+fn main() {
+ /* parse error: expected COMMA */
+/* parse error: expected R_BRACK */
+/* parse error: expected COMMA */
+/* parse error: expected COMMA */
+/* parse error: expected expression */
+/* parse error: expected R_PAREN */
+/* parse error: expected expression, item or let statement */
+/* parse error: expected expression, item or let statement */
+builtin #format_args ("{}", &[0 2]);
+}
+
+"##]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 7a87e61c6..8adced4e0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -131,7 +131,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
.as_call_id_with_errors(&db, krate, |path| {
resolver
.resolve_path_as_macro(&db, &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(&db, it))
+ .map(|(it, _)| macro_id_to_def_id(&db, it))
})
.unwrap();
let macro_call_id = res.value.unwrap();
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 86818ce26..9a9fa0e02 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -60,7 +60,7 @@ mod tests;
use std::{cmp::Ord, ops::Deref};
use base_db::{CrateId, Edition, FileId, ProcMacroKind};
-use hir_expand::{name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
+use hir_expand::{ast_id_map::FileAstId, name::Name, HirFileId, InFile, MacroCallId, MacroDefId};
use itertools::Itertools;
use la_arena::Arena;
use profile::Count;
@@ -77,8 +77,8 @@ use crate::{
path::ModPath,
per_ns::PerNs,
visibility::Visibility,
- AstId, BlockId, BlockLoc, CrateRootModuleId, FunctionId, LocalModuleId, Lookup, MacroExpander,
- MacroId, ModuleId, ProcMacroId,
+ AstId, BlockId, BlockLoc, CrateRootModuleId, ExternCrateId, FunctionId, LocalModuleId, Lookup,
+ MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
};
/// Contains the results of (early) name resolution.
@@ -93,7 +93,7 @@ use crate::{
#[derive(Debug, PartialEq, Eq)]
pub struct DefMap {
_c: Count<Self>,
- /// When this is a block def map, this will hold the block id of the the block and module that
+ /// When this is a block def map, this will hold the block id of the block and module that
/// contains this block.
block: Option<BlockInfo>,
/// The modules and their data declared in this crate.
@@ -105,10 +105,11 @@ pub struct DefMap {
/// The prelude is empty for non-block DefMaps (unless `#[prelude_import]` was used,
/// but that attribute is nightly and when used in a block, it affects resolution globally
/// so we aren't handling this correctly anyways).
- prelude: Option<ModuleId>,
+ prelude: Option<(ModuleId, Option<UseId>)>,
/// `macro_use` prelude that contains macros from `#[macro_use]`'d external crates. Note that
/// this contains all kinds of macro, not just `macro_rules!` macro.
- macro_use_prelude: FxHashMap<Name, MacroId>,
+ /// ExternCrateId being None implies it being imported from the general prelude import.
+ macro_use_prelude: FxHashMap<Name, (MacroId, Option<ExternCrateId>)>,
/// Tracks which custom derives are in scope for an item, to allow resolution of derive helper
/// attributes.
@@ -125,7 +126,7 @@ pub struct DefMap {
#[derive(Clone, Debug, PartialEq, Eq)]
struct DefMapCrateData {
/// The extern prelude which contains all root modules of external crates that are in scope.
- extern_prelude: FxHashMap<Name, CrateRootModuleId>,
+ extern_prelude: FxHashMap<Name, (CrateRootModuleId, Option<ExternCrateId>)>,
/// Side table for resolving derive helpers.
exported_derives: FxHashMap<MacroDefId, Box<[Name]>>,
@@ -217,16 +218,17 @@ pub enum ModuleOrigin {
/// Note that non-inline modules, by definition, live inside non-macro file.
File {
is_mod_rs: bool,
- declaration: AstId<ast::Module>,
+ declaration: FileAstId<ast::Module>,
declaration_tree_id: ItemTreeId<Mod>,
definition: FileId,
},
Inline {
definition_tree_id: ItemTreeId<Mod>,
- definition: AstId<ast::Module>,
+ definition: FileAstId<ast::Module>,
},
/// Pseudo-module introduced by a block scope (contains only inner items).
BlockExpr {
+ id: BlockId,
block: AstId<ast::BlockExpr>,
},
}
@@ -234,8 +236,12 @@ pub enum ModuleOrigin {
impl ModuleOrigin {
pub fn declaration(&self) -> Option<AstId<ast::Module>> {
match self {
- ModuleOrigin::File { declaration: module, .. }
- | ModuleOrigin::Inline { definition: module, .. } => Some(*module),
+ &ModuleOrigin::File { declaration, declaration_tree_id, .. } => {
+ Some(AstId::new(declaration_tree_id.file_id(), declaration))
+ }
+ &ModuleOrigin::Inline { definition, definition_tree_id } => {
+ Some(AstId::new(definition_tree_id.file_id(), definition))
+ }
ModuleOrigin::CrateRoot { .. } | ModuleOrigin::BlockExpr { .. } => None,
}
}
@@ -260,16 +266,17 @@ impl ModuleOrigin {
/// That is, a file or a `mod foo {}` with items.
fn definition_source(&self, db: &dyn DefDatabase) -> InFile<ModuleSource> {
match self {
- ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
- let file_id = *definition;
- let sf = db.parse(file_id).tree();
- InFile::new(file_id.into(), ModuleSource::SourceFile(sf))
+ &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
+ let sf = db.parse(definition).tree();
+ InFile::new(definition.into(), ModuleSource::SourceFile(sf))
}
- ModuleOrigin::Inline { definition, .. } => InFile::new(
- definition.file_id,
- ModuleSource::Module(definition.to_node(db.upcast())),
+ &ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
+ definition_tree_id.file_id(),
+ ModuleSource::Module(
+ AstId::new(definition_tree_id.file_id(), definition).to_node(db.upcast()),
+ ),
),
- ModuleOrigin::BlockExpr { block } => {
+ ModuleOrigin::BlockExpr { block, .. } => {
InFile::new(block.file_id, ModuleSource::BlockExpr(block.to_node(db.upcast())))
}
}
@@ -314,9 +321,7 @@ impl DefMap {
}
pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> {
- let block: BlockLoc = db.lookup_intern_block(block_id);
-
- let tree_id = TreeId::new(block.ast_id.file_id, Some(block_id));
+ let block: BlockLoc = block_id.lookup(db);
let parent_map = block.module.def_map(db);
let krate = block.module.krate;
@@ -325,8 +330,10 @@ impl DefMap {
// modules declared by blocks with items. At the moment, we don't use
// this visibility for anything outside IDE, so that's probably OK.
let visibility = Visibility::Module(ModuleId { krate, local_id, block: None });
- let module_data =
- ModuleData::new(ModuleOrigin::BlockExpr { block: block.ast_id }, visibility);
+ let module_data = ModuleData::new(
+ ModuleOrigin::BlockExpr { block: block.ast_id, id: block_id },
+ visibility,
+ );
let mut def_map = DefMap::empty(krate, parent_map.data.edition, module_data);
def_map.data = parent_map.data.clone();
@@ -338,7 +345,8 @@ impl DefMap {
},
});
- let def_map = collector::collect_defs(db, def_map, tree_id);
+ let def_map =
+ collector::collect_defs(db, def_map, TreeId::new(block.ast_id.file_id, Some(block_id)));
Arc::new(def_map)
}
@@ -427,15 +435,19 @@ impl DefMap {
self.block.map(|block| block.block)
}
- pub(crate) fn prelude(&self) -> Option<ModuleId> {
+ pub(crate) fn prelude(&self) -> Option<(ModuleId, Option<UseId>)> {
self.prelude
}
- pub(crate) fn extern_prelude(&self) -> impl Iterator<Item = (&Name, ModuleId)> + '_ {
- self.data.extern_prelude.iter().map(|(name, &def)| (name, def.into()))
+ pub(crate) fn extern_prelude(
+ &self,
+ ) -> impl Iterator<Item = (&Name, (CrateRootModuleId, Option<ExternCrateId>))> + '_ {
+ self.data.extern_prelude.iter().map(|(name, &def)| (name, def))
}
- pub(crate) fn macro_use_prelude(&self) -> impl Iterator<Item = (&Name, MacroId)> + '_ {
+ pub(crate) fn macro_use_prelude(
+ &self,
+ ) -> impl Iterator<Item = (&Name, (MacroId, Option<ExternCrateId>))> + '_ {
self.macro_use_prelude.iter().map(|(name, &def)| (name, def))
}
@@ -638,8 +650,8 @@ impl ModuleData {
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition } => {
definition.into()
}
- ModuleOrigin::Inline { definition, .. } => definition.file_id,
- ModuleOrigin::BlockExpr { block } => block.file_id,
+ ModuleOrigin::Inline { definition_tree_id, .. } => definition_tree_id.file_id(),
+ ModuleOrigin::BlockExpr { block, .. } => block.file_id,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index eef54fc49..2d4586146 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -33,7 +33,7 @@ use crate::{
attr_macro_as_call_id,
db::DefDatabase,
derive_macro_as_call_id,
- item_scope::{ImportType, PerNsGlobImports},
+ item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
item_tree::{
self, ExternCrate, Fields, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode,
MacroCall, MacroDef, MacroRules, Mod, ModItem, ModKind, TreeId,
@@ -52,10 +52,10 @@ use crate::{
tt,
visibility::{RawVisibility, Visibility},
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, EnumVariantId,
- ExternBlockLoc, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
- LocalModuleId, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, MacroRulesLoc,
- ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, StructLoc, TraitAliasLoc,
- TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseLoc,
+ ExternBlockLoc, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern,
+ ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+ MacroRulesId, MacroRulesLoc, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
+ StructLoc, TraitAliasLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
};
static GLOB_RECURSION_LIMIT: Limit = Limit::new(100);
@@ -146,8 +146,8 @@ impl PartialResolvedImport {
#[derive(Clone, Debug, Eq, PartialEq)]
enum ImportSource {
- Use { id: ItemTreeId<item_tree::Use>, use_tree: Idx<ast::UseTree> },
- ExternCrate(ItemTreeId<item_tree::ExternCrate>),
+ Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
+ ExternCrate { id: ExternCrateId },
}
#[derive(Debug, Eq, PartialEq)]
@@ -155,54 +155,41 @@ struct Import {
path: ModPath,
alias: Option<ImportAlias>,
visibility: RawVisibility,
- kind: ImportKind,
source: ImportSource,
- is_prelude: bool,
- is_macro_use: bool,
}
impl Import {
fn from_use(
- db: &dyn DefDatabase,
- krate: CrateId,
tree: &ItemTree,
- id: ItemTreeId<item_tree::Use>,
+ item_tree_id: ItemTreeId<item_tree::Use>,
+ id: UseId,
+ is_prelude: bool,
mut cb: impl FnMut(Self),
) {
- let it = &tree[id.value];
- let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+ let it = &tree[item_tree_id.value];
let visibility = &tree[it.visibility];
- let is_prelude = attrs.by_key("prelude_import").exists();
it.use_tree.expand(|idx, path, kind, alias| {
cb(Self {
path,
alias,
visibility: visibility.clone(),
- kind,
- is_prelude,
- is_macro_use: false,
- source: ImportSource::Use { id, use_tree: idx },
+ source: ImportSource::Use { use_tree: idx, id, is_prelude, kind },
});
});
}
fn from_extern_crate(
- db: &dyn DefDatabase,
- krate: CrateId,
tree: &ItemTree,
- id: ItemTreeId<item_tree::ExternCrate>,
+ item_tree_id: ItemTreeId<item_tree::ExternCrate>,
+ id: ExternCrateId,
) -> Self {
- let it = &tree[id.value];
- let attrs = &tree.attrs(db, krate, ModItem::from(id.value).into());
+ let it = &tree[item_tree_id.value];
let visibility = &tree[it.visibility];
Self {
path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
alias: it.alias.clone(),
visibility: visibility.clone(),
- kind: ImportKind::Plain,
- is_prelude: false,
- is_macro_use: attrs.by_key("macro_use").exists(),
- source: ImportSource::ExternCrate(id),
+ source: ImportSource::ExternCrate { id },
}
}
}
@@ -235,7 +222,7 @@ struct DefCollector<'a> {
db: &'a dyn DefDatabase,
def_map: DefMap,
deps: FxHashMap<Name, Dependency>,
- glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility)>>,
+ glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<ImportDirective>,
unresolved_macros: Vec<MacroDirective>,
@@ -280,7 +267,7 @@ impl DefCollector<'_> {
if dep.is_prelude() {
crate_data
.extern_prelude
- .insert(name.clone(), CrateRootModuleId { krate: dep.crate_id });
+ .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
}
}
@@ -556,8 +543,12 @@ impl DefCollector<'_> {
self.def_map.resolve_path(self.db, DefMap::ROOT, &path, BuiltinShadowMode::Other, None);
match per_ns.types {
- Some((ModuleDefId::ModuleId(m), _)) => {
- self.def_map.prelude = Some(m);
+ Some((ModuleDefId::ModuleId(m), _, import)) => {
+ // FIXME: This should specifically look for a glob import somehow and record that here
+ self.def_map.prelude = Some((
+ m,
+ import.and_then(ImportOrExternCrate::into_import).map(|it| it.import),
+ ));
}
types => {
tracing::debug!(
@@ -657,9 +648,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
Visibility::Public,
- ImportType::Named,
+ None,
);
}
}
@@ -693,9 +684,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
vis,
- ImportType::Named,
+ None,
);
}
@@ -708,9 +699,9 @@ impl DefCollector<'_> {
self.def_map.modules[module_id].scope.declare(macro_.into());
self.update(
module_id,
- &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public))],
+ &[(Some(name), PerNs::macros(macro_.into(), Visibility::Public, None))],
Visibility::Public,
- ImportType::Named,
+ None,
);
}
@@ -720,21 +711,29 @@ impl DefCollector<'_> {
/// Exported macros are just all macros in the root module scope.
/// Note that it contains not only all `#[macro_export]` macros, but also all aliases
/// created by `use` in the root module, ignoring the visibility of `use`.
- fn import_macros_from_extern_crate(&mut self, krate: CrateId, names: Option<Vec<Name>>) {
+ fn import_macros_from_extern_crate(
+ &mut self,
+ krate: CrateId,
+ names: Option<Vec<Name>>,
+ extern_crate: Option<ExternCrateId>,
+ ) {
let def_map = self.db.crate_def_map(krate);
// `#[macro_use]` brings macros into macro_use prelude. Yes, even non-`macro_rules!`
// macros.
let root_scope = &def_map[DefMap::ROOT].scope;
- if let Some(names) = names {
- for name in names {
- // FIXME: Report diagnostic on 404.
- if let Some(def) = root_scope.get(&name).take_macros() {
- self.def_map.macro_use_prelude.insert(name, def);
+ match names {
+ Some(names) => {
+ for name in names {
+ // FIXME: Report diagnostic on 404.
+ if let Some(def) = root_scope.get(&name).take_macros() {
+ self.def_map.macro_use_prelude.insert(name, (def, extern_crate));
+ }
}
}
- } else {
- for (name, def) in root_scope.macros() {
- self.def_map.macro_use_prelude.insert(name.clone(), def);
+ None => {
+ for (name, def) in root_scope.macros() {
+ self.def_map.macro_use_prelude.insert(name.clone(), (def, extern_crate));
+ }
}
}
}
@@ -771,48 +770,53 @@ impl DefCollector<'_> {
let _p = profile::span("resolve_import")
.detail(|| format!("{}", import.path.display(self.db.upcast())));
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
- if matches!(import.source, ImportSource::ExternCrate { .. }) {
- let name = import
- .path
- .as_ident()
- .expect("extern crate should have been desugared to one-element path");
-
- let res = self.resolve_extern_crate(name);
-
- match res {
- Some(res) => {
- PartialResolvedImport::Resolved(PerNs::types(res.into(), Visibility::Public))
+ match import.source {
+ ImportSource::ExternCrate { .. } => {
+ let name = import
+ .path
+ .as_ident()
+ .expect("extern crate should have been desugared to one-element path");
+
+ let res = self.resolve_extern_crate(name);
+
+ match res {
+ Some(res) => PartialResolvedImport::Resolved(PerNs::types(
+ res.into(),
+ Visibility::Public,
+ None,
+ )),
+ None => PartialResolvedImport::Unresolved,
}
- None => PartialResolvedImport::Unresolved,
}
- } else {
- let res = self.def_map.resolve_path_fp_with_macro(
- self.db,
- ResolveMode::Import,
- module_id,
- &import.path,
- BuiltinShadowMode::Module,
- None, // An import may resolve to any kind of macro.
- );
+ ImportSource::Use { .. } => {
+ let res = self.def_map.resolve_path_fp_with_macro(
+ self.db,
+ ResolveMode::Import,
+ module_id,
+ &import.path,
+ BuiltinShadowMode::Module,
+ None, // An import may resolve to any kind of macro.
+ );
- let def = res.resolved_def;
- if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
- return PartialResolvedImport::Unresolved;
- }
+ let def = res.resolved_def;
+ if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() {
+ return PartialResolvedImport::Unresolved;
+ }
- if let Some(krate) = res.krate {
- if krate != self.def_map.krate {
- return PartialResolvedImport::Resolved(
- def.filter_visibility(|v| matches!(v, Visibility::Public)),
- );
+ if let Some(krate) = res.krate {
+ if krate != self.def_map.krate {
+ return PartialResolvedImport::Resolved(
+ def.filter_visibility(|v| matches!(v, Visibility::Public)),
+ );
+ }
}
- }
- // Check whether all namespaces are resolved.
- if def.is_full() {
- PartialResolvedImport::Resolved(def)
- } else {
- PartialResolvedImport::Indeterminate(def)
+ // Check whether all namespaces are resolved.
+ if def.is_full() {
+ PartialResolvedImport::Resolved(def)
+ } else {
+ PartialResolvedImport::Indeterminate(def)
+ }
}
}
}
@@ -837,8 +841,9 @@ impl DefCollector<'_> {
.resolve_visibility(self.db, module_id, &directive.import.visibility, false)
.unwrap_or(Visibility::Public);
- match import.kind {
- ImportKind::Plain | ImportKind::TypeOnly => {
+ match import.source {
+ ImportSource::ExternCrate { .. }
+ | ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
let name = match &import.alias {
Some(ImportAlias::Alias(name)) => Some(name),
Some(ImportAlias::Underscore) => None,
@@ -851,40 +856,44 @@ impl DefCollector<'_> {
},
};
- if import.kind == ImportKind::TypeOnly {
- def.values = None;
- def.macros = None;
- }
-
- tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
-
- // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
- if matches!(import.source, ImportSource::ExternCrate { .. })
- && self.def_map.block.is_none()
- && module_id == DefMap::ROOT
- {
- if let (Some(ModuleDefId::ModuleId(def)), Some(name)) = (def.take_types(), name)
- {
- if let Ok(def) = def.try_into() {
- Arc::get_mut(&mut self.def_map.data)
- .unwrap()
- .extern_prelude
- .insert(name.clone(), def);
+ let imp = match import.source {
+ // extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
+ ImportSource::ExternCrate { id, .. } => {
+ if self.def_map.block.is_none() && module_id == DefMap::ROOT {
+ if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
+ (def.take_types(), name)
+ {
+ if let Ok(def) = def.try_into() {
+ Arc::get_mut(&mut self.def_map.data)
+ .unwrap()
+ .extern_prelude
+ .insert(name.clone(), (def, Some(id)));
+ }
+ }
}
+ ImportType::ExternCrate(id)
}
- }
+ ImportSource::Use { kind, id, use_tree, .. } => {
+ if kind == ImportKind::TypeOnly {
+ def.values = None;
+ def.macros = None;
+ }
+ ImportType::Import(ImportId { import: id, idx: use_tree })
+ }
+ };
+ tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
- self.update(module_id, &[(name.cloned(), def)], vis, ImportType::Named);
+ self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
}
- ImportKind::Glob => {
+ ImportSource::Use { kind: ImportKind::Glob, id, .. } => {
tracing::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDefId::ModuleId(m)) => {
- if import.is_prelude {
+ if let ImportSource::Use { id, is_prelude: true, .. } = import.source {
// Note: This dodgily overrides the injected prelude. The rustc
// implementation seems to work the same though.
cov_mark::hit!(std_prelude);
- self.def_map.prelude = Some(m);
+ self.def_map.prelude = Some((m, Some(id)));
} else if m.krate != self.def_map.krate {
cov_mark::hit!(glob_across_crates);
// glob import from other crate => we can just import everything once
@@ -901,7 +910,7 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
- self.update(module_id, &items, vis, ImportType::Glob);
+ self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
} else {
// glob import from same crate => we do an initial
// import, and then need to propagate any further
@@ -933,11 +942,11 @@ impl DefCollector<'_> {
.filter(|(_, res)| !res.is_none())
.collect::<Vec<_>>();
- self.update(module_id, &items, vis, ImportType::Glob);
+ self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
// record the glob import in case we add further items
let glob = self.glob_imports.entry(m.local_id).or_default();
- if !glob.iter().any(|(mid, _)| *mid == module_id) {
- glob.push((module_id, vis));
+ if !glob.iter().any(|(mid, _, _)| *mid == module_id) {
+ glob.push((module_id, vis, id));
}
}
}
@@ -959,11 +968,11 @@ impl DefCollector<'_> {
.map(|(local_id, variant_data)| {
let name = variant_data.name.clone();
let variant = EnumVariantId { parent: e, local_id };
- let res = PerNs::both(variant.into(), variant.into(), vis);
+ let res = PerNs::both(variant.into(), variant.into(), vis, None);
(Some(name), res)
})
.collect::<Vec<_>>();
- self.update(module_id, &resolutions, vis, ImportType::Glob);
+ self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id)));
}
Some(d) => {
tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
@@ -983,10 +992,10 @@ impl DefCollector<'_> {
resolutions: &[(Option<Name>, PerNs)],
// Visibility this import will have
vis: Visibility,
- import_type: ImportType,
+ import: Option<ImportType>,
) {
self.db.unwind_if_cancelled();
- self.update_recursive(module_id, resolutions, vis, import_type, 0)
+ self.update_recursive(module_id, resolutions, vis, import, 0)
}
fn update_recursive(
@@ -997,7 +1006,7 @@ impl DefCollector<'_> {
// All resolutions are imported with this visibility; the visibilities in
// the `PerNs` values are ignored and overwritten
vis: Visibility,
- import_type: ImportType,
+ import: Option<ImportType>,
depth: usize,
) {
if GLOB_RECURSION_LIMIT.check(depth).is_err() {
@@ -1014,7 +1023,7 @@ impl DefCollector<'_> {
&mut self.from_glob_import,
(module_id, name.clone()),
res.with_visibility(vis),
- import_type,
+ import,
);
}
None => {
@@ -1059,7 +1068,7 @@ impl DefCollector<'_> {
.get(&module_id)
.into_iter()
.flatten()
- .filter(|(glob_importing_module, _)| {
+ .filter(|(glob_importing_module, _, _)| {
// we know all resolutions have the same visibility (`vis`), so we
// just need to check that once
vis.is_visible_from_def_map(self.db, &self.def_map, *glob_importing_module)
@@ -1067,12 +1076,12 @@ impl DefCollector<'_> {
.cloned()
.collect::<Vec<_>>();
- for (glob_importing_module, glob_import_vis) in glob_imports {
+ for (glob_importing_module, glob_import_vis, use_) in glob_imports {
self.update_recursive(
glob_importing_module,
resolutions,
glob_import_vis,
- ImportType::Glob,
+ Some(ImportType::Glob(use_)),
depth + 1,
);
}
@@ -1460,31 +1469,34 @@ impl DefCollector<'_> {
// heuristic, but it works in practice.
let mut diagnosed_extern_crates = FxHashSet::default();
for directive in &self.unresolved_imports {
- if let ImportSource::ExternCrate(krate) = directive.import.source {
- let item_tree = krate.item_tree(self.db);
- let extern_crate = &item_tree[krate.value];
+ if let ImportSource::ExternCrate { id } = directive.import.source {
+ let item_tree_id = id.lookup(self.db).id;
+ let item_tree = item_tree_id.item_tree(self.db);
+ let extern_crate = &item_tree[item_tree_id.value];
diagnosed_extern_crates.insert(extern_crate.name.clone());
self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
directive.module_id,
- InFile::new(krate.file_id(), extern_crate.ast_id),
+ InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
));
}
}
for directive in &self.unresolved_imports {
- if let ImportSource::Use { id: import, use_tree } = directive.import.source {
+ if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
+ directive.import.source
+ {
if matches!(
(directive.import.path.segments().first(), &directive.import.path.kind),
(Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
) {
continue;
}
-
+ let item_tree_id = id.lookup(self.db).id;
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
directive.module_id,
- import,
+ item_tree_id,
use_tree,
));
}
@@ -1519,72 +1531,66 @@ impl ModCollector<'_, '_> {
self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone());
// Prelude module is always considered to be `#[macro_use]`.
- if let Some(prelude_module) = self.def_collector.def_map.prelude {
+ if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
if prelude_module.krate != krate && is_crate_root {
cov_mark::hit!(prelude_is_macro_use);
- self.def_collector.import_macros_from_extern_crate(prelude_module.krate, None);
- }
- }
-
- // This should be processed eagerly instead of deferred to resolving.
- // `#[macro_use] extern crate` is hoisted to imports macros before collecting
- // any other items.
- //
- // If we're not at the crate root, `macro_use`d extern crates are an error so let's just
- // ignore them.
- if is_crate_root {
- for &item in items {
- if let ModItem::ExternCrate(id) = item {
- self.process_macro_use_extern_crate(id);
- }
+ self.def_collector.import_macros_from_extern_crate(
+ prelude_module.krate,
+ None,
+ None,
+ );
}
}
+ let db = self.def_collector.db;
+ let module_id = self.module_id;
+ let update_def =
+ |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
+ def_collector.def_map.modules[module_id].scope.declare(id);
+ def_collector.update(
+ module_id,
+ &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor, None))],
+ vis,
+ None,
+ )
+ };
+ let resolve_vis = |def_map: &DefMap, visibility| {
+ def_map
+ .resolve_visibility(db, module_id, visibility, false)
+ .unwrap_or(Visibility::Public)
+ };
- for &item in items {
- let attrs = self.item_tree.attrs(self.def_collector.db, krate, item.into());
+ let mut process_mod_item = |item: ModItem| {
+ let attrs = self.item_tree.attrs(db, krate, item.into());
if let Some(cfg) = attrs.cfg() {
if !self.is_cfg_enabled(&cfg) {
self.emit_unconfigured_diagnostic(item, &cfg);
- continue;
+ return;
}
}
if let Err(()) = self.resolve_attributes(&attrs, item, container) {
// Do not process the item. It has at least one non-builtin attribute, so the
// fixed-point algorithm is required to resolve the rest of them.
- continue;
+ return;
}
- let db = self.def_collector.db;
- let module = self.def_collector.def_map.module_id(self.module_id);
+ let module = self.def_collector.def_map.module_id(module_id);
let def_map = &mut self.def_collector.def_map;
- let update_def =
- |def_collector: &mut DefCollector<'_>, id, name: &Name, vis, has_constructor| {
- def_collector.def_map.modules[self.module_id].scope.declare(id);
- def_collector.update(
- self.module_id,
- &[(Some(name.clone()), PerNs::from_def(id, vis, has_constructor))],
- vis,
- ImportType::Named,
- )
- };
- let resolve_vis = |def_map: &DefMap, visibility| {
- def_map
- .resolve_visibility(db, self.module_id, visibility, false)
- .unwrap_or(Visibility::Public)
- };
match item {
ModItem::Mod(m) => self.collect_module(m, &attrs),
- ModItem::Use(import_id) => {
- let _import_id =
- UseLoc { container: module, id: ItemTreeId::new(self.tree_id, import_id) }
- .intern(db);
+ ModItem::Use(item_tree_id) => {
+ let id = UseLoc {
+ container: module,
+ id: ItemTreeId::new(self.tree_id, item_tree_id),
+ }
+ .intern(db);
+ let is_prelude = attrs.by_key("prelude_import").exists();
Import::from_use(
- db,
- krate,
self.item_tree,
- ItemTreeId::new(self.tree_id, import_id),
+ ItemTreeId::new(self.tree_id, item_tree_id),
+ id,
+ is_prelude,
|import| {
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
@@ -1594,22 +1600,29 @@ impl ModCollector<'_, '_> {
},
)
}
- ModItem::ExternCrate(import_id) => {
- let extern_crate_id = ExternCrateLoc {
+ ModItem::ExternCrate(item_tree_id) => {
+ let id = ExternCrateLoc {
container: module,
- id: ItemTreeId::new(self.tree_id, import_id),
+ id: ItemTreeId::new(self.tree_id, item_tree_id),
}
.intern(db);
+ if is_crate_root {
+ self.process_macro_use_extern_crate(
+ item_tree_id,
+ id,
+ attrs.by_key("macro_use").attrs(),
+ );
+ }
+
self.def_collector.def_map.modules[self.module_id]
.scope
- .define_extern_crate_decl(extern_crate_id);
+ .define_extern_crate_decl(id);
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
import: Import::from_extern_crate(
- db,
- krate,
self.item_tree,
- ItemTreeId::new(self.tree_id, import_id),
+ ItemTreeId::new(self.tree_id, item_tree_id),
+ id,
),
status: PartialResolvedImport::Unresolved,
})
@@ -1768,21 +1781,34 @@ impl ModCollector<'_, '_> {
);
}
}
+ };
+
+ // extern crates should be processed eagerly instead of deferred to resolving.
+ // `#[macro_use] extern crate` is hoisted to imports macros before collecting
+ // any other items.
+ if is_crate_root {
+ items
+ .iter()
+ .filter(|it| matches!(it, ModItem::ExternCrate(..)))
+ .copied()
+ .for_each(&mut process_mod_item);
+ items
+ .iter()
+ .filter(|it| !matches!(it, ModItem::ExternCrate(..)))
+ .copied()
+ .for_each(process_mod_item);
+ } else {
+ items.iter().copied().for_each(process_mod_item);
}
}
- fn process_macro_use_extern_crate(&mut self, extern_crate: FileItemTreeId<ExternCrate>) {
+ fn process_macro_use_extern_crate<'a>(
+ &mut self,
+ extern_crate: FileItemTreeId<ExternCrate>,
+ extern_crate_id: ExternCrateId,
+ macro_use_attrs: impl Iterator<Item = &'a Attr>,
+ ) {
let db = self.def_collector.db;
- let attrs = self.item_tree.attrs(
- db,
- self.def_collector.def_map.krate,
- ModItem::from(extern_crate).into(),
- );
- if let Some(cfg) = attrs.cfg() {
- if !self.is_cfg_enabled(&cfg) {
- return;
- }
- }
let target_crate =
match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
@@ -1798,11 +1824,15 @@ impl ModCollector<'_, '_> {
let mut single_imports = Vec::new();
let hygiene = Hygiene::new_unhygienic();
- for attr in attrs.by_key("macro_use").attrs() {
+ for attr in macro_use_attrs {
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast(), &hygiene) else {
// `#[macro_use]` (without any paths) found, forget collected names and just import
// all visible macros.
- self.def_collector.import_macros_from_extern_crate(target_crate, None);
+ self.def_collector.import_macros_from_extern_crate(
+ target_crate,
+ None,
+ Some(extern_crate_id),
+ );
return;
};
for path in paths {
@@ -1812,7 +1842,11 @@ impl ModCollector<'_, '_> {
}
}
- self.def_collector.import_macros_from_extern_crate(target_crate, Some(single_imports));
+ self.def_collector.import_macros_from_extern_crate(
+ target_crate,
+ Some(single_imports),
+ Some(extern_crate_id),
+ );
}
fn collect_module(&mut self, module_id: FileItemTreeId<Mod>, attrs: &Attrs) {
@@ -1824,7 +1858,7 @@ impl ModCollector<'_, '_> {
ModKind::Inline { items } => {
let module_id = self.push_child_module(
module.name.clone(),
- AstId::new(self.file_id(), module.ast_id),
+ module.ast_id,
None,
&self.item_tree[module.visibility],
module_id,
@@ -1862,7 +1896,7 @@ impl ModCollector<'_, '_> {
if is_enabled {
let module_id = self.push_child_module(
module.name.clone(),
- ast_id,
+ ast_id.value,
Some((file_id, is_mod_rs)),
&self.item_tree[module.visibility],
module_id,
@@ -1889,7 +1923,7 @@ impl ModCollector<'_, '_> {
Err(candidates) => {
self.push_child_module(
module.name.clone(),
- ast_id,
+ ast_id.value,
None,
&self.item_tree[module.visibility],
module_id,
@@ -1906,7 +1940,7 @@ impl ModCollector<'_, '_> {
fn push_child_module(
&mut self,
name: Name,
- declaration: AstId<ast::Module>,
+ declaration: FileAstId<ast::Module>,
definition: Option<(FileId, bool)>,
visibility: &crate::visibility::RawVisibility,
mod_tree_id: FileItemTreeId<Mod>,
@@ -1948,9 +1982,9 @@ impl ModCollector<'_, '_> {
def_map.modules[self.module_id].scope.declare(def);
self.def_collector.update(
self.module_id,
- &[(Some(name), PerNs::from_def(def, vis, false))],
+ &[(Some(name), PerNs::from_def(def, vis, false, None))],
vis,
- ImportType::Named,
+ None,
);
res
}
@@ -2198,7 +2232,7 @@ impl ModCollector<'_, '_> {
map[module].scope.get_legacy_macro(name)?.last().copied()
})
.or_else(|| def_map[self.module_id].scope.get(name).take_macros())
- .or_else(|| def_map.macro_use_prelude.get(name).copied())
+ .or_else(|| Some(def_map.macro_use_prelude.get(name).copied()?.0))
.filter(|&id| {
sub_namespace_match(
Some(MacroSubNs::from_id(db, id)),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
index de22ea101..460a908b6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/path_resolution.rs
@@ -15,8 +15,9 @@ use hir_expand::name::Name;
use triomphe::Arc;
use crate::{
+ data::adt::VariantData,
db::DefDatabase,
- item_scope::BUILTIN_SCOPE,
+ item_scope::{ImportOrExternCrate, BUILTIN_SCOPE},
nameres::{sub_namespace_match, BlockInfo, BuiltinShadowMode, DefMap, MacroSubNs},
path::{ModPath, PathKind},
per_ns::PerNs,
@@ -65,7 +66,7 @@ impl PerNs {
db: &dyn DefDatabase,
expected: Option<MacroSubNs>,
) -> Self {
- self.macros = self.macros.filter(|&(id, _)| {
+ self.macros = self.macros.filter(|&(id, _, _)| {
let this = MacroSubNs::from_id(db, id);
sub_namespace_match(Some(this), expected)
});
@@ -196,15 +197,15 @@ impl DefMap {
PathKind::DollarCrate(krate) => {
if krate == self.krate {
cov_mark::hit!(macro_dollar_crate_self);
- PerNs::types(self.crate_root().into(), Visibility::Public)
+ PerNs::types(self.crate_root().into(), Visibility::Public, None)
} else {
let def_map = db.crate_def_map(krate);
let module = def_map.module_id(Self::ROOT);
cov_mark::hit!(macro_dollar_crate_other);
- PerNs::types(module.into(), Visibility::Public)
+ PerNs::types(module.into(), Visibility::Public, None)
}
}
- PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public),
+ PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public, None),
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
@@ -291,7 +292,7 @@ impl DefMap {
);
}
- PerNs::types(module.into(), Visibility::Public)
+ PerNs::types(module.into(), Visibility::Public, None)
}
PathKind::Abs => {
// 2018-style absolute path -- only extern prelude
@@ -299,9 +300,13 @@ impl DefMap {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
- if let Some(&def) = self.data.extern_prelude.get(segment) {
+ if let Some(&(def, extern_crate)) = self.data.extern_prelude.get(segment) {
tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def);
- PerNs::types(def.into(), Visibility::Public)
+ PerNs::types(
+ def.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
} else {
return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
}
@@ -309,7 +314,7 @@ impl DefMap {
};
for (i, segment) in segments {
- let (curr, vis) = match curr_per_ns.take_types_vis() {
+ let (curr, vis, imp) = match curr_per_ns.take_types_full() {
Some(r) => r,
None => {
// we still have path segments left, but the path so far
@@ -364,18 +369,20 @@ impl DefMap {
Some(local_id) => {
let variant = EnumVariantId { parent: e, local_id };
match &*enum_data.variants[local_id].variant_data {
- crate::data::adt::VariantData::Record(_) => {
- PerNs::types(variant.into(), Visibility::Public)
- }
- crate::data::adt::VariantData::Tuple(_)
- | crate::data::adt::VariantData::Unit => {
- PerNs::both(variant.into(), variant.into(), Visibility::Public)
+ VariantData::Record(_) => {
+ PerNs::types(variant.into(), Visibility::Public, None)
}
+ VariantData::Tuple(_) | VariantData::Unit => PerNs::both(
+ variant.into(),
+ variant.into(),
+ Visibility::Public,
+ None,
+ ),
}
}
None => {
return ResolvePathResult::with(
- PerNs::types(e.into(), vis),
+ PerNs::types(e.into(), vis, imp),
ReachedFixedPoint::Yes,
Some(i),
Some(self.krate),
@@ -393,7 +400,7 @@ impl DefMap {
);
return ResolvePathResult::with(
- PerNs::types(s, vis),
+ PerNs::types(s, vis, imp),
ReachedFixedPoint::Yes,
Some(i),
Some(self.krate),
@@ -430,7 +437,7 @@ impl DefMap {
.filter(|&id| {
sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns)
})
- .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public));
+ .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public, None));
let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns);
let from_builtin = match self.block {
Some(_) => {
@@ -449,18 +456,27 @@ impl DefMap {
let extern_prelude = || {
if self.block.is_some() {
- // Don't resolve extern prelude in block `DefMap`s.
+ // Don't resolve extern prelude in block `DefMap`s, defer it to the crate def map so
+ // that blocks can properly shadow them
return PerNs::none();
}
- self.data
- .extern_prelude
- .get(name)
- .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public))
+ self.data.extern_prelude.get(name).map_or(PerNs::none(), |&(it, extern_crate)| {
+ PerNs::types(
+ it.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
};
let macro_use_prelude = || {
- self.macro_use_prelude
- .get(name)
- .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public))
+ self.macro_use_prelude.get(name).map_or(PerNs::none(), |&(it, _extern_crate)| {
+ PerNs::macros(
+ it.into(),
+ Visibility::Public,
+ // FIXME?
+ None, // extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ })
};
let prelude = || self.resolve_in_prelude(db, name);
@@ -488,18 +504,23 @@ impl DefMap {
// Don't resolve extern prelude in block `DefMap`s.
return PerNs::none();
}
- self.data
- .extern_prelude
- .get(name)
- .copied()
- .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public))
+ self.data.extern_prelude.get(name).copied().map_or(
+ PerNs::none(),
+ |(it, extern_crate)| {
+ PerNs::types(
+ it.into(),
+ Visibility::Public,
+ extern_crate.map(ImportOrExternCrate::ExternCrate),
+ )
+ },
+ )
};
from_crate_root.or_else(from_extern_prelude)
}
fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs {
- if let Some(prelude) = self.prelude {
+ if let Some((prelude, _use)) = self.prelude {
let keep;
let def_map = if prelude.krate == self.krate {
self
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index dd7c3c363..e7cc44b04 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -168,7 +168,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Foo: t v
+ Foo: ti vi
foo: t
crate::foo
@@ -194,8 +194,8 @@ pub enum Quux {};
"#,
expect![[r#"
crate
- Baz: t v
- Quux: t
+ Baz: ti vi
+ Quux: ti
foo: t
crate::foo
@@ -225,11 +225,11 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -274,7 +274,7 @@ use self::E::V;
expect![[r#"
crate
E: t
- V: t v
+ V: ti vi
"#]],
);
}
@@ -307,7 +307,7 @@ pub struct FromLib;
crate::foo
Bar: _
- FromLib: t v
+ FromLib: ti vi
"#]],
);
}
@@ -328,7 +328,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t
+ Baz: ti
foo: t
crate::foo
@@ -352,7 +352,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -375,13 +375,13 @@ pub struct Arc;
expect![[r#"
crate
alloc: t
- alloc_crate: t
+ alloc_crate: te
sync: t
crate::alloc
crate::sync
- Arc: t v
+ Arc: ti vi
"#]],
);
}
@@ -404,13 +404,13 @@ pub struct Arc;
expect![[r#"
crate
alloc: t
- alloc_crate: t
+ alloc_crate: te
sync: t
crate::alloc
crate::sync
- Arc: t v
+ Arc: ti vi
"#]],
);
}
@@ -426,7 +426,7 @@ extern crate self as bla;
"#,
expect![[r#"
crate
- bla: t
+ bla: te
"#]],
);
}
@@ -447,7 +447,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -465,7 +465,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: v
"#]],
);
@@ -492,9 +492,9 @@ fn no_std_prelude() {
}
"#,
expect![[r#"
- crate
- Rust: t v
- "#]],
+ crate
+ Rust: ti vi
+ "#]],
);
}
@@ -516,9 +516,9 @@ fn edition_specific_preludes() {
}
"#,
expect![[r#"
- crate
- Rust2018: t v
- "#]],
+ crate
+ Rust2018: ti vi
+ "#]],
);
check(
r#"
@@ -533,9 +533,9 @@ fn edition_specific_preludes() {
}
"#,
expect![[r#"
- crate
- Rust2021: t v
- "#]],
+ crate
+ Rust2021: ti vi
+ "#]],
);
}
@@ -563,8 +563,8 @@ pub mod prelude {
"#,
expect![[r#"
crate
- Bar: t v
- Foo: t v
+ Bar: ti vi
+ Foo: ti vi
"#]],
);
}
@@ -590,7 +590,7 @@ pub mod prelude {
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
Baz: _
Foo: _
"#]],
@@ -619,8 +619,8 @@ pub mod prelude {
expect![[r#"
crate
Bar: _
- Baz: t v
- Foo: t v
+ Baz: ti vi
+ Foo: ti vi
"#]],
);
}
@@ -643,7 +643,7 @@ mod b {
"#,
expect![[r#"
crate
- T: t v
+ T: ti vi
a: t
b: t
@@ -816,8 +816,8 @@ fn bar() {}
expect![[r#"
crate
bar: v
- baz: v
- foo: t
+ baz: vi
+ foo: ti
"#]],
);
}
@@ -836,7 +836,7 @@ use self::m::S::{self};
"#,
expect![[r#"
crate
- S: t
+ S: ti
m: t
crate::m
@@ -860,8 +860,8 @@ pub const settings: () = ();
"#,
expect![[r#"
crate
- Settings: t v
- settings: v
+ Settings: ti vi
+ settings: vi
"#]],
)
}
@@ -890,8 +890,8 @@ pub struct Struct;
"#,
expect![[r#"
crate
- Struct: t v
- dep: t
+ Struct: ti vi
+ dep: te
"#]],
);
}
@@ -917,13 +917,13 @@ use some_module::unknown_func;
crate
other_module: t
some_module: t
- unknown_func: v
+ unknown_func: vi
crate::other_module
some_submodule: t
crate::other_module::some_submodule
- unknown_func: v
+ unknown_func: vi
crate::some_module
unknown_func: v
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
index 88a3c7639..1ca74b5da 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs
@@ -24,7 +24,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
Foo: t v
bar: t
@@ -237,9 +237,9 @@ pub mod baz { pub struct Bar; }
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
bar: t
- baz: t
+ baz: ti
foo: t
crate::bar
@@ -276,9 +276,9 @@ pub mod baz { pub struct Bar; }
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
bar: t
- baz: t
+ baz: ti
foo: t
crate::bar
@@ -323,7 +323,7 @@ mod d {
X: t v
crate::b
- foo: t
+ foo: ti
crate::c
foo: t
@@ -332,8 +332,8 @@ mod d {
Y: t v
crate::d
- Y: t v
- foo: t
+ Y: ti vi
+ foo: ti
"#]],
);
}
@@ -355,7 +355,7 @@ use event::Event;
"#,
expect![[r#"
crate
- Event: t
+ Event: ti
event: t
crate::event
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index 40d3a1654..4a86f88e5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -212,7 +212,7 @@ pub type Ty = ();
}
for (_, res) in module_data.scope.resolutions() {
- match res.values.or(res.types).unwrap().0 {
+ match res.values.map(|(a, _, _)| a).or(res.types.map(|(a, _, _)| a)).unwrap() {
ModuleDefId::FunctionId(f) => _ = db.function_data(f),
ModuleDefId::AdtId(adt) => match adt {
AdtId::StructId(it) => _ = db.struct_data(it),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
index f4cca8d68..e64fa0b46 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs
@@ -203,8 +203,8 @@ macro_rules! bar {
expect![[r#"
crate
Foo: t
- bar: m
- foo: m
+ bar: mi
+ foo: mi
"#]],
);
}
@@ -251,7 +251,7 @@ mod priv_mod {
Bar: t v
Foo: t v
bar: t
- foo: t
+ foo: te
crate::bar
Baz: t v
@@ -318,9 +318,9 @@ macro_rules! baz3 { () => { struct OkBaz3; } }
OkBaz1: t v
OkBaz2: t v
OkBaz3: t v
- all: t
- empty: t
- multiple: t
+ all: te
+ empty: te
+ multiple: te
"#]],
);
}
@@ -551,8 +551,8 @@ fn baz() {}
"#,
expect![[r#"
crate
- bar: t m
- baz: t v m
+ bar: ti mi
+ baz: ti v mi
foo: t m
"#]],
);
@@ -583,7 +583,7 @@ mod m {
crate
Alias: t v
Direct: t v
- foo: t
+ foo: te
"#]],
);
}
@@ -628,9 +628,9 @@ mod m {
m: t
crate::m
- alias1: m
- alias2: m
- alias3: m
+ alias1: mi
+ alias2: mi
+ alias3: mi
not_found: _
"#]],
);
@@ -682,11 +682,11 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Bar: t v
- Baz: t v
+ Bar: ti vi
+ Baz: ti vi
Foo: t v
- FooSelf: t v
- foo: t
+ FooSelf: ti vi
+ foo: te
m: t
crate::m
@@ -725,7 +725,7 @@ pub struct bar;
"#,
expect![[r#"
crate
- bar: t v
+ bar: ti vi
"#]],
);
}
@@ -1340,7 +1340,7 @@ pub mod prelude {
crate
Ok: t v
bar: m
- dep: t
+ dep: te
foo: m
ok: v
"#]],
@@ -1370,13 +1370,13 @@ macro_rules! mk_foo {
}
"#,
expect![[r#"
- crate
- a: t
- lib: t
+ crate
+ a: t
+ lib: te
- crate::a
- Ok: t v
- "#]],
+ crate::a
+ Ok: t v
+ "#]],
);
}
@@ -1427,8 +1427,8 @@ pub mod prelude {
expect![[r#"
crate
Ok: t v
- bar: m
- foo: m
+ bar: mi
+ foo: mi
ok: v
"#]],
);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
index 81bc0ff91..1327d9aa6 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/mod_resolution.rs
@@ -80,18 +80,18 @@ pub trait Iterator;
prelude: t
crate::iter
- Iterator: t
+ Iterator: ti
traits: t
crate::iter::traits
- Iterator: t
+ Iterator: ti
iterator: t
crate::iter::traits::iterator
Iterator: t
crate::prelude
- Iterator: t
+ Iterator: ti
"#]],
);
}
@@ -109,7 +109,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: t
crate::foo
@@ -139,7 +139,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
r#async: t
crate::r#async
@@ -176,8 +176,8 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
- Foo: t v
+ Bar: ti vi
+ Foo: ti vi
r#async: t
crate::r#async
@@ -207,7 +207,7 @@ pub struct Bar;
"#,
expect![[r#"
crate
- Bar: t v
+ Bar: ti vi
foo: t
crate::foo
@@ -236,7 +236,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -265,7 +265,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -292,7 +292,7 @@ use super::Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
"#]],
);
}
@@ -626,7 +626,7 @@ pub struct Baz;
"#,
expect![[r#"
crate
- Baz: t v
+ Baz: ti vi
foo: t
crate::foo
@@ -660,7 +660,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -694,7 +694,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -728,7 +728,7 @@ pub struct Baz;
foo: t
crate::foo
- Baz: t v
+ Baz: ti vi
bar: t
crate::foo::bar
@@ -868,7 +868,7 @@ pub mod hash { pub trait Hash {} }
"#,
expect![[r#"
crate
- Hash: t
+ Hash: ti
core: t
crate::core
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
index 215e8952d..271eb1c79 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
@@ -14,10 +14,10 @@ pub use i32 as int;
expect![[r#"
crate
foo: t
- int: t
+ int: ti
crate::foo
- int: t
+ int: ti
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
index 06530cc7e..3894172a5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/path.rs
@@ -47,7 +47,7 @@ pub enum Path {
},
/// A link to a lang item. It is used in desugaring of things like `it?`. We can show these
/// links via a normal path since they might be private and not accessible in the usage place.
- LangItem(LangItemTarget),
+ LangItem(LangItemTarget, Option<Name>),
}
/// Generic arguments to a path segment (e.g. the `i32` in `Option<i32>`). This
@@ -122,33 +122,40 @@ impl Path {
pub fn kind(&self) -> &PathKind {
match self {
Path::Normal { mod_path, .. } => &mod_path.kind,
- Path::LangItem(_) => &PathKind::Abs,
+ Path::LangItem(..) => &PathKind::Abs,
}
}
pub fn type_anchor(&self) -> Option<&TypeRef> {
match self {
Path::Normal { type_anchor, .. } => type_anchor.as_deref(),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
pub fn segments(&self) -> PathSegments<'_> {
- let Path::Normal { mod_path, generic_args, .. } = self else {
- return PathSegments { segments: &[], generic_args: None };
- };
- let s =
- PathSegments { segments: mod_path.segments(), generic_args: generic_args.as_deref() };
- if let Some(generic_args) = s.generic_args {
- assert_eq!(s.segments.len(), generic_args.len());
+ match self {
+ Path::Normal { mod_path, generic_args, .. } => {
+ let s = PathSegments {
+ segments: mod_path.segments(),
+ generic_args: generic_args.as_deref(),
+ };
+ if let Some(generic_args) = s.generic_args {
+ assert_eq!(s.segments.len(), generic_args.len());
+ }
+ s
+ }
+ Path::LangItem(_, seg) => PathSegments {
+ segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
+ generic_args: None,
+ },
}
- s
}
pub fn mod_path(&self) -> Option<&ModPath> {
match self {
Path::Normal { mod_path, .. } => Some(&mod_path),
- Path::LangItem(_) => None,
+ Path::LangItem(..) => None,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs
index 2bc1f8e92..14890364d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs
@@ -3,13 +3,24 @@
//!
//! `PerNs` (per namespace) captures this.
-use crate::{item_scope::ItemInNs, visibility::Visibility, MacroId, ModuleDefId};
+use crate::{
+ item_scope::{ImportId, ImportOrExternCrate, ItemInNs},
+ visibility::Visibility,
+ MacroId, ModuleDefId,
+};
+
+#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
+pub enum Namespace {
+ Types,
+ Values,
+ Macros,
+}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PerNs {
- pub types: Option<(ModuleDefId, Visibility)>,
- pub values: Option<(ModuleDefId, Visibility)>,
- pub macros: Option<(MacroId, Visibility)>,
+ pub types: Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)>,
+ pub values: Option<(ModuleDefId, Visibility, Option<ImportId>)>,
+ pub macros: Option<(MacroId, Visibility, Option<ImportId>)>,
}
impl Default for PerNs {
@@ -23,20 +34,29 @@ impl PerNs {
PerNs { types: None, values: None, macros: None }
}
- pub fn values(t: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: None, values: Some((t, v)), macros: None }
+ pub fn values(t: ModuleDefId, v: Visibility, i: Option<ImportId>) -> PerNs {
+ PerNs { types: None, values: Some((t, v, i)), macros: None }
}
- pub fn types(t: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: Some((t, v)), values: None, macros: None }
+ pub fn types(t: ModuleDefId, v: Visibility, i: Option<ImportOrExternCrate>) -> PerNs {
+ PerNs { types: Some((t, v, i)), values: None, macros: None }
}
- pub fn both(types: ModuleDefId, values: ModuleDefId, v: Visibility) -> PerNs {
- PerNs { types: Some((types, v)), values: Some((values, v)), macros: None }
+ pub fn both(
+ types: ModuleDefId,
+ values: ModuleDefId,
+ v: Visibility,
+ i: Option<ImportOrExternCrate>,
+ ) -> PerNs {
+ PerNs {
+ types: Some((types, v, i)),
+ values: Some((values, v, i.and_then(ImportOrExternCrate::into_import))),
+ macros: None,
+ }
}
- pub fn macros(macro_: MacroId, v: Visibility) -> PerNs {
- PerNs { types: None, values: None, macros: Some((macro_, v)) }
+ pub fn macros(macro_: MacroId, v: Visibility, i: Option<ImportId>) -> PerNs {
+ PerNs { types: None, values: None, macros: Some((macro_, v, i)) }
}
pub fn is_none(&self) -> bool {
@@ -51,7 +71,7 @@ impl PerNs {
self.types.map(|it| it.0)
}
- pub fn take_types_vis(self) -> Option<(ModuleDefId, Visibility)> {
+ pub fn take_types_full(self) -> Option<(ModuleDefId, Visibility, Option<ImportOrExternCrate>)> {
self.types
}
@@ -59,24 +79,32 @@ impl PerNs {
self.values.map(|it| it.0)
}
+ pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
+ self.values.map(|it| (it.0, it.2))
+ }
+
pub fn take_macros(self) -> Option<MacroId> {
self.macros.map(|it| it.0)
}
+ pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
+ self.macros.map(|it| (it.0, it.2))
+ }
+
pub fn filter_visibility(self, mut f: impl FnMut(Visibility) -> bool) -> PerNs {
let _p = profile::span("PerNs::filter_visibility");
PerNs {
- types: self.types.filter(|(_, v)| f(*v)),
- values: self.values.filter(|(_, v)| f(*v)),
- macros: self.macros.filter(|(_, v)| f(*v)),
+ types: self.types.filter(|&(_, v, _)| f(v)),
+ values: self.values.filter(|&(_, v, _)| f(v)),
+ macros: self.macros.filter(|&(_, v, _)| f(v)),
}
}
pub fn with_visibility(self, vis: Visibility) -> PerNs {
PerNs {
- types: self.types.map(|(it, _)| (it, vis)),
- values: self.values.map(|(it, _)| (it, vis)),
- macros: self.macros.map(|(it, _)| (it, vis)),
+ types: self.types.map(|(it, _, c)| (it, vis, c)),
+ values: self.values.map(|(it, _, c)| (it, vis, c)),
+ macros: self.macros.map(|(it, _, import)| (it, vis, import)),
}
}
@@ -96,12 +124,20 @@ impl PerNs {
}
}
- pub fn iter_items(self) -> impl Iterator<Item = ItemInNs> {
+ pub fn iter_items(self) -> impl Iterator<Item = (ItemInNs, Option<ImportOrExternCrate>)> {
let _p = profile::span("PerNs::iter_items");
self.types
- .map(|it| ItemInNs::Types(it.0))
+ .map(|it| (ItemInNs::Types(it.0), it.2))
.into_iter()
- .chain(self.values.map(|it| ItemInNs::Values(it.0)).into_iter())
- .chain(self.macros.map(|it| ItemInNs::Macros(it.0)).into_iter())
+ .chain(
+ self.values
+ .map(|it| (ItemInNs::Values(it.0), it.2.map(ImportOrExternCrate::Import)))
+ .into_iter(),
+ )
+ .chain(
+ self.macros
+ .map(|it| (ItemInNs::Macros(it.0), it.2.map(ImportOrExternCrate::Import)))
+ .into_iter(),
+ )
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
index 11d58a6ba..f4f5541e3 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/pretty.rs
@@ -2,18 +2,54 @@
use std::fmt::{self, Write};
-use hir_expand::{db::ExpandDatabase, mod_path::PathKind};
+use hir_expand::mod_path::PathKind;
use intern::Interned;
use itertools::Itertools;
use crate::{
+ db::DefDatabase,
+ lang_item::LangItemTarget,
path::{GenericArg, GenericArgs, Path},
type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef},
};
-pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
- if let Path::LangItem(it) = path {
- return write!(buf, "$lang_item::{it:?}");
+pub(crate) fn print_path(db: &dyn DefDatabase, path: &Path, buf: &mut dyn Write) -> fmt::Result {
+ if let Path::LangItem(it, s) = path {
+ write!(buf, "builtin#lang(")?;
+ match *it {
+ LangItemTarget::ImplDef(it) => write!(buf, "{it:?}")?,
+ LangItemTarget::EnumId(it) => {
+ write!(buf, "{}", db.enum_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Function(it) => {
+ write!(buf, "{}", db.function_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Static(it) => {
+ write!(buf, "{}", db.static_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Struct(it) => {
+ write!(buf, "{}", db.struct_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Union(it) => {
+ write!(buf, "{}", db.union_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::TypeAlias(it) => {
+ write!(buf, "{}", db.type_alias_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::Trait(it) => {
+ write!(buf, "{}", db.trait_data(it).name.display(db.upcast()))?
+ }
+ LangItemTarget::EnumVariant(it) => write!(
+ buf,
+ "{}",
+ db.enum_data(it.parent).variants[it.local_id].name.display(db.upcast())
+ )?,
+ }
+
+ if let Some(s) = s {
+ write!(buf, "::{}", s.display(db.upcast()))?;
+ }
+ return write!(buf, ")");
}
match path.type_anchor() {
Some(anchor) => {
@@ -44,7 +80,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
write!(buf, "::")?;
}
- write!(buf, "{}", segment.name.display(db))?;
+ write!(buf, "{}", segment.name.display(db.upcast()))?;
if let Some(generics) = segment.args_and_bindings {
write!(buf, "::<")?;
print_generic_args(db, generics, buf)?;
@@ -57,7 +93,7 @@ pub(crate) fn print_path(db: &dyn ExpandDatabase, path: &Path, buf: &mut dyn Wri
}
pub(crate) fn print_generic_args(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
generics: &GenericArgs,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -83,7 +119,7 @@ pub(crate) fn print_generic_args(
write!(buf, ", ")?;
}
first = false;
- write!(buf, "{}", binding.name.display(db))?;
+ write!(buf, "{}", binding.name.display(db.upcast()))?;
if !binding.bounds.is_empty() {
write!(buf, ": ")?;
print_type_bounds(db, &binding.bounds, buf)?;
@@ -97,19 +133,19 @@ pub(crate) fn print_generic_args(
}
pub(crate) fn print_generic_arg(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
arg: &GenericArg,
buf: &mut dyn Write,
) -> fmt::Result {
match arg {
GenericArg::Type(ty) => print_type_ref(db, ty, buf),
- GenericArg::Const(c) => write!(buf, "{}", c.display(db)),
- GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db)),
+ GenericArg::Const(c) => write!(buf, "{}", c.display(db.upcast())),
+ GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast())),
}
}
pub(crate) fn print_type_ref(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
type_ref: &TypeRef,
buf: &mut dyn Write,
) -> fmt::Result {
@@ -143,7 +179,7 @@ pub(crate) fn print_type_ref(
};
write!(buf, "&")?;
if let Some(lt) = lt {
- write!(buf, "{} ", lt.name.display(db))?;
+ write!(buf, "{} ", lt.name.display(db.upcast()))?;
}
write!(buf, "{mtbl}")?;
print_type_ref(db, pointee, buf)?;
@@ -151,7 +187,7 @@ pub(crate) fn print_type_ref(
TypeRef::Array(elem, len) => {
write!(buf, "[")?;
print_type_ref(db, elem, buf)?;
- write!(buf, "; {}]", len.display(db))?;
+ write!(buf, "; {}]", len.display(db.upcast()))?;
}
TypeRef::Slice(elem) => {
write!(buf, "[")?;
@@ -198,7 +234,7 @@ pub(crate) fn print_type_ref(
}
pub(crate) fn print_type_bounds(
- db: &dyn ExpandDatabase,
+ db: &dyn DefDatabase,
bounds: &[Interned<TypeBound>],
buf: &mut dyn Write,
) -> fmt::Result {
@@ -216,10 +252,14 @@ pub(crate) fn print_type_bounds(
print_path(db, path, buf)?;
}
TypeBound::ForLifetime(lifetimes, path) => {
- write!(buf, "for<{}> ", lifetimes.iter().map(|it| it.display(db)).format(", "))?;
+ write!(
+ buf,
+ "for<{}> ",
+ lifetimes.iter().map(|it| it.display(db.upcast())).format(", ")
+ )?;
print_path(db, path, buf)?;
}
- TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db))?,
+ TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name.display(db.upcast()))?,
TypeBound::Error => write!(buf, "{{unknown}}")?,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index b112c1070..50da9ed06 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -12,20 +12,21 @@ use triomphe::Arc;
use crate::{
body::scope::{ExprScopes, ScopeId},
builtin_type::BuiltinType,
+ data::ExternCrateDeclData,
db::DefDatabase,
generics::{GenericParams, TypeOrConstParamData},
hir::{BindingId, ExprId, LabelId},
- item_scope::{BuiltinShadowMode, BUILTIN_SCOPE},
+ item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::{DefMap, MacroSubNs},
path::{ModPath, Path, PathKind},
per_ns::PerNs,
visibility::{RawVisibility, Visibility},
- AdtId, AssocItemId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId,
- EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId,
- HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId,
- MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId,
- TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
+ AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId,
+ ExternBlockId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId,
+ ItemContainerId, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, MacroId, MacroRulesId,
+ ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId,
+ TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId,
};
#[derive(Debug, Clone)]
@@ -100,8 +101,8 @@ pub enum TypeNs {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ResolveValueResult {
- ValueNs(ValueNs),
- Partial(TypeNs, usize),
+ ValueNs(ValueNs, Option<ImportId>),
+ Partial(TypeNs, usize, Option<ImportOrExternCrate>),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
@@ -148,56 +149,26 @@ impl Resolver {
self.resolve_module_path(db, path, BuiltinShadowMode::Module)
}
- // FIXME: This shouldn't exist
- pub fn resolve_module_path_in_trait_assoc_items(
- &self,
- db: &dyn DefDatabase,
- path: &ModPath,
- ) -> Option<PerNs> {
- let (item_map, module) = self.item_scope();
- let (module_res, idx) =
- item_map.resolve_path(db, module, path, BuiltinShadowMode::Module, None);
- match module_res.take_types()? {
- ModuleDefId::TraitId(it) => {
- let idx = idx?;
- let unresolved = &path.segments()[idx..];
- let assoc = match unresolved {
- [it] => it,
- _ => return None,
- };
- let &(_, assoc) = db.trait_data(it).items.iter().find(|(n, _)| n == assoc)?;
- Some(match assoc {
- AssocItemId::FunctionId(it) => PerNs::values(it.into(), Visibility::Public),
- AssocItemId::ConstId(it) => PerNs::values(it.into(), Visibility::Public),
- AssocItemId::TypeAliasId(it) => PerNs::types(it.into(), Visibility::Public),
- })
- }
- _ => None,
- }
- }
-
pub fn resolve_path_in_type_ns(
&self,
db: &dyn DefDatabase,
path: &Path,
- ) -> Option<(TypeNs, Option<usize>)> {
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
- return Some((
- match *l {
- LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
- LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
- LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
- LangItemTarget::Trait(it) => TypeNs::TraitId(it),
- LangItemTarget::Function(_)
- | LangItemTarget::ImplDef(_)
- | LangItemTarget::Static(_) => return None,
- },
- None,
- ))
+ Path::LangItem(l, seg) => {
+ let type_ns = match *l {
+ LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+ LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+ LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+ LangItemTarget::Function(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::Static(_) => return None,
+ };
+ return Some((type_ns, seg.as_ref().map(|_| 1), None));
}
};
let first_name = path.segments().first()?;
@@ -213,17 +184,17 @@ impl Resolver {
Scope::ExprScope(_) => continue,
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
- return Some((TypeNs::GenericParam(id), remaining_idx()));
+ return Some((TypeNs::GenericParam(id), remaining_idx(), None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some((TypeNs::SelfType(impl_), remaining_idx()));
+ return Some((TypeNs::SelfType(impl_), remaining_idx(), None));
}
}
&Scope::AdtScope(adt) => {
if first_name == &name![Self] {
- return Some((TypeNs::AdtSelfType(adt), remaining_idx()));
+ return Some((TypeNs::AdtSelfType(adt), remaining_idx(), None));
}
}
Scope::BlockScope(m) => {
@@ -236,12 +207,24 @@ impl Resolver {
self.module_scope.resolve_path_in_type_ns(db, path)
}
+ pub fn resolve_path_in_type_ns_fully_with_imports(
+ &self,
+ db: &dyn DefDatabase,
+ path: &Path,
+ ) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+ let (res, unresolved, imp) = self.resolve_path_in_type_ns(db, path)?;
+ if unresolved.is_some() {
+ return None;
+ }
+ Some((res, imp))
+ }
+
pub fn resolve_path_in_type_ns_fully(
&self,
db: &dyn DefDatabase,
path: &Path,
) -> Option<TypeNs> {
- let (res, unresolved) = self.resolve_path_in_type_ns(db, path)?;
+ let (res, unresolved, _) = self.resolve_path_in_type_ns(db, path)?;
if unresolved.is_some() {
return None;
}
@@ -263,7 +246,6 @@ impl Resolver {
RawVisibility::Public => Some(Visibility::Public),
}
}
-
pub fn resolve_path_in_value_ns(
&self,
db: &dyn DefDatabase,
@@ -271,18 +253,35 @@ impl Resolver {
) -> Option<ResolveValueResult> {
let path = match path {
Path::Normal { mod_path, .. } => mod_path,
- Path::LangItem(l) => {
- return Some(ResolveValueResult::ValueNs(match *l {
- LangItemTarget::Function(it) => ValueNs::FunctionId(it),
- LangItemTarget::Static(it) => ValueNs::StaticId(it),
- LangItemTarget::Struct(it) => ValueNs::StructId(it),
- LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
- LangItemTarget::Union(_)
+ Path::LangItem(l, None) => {
+ return Some(ResolveValueResult::ValueNs(
+ match *l {
+ LangItemTarget::Function(it) => ValueNs::FunctionId(it),
+ LangItemTarget::Static(it) => ValueNs::StaticId(it),
+ LangItemTarget::Struct(it) => ValueNs::StructId(it),
+ LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
+ LangItemTarget::Union(_)
+ | LangItemTarget::ImplDef(_)
+ | LangItemTarget::TypeAlias(_)
+ | LangItemTarget::Trait(_)
+ | LangItemTarget::EnumId(_) => return None,
+ },
+ None,
+ ))
+ }
+ Path::LangItem(l, Some(_)) => {
+ let type_ns = match *l {
+ LangItemTarget::Union(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::TypeAlias(it) => TypeNs::TypeAliasId(it),
+ LangItemTarget::Struct(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::EnumVariant(it) => TypeNs::EnumVariantId(it),
+ LangItemTarget::EnumId(it) => TypeNs::AdtId(it.into()),
+ LangItemTarget::Trait(it) => TypeNs::TraitId(it),
+ LangItemTarget::Function(_)
| LangItemTarget::ImplDef(_)
- | LangItemTarget::TypeAlias(_)
- | LangItemTarget::Trait(_)
- | LangItemTarget::EnumId(_) => return None,
- }))
+ | LangItemTarget::Static(_) => return None,
+ };
+ return Some(ResolveValueResult::Partial(type_ns, 1, None));
}
};
let n_segments = path.segments().len();
@@ -304,20 +303,24 @@ impl Resolver {
.find(|entry| entry.name() == first_name);
if let Some(e) = entry {
- return Some(ResolveValueResult::ValueNs(ValueNs::LocalBinding(
- e.binding(),
- )));
+ return Some(ResolveValueResult::ValueNs(
+ ValueNs::LocalBinding(e.binding()),
+ None,
+ ));
}
}
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_const_by_name(first_name, *def) {
let val = ValueNs::GenericParam(id);
- return Some(ResolveValueResult::ValueNs(val));
+ return Some(ResolveValueResult::ValueNs(val, None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some(ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_)));
+ return Some(ResolveValueResult::ValueNs(
+ ValueNs::ImplSelf(impl_),
+ None,
+ ));
}
}
// bare `Self` doesn't work in the value namespace in a struct/enum definition
@@ -336,18 +339,22 @@ impl Resolver {
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
let ty = TypeNs::GenericParam(id);
- return Some(ResolveValueResult::Partial(ty, 1));
+ return Some(ResolveValueResult::Partial(ty, 1, None));
}
}
&Scope::ImplDefScope(impl_) => {
if first_name == &name![Self] {
- return Some(ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1));
+ return Some(ResolveValueResult::Partial(
+ TypeNs::SelfType(impl_),
+ 1,
+ None,
+ ));
}
}
Scope::AdtScope(adt) => {
if first_name == &name![Self] {
let ty = TypeNs::AdtSelfType(*adt);
- return Some(ResolveValueResult::Partial(ty, 1));
+ return Some(ResolveValueResult::Partial(ty, 1, None));
}
}
Scope::BlockScope(m) => {
@@ -368,7 +375,7 @@ impl Resolver {
// `use core::u16;`.
if path.kind == PathKind::Plain && n_segments > 1 {
if let Some(builtin) = BuiltinType::by_name(first_name) {
- return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1));
+ return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None));
}
}
@@ -381,7 +388,7 @@ impl Resolver {
path: &Path,
) -> Option<ValueNs> {
match self.resolve_path_in_value_ns(db, path)? {
- ResolveValueResult::ValueNs(it) => Some(it),
+ ResolveValueResult::ValueNs(it, _) => Some(it),
ResolveValueResult::Partial(..) => None,
}
}
@@ -391,12 +398,12 @@ impl Resolver {
db: &dyn DefDatabase,
path: &ModPath,
expected_macro_kind: Option<MacroSubNs>,
- ) -> Option<MacroId> {
+ ) -> Option<(MacroId, Option<ImportId>)> {
let (item_map, module) = self.item_scope();
item_map
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
.0
- .take_macros()
+ .take_macros_import()
}
/// Returns a set of names available in the current scope.
@@ -456,21 +463,22 @@ impl Resolver {
def_map[module_id].scope.entries().for_each(|(name, def)| {
res.add_per_ns(name, def);
});
+
def_map[module_id].scope.legacy_macros().for_each(|(name, macs)| {
macs.iter().for_each(|&mac| {
res.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)));
})
});
- def_map.macro_use_prelude().for_each(|(name, def)| {
+ def_map.macro_use_prelude().for_each(|(name, (def, _extern_crate))| {
res.add(name, ScopeDef::ModuleDef(def.into()));
});
- def_map.extern_prelude().for_each(|(name, def)| {
- res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def)));
+ def_map.extern_prelude().for_each(|(name, (def, _extern_crate))| {
+ res.add(name, ScopeDef::ModuleDef(ModuleDefId::ModuleId(def.into())));
});
BUILTIN_SCOPE.iter().for_each(|(name, &def)| {
res.add_per_ns(name, def);
});
- if let Some(prelude) = def_map.prelude() {
+ if let Some((prelude, _use)) = def_map.prelude() {
let prelude_def_map = prelude.def_map(db);
for (name, def) in prelude_def_map[prelude.local_id].scope.entries() {
res.add_per_ns(name, def)
@@ -479,6 +487,23 @@ impl Resolver {
res.map
}
+ pub fn extern_crate_decls_in_scope<'a>(
+ &'a self,
+ db: &'a dyn DefDatabase,
+ ) -> impl Iterator<Item = Name> + 'a {
+ self.module_scope.def_map[self.module_scope.module_id]
+ .scope
+ .extern_crate_decls()
+ .map(|id| ExternCrateDeclData::extern_crate_decl_data_query(db, id).name.clone())
+ }
+
+ pub fn extern_crates_in_scope<'a>(&'a self) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
+ self.module_scope
+ .def_map
+ .extern_prelude()
+ .map(|(name, module_id)| (name.clone(), module_id.0.into()))
+ }
+
pub fn traits_in_scope(&self, db: &dyn DefDatabase) -> FxHashSet<TraitId> {
// FIXME(trait_alias): Trait alias brings aliased traits in scope! Note that supertraits of
// aliased traits are NOT brought in scope (unless also aliased).
@@ -501,7 +526,7 @@ impl Resolver {
}
// Fill in the prelude traits
- if let Some(prelude) = self.module_scope.def_map.prelude() {
+ if let Some((prelude, _use)) = self.module_scope.def_map.prelude() {
let prelude_def_map = prelude.def_map(db);
traits.extend(prelude_def_map[prelude.local_id].scope.traits());
}
@@ -804,11 +829,12 @@ impl ModuleItemMap {
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
match idx {
None => {
- let value = to_value_ns(module_def)?;
- Some(ResolveValueResult::ValueNs(value))
+ let (value, import) = to_value_ns(module_def)?;
+ Some(ResolveValueResult::ValueNs(value, import))
}
Some(idx) => {
- let ty = match module_def.take_types()? {
+ let (def, _, import) = module_def.take_types_full()?;
+ let ty = match def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::TraitId(it) => TypeNs::TraitId(it),
ModuleDefId::TraitAliasId(it) => TypeNs::TraitAliasId(it),
@@ -822,7 +848,7 @@ impl ModuleItemMap {
| ModuleDefId::MacroId(_)
| ModuleDefId::StaticId(_) => return None,
};
- Some(ResolveValueResult::Partial(ty, idx))
+ Some(ResolveValueResult::Partial(ty, idx, import))
}
}
}
@@ -831,16 +857,17 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
- ) -> Option<(TypeNs, Option<usize>)> {
+ ) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let (module_def, idx) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
- let res = to_type_ns(module_def)?;
- Some((res, idx))
+ let (res, import) = to_type_ns(module_def)?;
+ Some((res, idx, import))
}
}
-fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
- let res = match per_ns.take_values()? {
+fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
+ let (def, import) = per_ns.take_values_import()?;
+ let res = match def {
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
ModuleDefId::AdtId(AdtId::StructId(it)) => ValueNs::StructId(it),
ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariantId(it),
@@ -855,11 +882,12 @@ fn to_value_ns(per_ns: PerNs) -> Option<ValueNs> {
| ModuleDefId::MacroId(_)
| ModuleDefId::ModuleId(_) => return None,
};
- Some(res)
+ Some((res, import))
}
-fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
- let res = match per_ns.take_types()? {
+fn to_type_ns(per_ns: PerNs) -> Option<(TypeNs, Option<ImportOrExternCrate>)> {
+ let (def, _, import) = per_ns.take_types_full()?;
+ let res = match def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariantId(it),
@@ -875,7 +903,7 @@ fn to_type_ns(per_ns: PerNs) -> Option<TypeNs> {
| ModuleDefId::StaticId(_)
| ModuleDefId::ModuleId(_) => return None,
};
- Some(res)
+ Some((res, import))
}
type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<rustc_hash::FxHasher>>;
@@ -892,13 +920,13 @@ impl ScopeNames {
}
}
fn add_per_ns(&mut self, name: &Name, def: PerNs) {
- if let &Some((ty, _)) = &def.types {
+ if let &Some((ty, _, _)) = &def.types {
self.add(name, ScopeDef::ModuleDef(ty))
}
- if let &Some((def, _)) = &def.values {
+ if let &Some((def, _, _)) = &def.values {
self.add(name, ScopeDef::ModuleDef(def))
}
- if let &Some((mac, _)) = &def.macros {
+ if let &Some((mac, _, _)) = &def.macros {
self.add(name, ScopeDef::ModuleDef(ModuleDefId::MacroId(mac)))
}
if def.is_none() {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/src.rs b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
index 6047f770d..3770103cd 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/src.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/src.rs
@@ -5,8 +5,8 @@ use la_arena::ArenaMap;
use syntax::ast;
use crate::{
- db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Macro2Loc, MacroRulesLoc,
- ProcMacroLoc,
+ db::DefDatabase, item_tree::ItemTreeNode, AssocItemLoc, ItemLoc, Lookup, Macro2Loc,
+ MacroRulesLoc, ProcMacroLoc, UseId,
};
pub trait HasSource {
@@ -83,3 +83,18 @@ pub trait HasChildSource<ChildId> {
type Value;
fn child_source(&self, db: &dyn DefDatabase) -> InFile<ArenaMap<ChildId, Self::Value>>;
}
+
+impl HasChildSource<la_arena::Idx<ast::UseTree>> for UseId {
+ type Value = ast::UseTree;
+ fn child_source(
+ &self,
+ db: &dyn DefDatabase,
+ ) -> InFile<ArenaMap<la_arena::Idx<ast::UseTree>, Self::Value>> {
+ let loc = &self.lookup(db);
+ let use_ = &loc.id.item_tree(db)[loc.id.value];
+ InFile::new(
+ loc.id.file_id(),
+ use_.use_tree_source_map(db, loc.id.file_id()).into_iter().collect(),
+ )
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
index 4c918e55b..0ec2422b3 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs
@@ -342,14 +342,7 @@ fn inner_attributes(
ast::Impl(it) => it.assoc_item_list()?.syntax().clone(),
ast::Module(it) => it.item_list()?.syntax().clone(),
ast::BlockExpr(it) => {
- use syntax::SyntaxKind::{BLOCK_EXPR , EXPR_STMT};
- // Block expressions accept outer and inner attributes, but only when they are the outer
- // expression of an expression statement or the final expression of another block expression.
- let may_carry_attributes = matches!(
- it.syntax().parent().map(|it| it.kind()),
- Some(BLOCK_EXPR | EXPR_STMT)
- );
- if !may_carry_attributes {
+ if !it.may_carry_attributes() {
return None
}
syntax.clone()
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
index 95c6baf42..30b19b6e5 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs
@@ -1,13 +1,9 @@
//! Builtin macro
-use std::mem;
-
-use ::tt::Ident;
use base_db::{AnchoredPath, Edition, FileId};
use cfg::CfgExpr;
use either::Either;
use mbe::{parse_exprs_with_sep, parse_to_token_tree, TokenMap};
-use rustc_hash::FxHashMap;
use syntax::{
ast::{self, AstToken},
SmolStr,
@@ -97,11 +93,11 @@ register_builtin! {
(unreachable, Unreachable) => unreachable_expand,
(log_syntax, LogSyntax) => log_syntax_expand,
(trace_macros, TraceMacros) => trace_macros_expand,
-
- EAGER:
(format_args, FormatArgs) => format_args_expand,
(const_format_args, ConstFormatArgs) => format_args_expand,
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
+
+ EAGER:
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -247,151 +243,15 @@ fn format_args_expand_general(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
tt: &tt::Subtree,
- end_string: &str,
+ // FIXME: Make use of this so that mir interpretation works properly
+ _end_string: &str,
) -> ExpandResult<tt::Subtree> {
- let args = parse_exprs_with_sep(tt, ',');
-
- let expand_error =
- ExpandResult::new(tt::Subtree::empty(), mbe::ExpandError::NoMatchingRule.into());
-
- let mut key_args = FxHashMap::default();
- let mut args = args.into_iter().filter_map(|mut arg| {
- // Remove `key =`.
- if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
- {
- // but not with `==`
- if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=')
- {
- let key = arg.token_trees.drain(..2).next().unwrap();
- key_args.insert(key.to_string(), arg);
- return None;
- }
- }
- Some(arg)
- }).collect::<Vec<_>>().into_iter();
- // ^^^^^^^ we need this collect, to enforce the side effect of the filter_map closure (building the `key_args`)
- let Some(format_subtree) = args.next() else {
- return expand_error;
- };
- let format_string = (|| {
- let token_tree = format_subtree.token_trees.get(0)?;
- match token_tree {
- tt::TokenTree::Leaf(l) => match l {
- tt::Leaf::Literal(l) => {
- if let Some(mut text) = l.text.strip_prefix('r') {
- let mut raw_sharps = String::new();
- while let Some(t) = text.strip_prefix('#') {
- text = t;
- raw_sharps.push('#');
- }
- text =
- text.strip_suffix(&raw_sharps)?.strip_prefix('"')?.strip_suffix('"')?;
- Some((text, l.span, Some(raw_sharps)))
- } else {
- let text = l.text.strip_prefix('"')?.strip_suffix('"')?;
- let span = l.span;
- Some((text, span, None))
- }
- }
- _ => None,
- },
- tt::TokenTree::Subtree(_) => None,
- }
- })();
- let Some((format_string, _format_string_span, raw_sharps)) = format_string else {
- return expand_error;
- };
- let mut format_iter = format_string.chars().peekable();
- let mut parts = vec![];
- let mut last_part = String::new();
- let mut arg_tts = vec![];
- let mut err = None;
- while let Some(c) = format_iter.next() {
- // Parsing the format string. See https://doc.rust-lang.org/std/fmt/index.html#syntax for the grammar and more info
- match c {
- '{' => {
- if format_iter.peek() == Some(&'{') {
- format_iter.next();
- last_part.push('{');
- continue;
- }
- let mut argument = String::new();
- while ![Some(&'}'), Some(&':')].contains(&format_iter.peek()) {
- argument.push(match format_iter.next() {
- Some(c) => c,
- None => return expand_error,
- });
- }
- let format_spec = match format_iter.next().unwrap() {
- '}' => "".to_owned(),
- ':' => {
- let mut s = String::new();
- while let Some(c) = format_iter.next() {
- if c == '}' {
- break;
- }
- s.push(c);
- }
- s
- }
- _ => unreachable!(),
- };
- parts.push(mem::take(&mut last_part));
- let arg_tree = if argument.is_empty() {
- match args.next() {
- Some(it) => it,
- None => {
- err = Some(mbe::ExpandError::NoMatchingRule.into());
- tt::Subtree::empty()
- }
- }
- } else if let Some(tree) = key_args.get(&argument) {
- tree.clone()
- } else {
- // FIXME: we should pick the related substring of the `_format_string_span` as the span. You
- // can use `.char_indices()` instead of `.char()` for `format_iter` to find the substring interval.
- let ident = Ident::new(argument, tt::TokenId::unspecified());
- quote!(#ident)
- };
- let formatter = match &*format_spec {
- "?" => quote!(::core::fmt::Debug::fmt),
- "" => quote!(::core::fmt::Display::fmt),
- _ => {
- // FIXME: implement the rest and return expand error here
- quote!(::core::fmt::Display::fmt)
- }
- };
- arg_tts.push(quote! { ::core::fmt::ArgumentV1::new(&(#arg_tree), #formatter), });
- }
- '}' => {
- if format_iter.peek() == Some(&'}') {
- format_iter.next();
- last_part.push('}');
- } else {
- return expand_error;
- }
- }
- _ => last_part.push(c),
- }
- }
- last_part += end_string;
- if !last_part.is_empty() {
- parts.push(last_part);
- }
- let part_tts = parts.into_iter().map(|it| {
- let text = if let Some(raw) = &raw_sharps {
- format!("r{raw}\"{}\"{raw}", it).into()
- } else {
- format!("\"{}\"", it).into()
- };
- let l = tt::Literal { span: tt::TokenId::unspecified(), text };
- quote!(#l ,)
+ let pound = quote! {@PUNCT '#'};
+ let mut tt = tt.clone();
+ tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
+ return ExpandResult::ok(quote! {
+ builtin #pound format_args #tt
});
- let arg_tts = arg_tts.into_iter().flat_map(|arg| arg.token_trees);
- let expanded = quote! {
- ::core::fmt::Arguments::new_v1(&[##part_tts], &[##arg_tts])
- };
- ExpandResult { value: expanded, err }
}
fn asm_expand(
@@ -415,10 +275,12 @@ fn asm_expand(
}
}
- let expanded = quote! {{
- ##literals
- loop {}
- }};
+ let pound = quote! {@PUNCT '#'};
+ let expanded = quote! {
+ builtin #pound asm (
+ {##literals}
+ )
+ };
ExpandResult::ok(expanded)
}
@@ -692,7 +554,7 @@ pub(crate) fn include_arg_to_tt(
arg_id: MacroCallId,
) -> Result<(triomphe::Arc<(::tt::Subtree<::tt::TokenId>, TokenMap)>, FileId), ExpandError> {
let loc = db.lookup_intern_macro_call(arg_id);
- let Some(EagerCallInfo { arg,arg_id, .. }) = loc.eager.as_deref() else {
+ let Some(EagerCallInfo { arg, arg_id, .. }) = loc.eager.as_deref() else {
panic!("include_arg_to_tt called on non include macro call: {:?}", &loc.eager);
};
let path = parse_string(&arg.0)?;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index ade4a5928..ca65db113 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -242,7 +242,7 @@ impl HygieneFrame {
krate,
call_site: None,
def_site: None,
- }
+ };
};
let def_site = info.attr_input_or_mac_def_start.map(|it| db.hygiene_frame(it.file_id));
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 1f1e20f49..4be55126b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -37,7 +37,7 @@ use either::Either;
use syntax::{
algo::{self, skip_trivia_token},
ast::{self, AstNode, HasDocComments},
- AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken,
+ AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
};
use crate::{
@@ -544,7 +544,7 @@ impl MacroCallKind {
};
let range = match kind {
- MacroCallKind::FnLike { ast_id, .. } => ast_id.to_node(db).syntax().text_range(),
+ MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
// FIXME: should be the range of the macro name, not the whole derive
// FIXME: handle `cfg_attr`
@@ -642,6 +642,8 @@ impl ExpansionInfo {
db: &dyn db::ExpandDatabase,
item: Option<ast::Item>,
token: InFile<&SyntaxToken>,
+ // FIXME: use this for range mapping, so that we can resolve inline format args
+ _relative_token_offset: Option<TextSize>,
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
assert_eq!(token.file_id, self.arg.file_id);
let token_id_in_attr_input = if let Some(item) = item {
@@ -840,9 +842,6 @@ impl<N: AstIdNode> AstId<N> {
pub type ErasedAstId = InFile<ErasedFileAstId>;
impl ErasedAstId {
- pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
- }
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
db.ast_id_map(self.file_id).get_raw(self.value)
}
@@ -1054,16 +1053,6 @@ impl InFile<SyntaxToken> {
}
}
}
-
- pub fn ancestors_with_macros(
- self,
- db: &dyn db::ExpandDatabase,
- ) -> impl Iterator<Item = InFile<SyntaxNode>> + '_ {
- self.value.parent().into_iter().flat_map({
- let file_id = self.file_id;
- move |parent| InFile::new(file_id, &parent).ancestors_with_macros(db)
- })
- }
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 7c179c0cf..a876f48bd 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -54,6 +54,12 @@ impl Name {
Name(Repr::Text(text))
}
+ // FIXME: See above, unfortunately some places really need this right now
+ #[doc(hidden)]
+ pub const fn new_text_dont_use(text: SmolStr) -> Name {
+ Name(Repr::Text(text))
+ }
+
pub fn new_tuple_field(idx: usize) -> Name {
Name(Repr::TupleField(idx))
}
@@ -302,6 +308,16 @@ pub mod known {
rust_2018,
rust_2021,
v1,
+ new_display,
+ new_debug,
+ new_lower_exp,
+ new_upper_exp,
+ new_octal,
+ new_pointer,
+ new_binary,
+ new_lower_hex,
+ new_upper_hex,
+ from_usize,
// Components of known path (type name)
Iterator,
IntoIterator,
@@ -327,6 +343,13 @@ pub mod known {
Not,
None,
Index,
+ Left,
+ Right,
+ Center,
+ Unknown,
+ Is,
+ Param,
+ Implied,
// Components of known path (function name)
filter_map,
next,
@@ -335,6 +358,8 @@ pub mod known {
is_empty,
as_str,
new,
+ new_v1_formatted,
+ none,
// Builtin macros
asm,
assert,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index abc19d63a..b95ae05cc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -32,7 +32,8 @@ once_cell = "1.17.0"
triomphe.workspace = true
nohash-hasher.workspace = true
typed-arena = "2.0.1"
-rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false }
+
+rustc_index.workspace = true
# local deps
stdx.workspace = true
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
index eec57ba3f..967e028bf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -17,7 +17,8 @@ use smallvec::SmallVec;
use crate::{
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
- GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+ GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt,
+ TyKind,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -79,9 +80,9 @@ impl<D> TyBuilder<D> {
let expected_kind = &self.param_kinds[self.vec.len()];
let arg_kind = match arg.data(Interner) {
- chalk_ir::GenericArgData::Ty(_) => ParamKind::Type,
- chalk_ir::GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
- chalk_ir::GenericArgData::Const(c) => {
+ GenericArgData::Ty(_) => ParamKind::Type,
+ GenericArgData::Lifetime(_) => panic!("Got lifetime in TyBuilder::push"),
+ GenericArgData::Const(c) => {
let c = c.data(Interner);
ParamKind::Const(c.ty.clone())
}
@@ -139,8 +140,8 @@ impl<D> TyBuilder<D> {
fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) {
- (chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
- | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (),
+ (GenericArgData::Ty(_), ParamKind::Type)
+ | (GenericArgData::Const(_), ParamKind::Const(_)) => (),
_ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 1c0f7b08d..0348680e5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -1,7 +1,7 @@
//! Constant evaluation details
use base_db::CrateId;
-use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData};
+use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex};
use hir_def::{
hir::Expr,
path::Path,
@@ -120,7 +120,7 @@ pub fn unknown_const(ty: Ty) -> Const {
}
pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
- GenericArgData::Const(unknown_const(ty)).intern(Interner)
+ unknown_const(ty).cast(Interner)
}
/// Interns a constant scalar with the given type
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index 666955fa1..7ad3659a4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -1203,6 +1203,27 @@ fn destructing_assignment() {
"#,
5,
);
+ check_number(
+ r#"
+ const GOAL: u8 = {
+ let (mut a, mut b) = (2, 5);
+ (a, b) = (b, a);
+ a * 10 + b
+ };
+ "#,
+ 52,
+ );
+ check_number(
+ r#"
+ struct Point { x: i32, y: i32 }
+ const GOAL: i32 = {
+ let mut p = Point { x: 5, y: 6 };
+ (p.x, _) = (p.y, p.x);
+ p.x * 10 + p.y
+ };
+ "#,
+ 66,
+ );
}
#[test]
@@ -1433,6 +1454,30 @@ fn from_trait() {
}
#[test]
+fn closure_clone() {
+ check_number(
+ r#"
+//- minicore: clone, fn
+struct S(u8);
+
+impl Clone for S(u8) {
+ fn clone(&self) -> S {
+ S(self.0 + 5)
+ }
+}
+
+const GOAL: u8 = {
+ let s = S(3);
+ let cl = move || s;
+ let cl = cl.clone();
+ cl().0
+}
+ "#,
+ 8,
+ );
+}
+
+#[test]
fn builtin_derive_macro() {
check_number(
r#"
@@ -2396,14 +2441,14 @@ fn const_loop() {
fn const_transfer_memory() {
check_number(
r#"
- //- minicore: slice, index, coerce_unsized
+ //- minicore: slice, index, coerce_unsized, option
const A1: &i32 = &1;
const A2: &i32 = &10;
const A3: [&i32; 3] = [&1, &2, &100];
- const A4: (i32, &i32) = (1, &1000);
- const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1;
+ const A4: (i32, &i32, Option<&i32>) = (1, &1000, Some(&10000));
+ const GOAL: i32 = *A1 + *A2 + *A3[2] + *A4.1 + *A4.2.unwrap_or(&5);
"#,
- 1111,
+ 11111,
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 2855f7890..44a4ac27a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -499,24 +499,26 @@ fn offset() {
r#"
//- minicore: coerce_unsized, index, slice
extern "rust-intrinsic" {
- pub fn offset<T>(dst: *const T, offset: isize) -> *const T;
+ pub fn offset<Ptr, Delta>(dst: Ptr, offset: Delta) -> Ptr;
+ pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
}
- const GOAL: u8 = unsafe {
- let ar: &[(u8, u8, u8)] = &[
+ const GOAL: i32 = unsafe {
+ let ar: &[(i32, i32, i32)] = &[
(10, 11, 12),
(20, 21, 22),
(30, 31, 32),
(40, 41, 42),
(50, 51, 52),
];
- let ar: *const [(u8, u8, u8)] = ar;
- let ar = ar as *const (u8, u8, u8);
- let element = *offset(ar, 2);
- element.1
+ let ar: *const [(i32, i32, i32)] = ar;
+ let ar = ar as *const (i32, i32, i32);
+ let element3 = *offset(ar, 2usize);
+ let element4 = *arith_offset(ar, 3);
+ element3.1 * 100 + element4.0
};
"#,
- 31,
+ 3140,
);
}
@@ -585,6 +587,24 @@ fn write_bytes() {
}
#[test]
+fn write_via_move() {
+ check_number(
+ r#"
+ extern "rust-intrinsic" {
+ fn write_via_move<T>(ptr: *mut T, value: T);
+ }
+
+ const GOAL: i32 = unsafe {
+ let mut x = 2;
+ write_via_move(&mut x, 100);
+ x
+ };
+ "#,
+ 100,
+ );
+}
+
+#[test]
fn copy() {
check_number(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index a94a962c1..36d69edf9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -163,25 +163,56 @@ impl<'a> DeclValidator<'a> {
|| allows.contains(allow::NONSTANDARD_STYLE)
})
};
+ let db = self.db.upcast();
+ let file_id_is_derive = || {
+ match id {
+ AttrDefId::ModuleId(m) => {
+ m.def_map(db)[m.local_id].origin.file_id().map(Into::into)
+ }
+ AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()),
+ AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()),
+ AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()),
+ AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()),
+ AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()),
+ AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()),
+ AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()),
+ AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()),
+ AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()),
+ // These warnings should not explore macro definitions at all
+ AttrDefId::MacroId(_) => None,
+ AttrDefId::AdtId(aid) => match aid {
+ AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()),
+ AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()),
+ // Unions aren't yet supported
+ AdtId::UnionId(_) => None,
+ },
+ AttrDefId::FieldId(_) => None,
+ AttrDefId::EnumVariantId(_) => None,
+ AttrDefId::TypeAliasId(_) => None,
+ AttrDefId::GenericParamId(_) => None,
+ }
+ .map_or(false, |file_id| {
+ file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())
+ })
+ };
- is_allowed(id)
- // go upwards one step or give up
- || match id {
- AttrDefId::ModuleId(m) => m.containing_module(self.db.upcast()).map(|v| v.into()),
- AttrDefId::FunctionId(f) => Some(f.lookup(self.db.upcast()).container.into()),
- AttrDefId::StaticId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ConstId(cid) => Some(cid.lookup(self.db.upcast()).container.into()),
- AttrDefId::TraitId(tid) => Some(tid.lookup(self.db.upcast()).container.into()),
- AttrDefId::TraitAliasId(taid) => Some(taid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ImplId(iid) => Some(iid.lookup(self.db.upcast()).container.into()),
- AttrDefId::ExternBlockId(id) => Some(id.lookup(self.db.upcast()).container.into()),
- AttrDefId::ExternCrateId(id) => Some(id.lookup(self.db.upcast()).container.into()),
- AttrDefId::UseId(id) => Some(id.lookup(self.db.upcast()).container.into()),
+ let parent = || {
+ match id {
+ AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()),
+ AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()),
+ AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()),
+ AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()),
+ AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()),
+ AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()),
+ AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()),
+ AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()),
+ AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()),
+ AttrDefId::UseId(id) => Some(id.lookup(db).container.into()),
// These warnings should not explore macro definitions at all
AttrDefId::MacroId(_) => None,
AttrDefId::AdtId(aid) => match aid {
- AdtId::StructId(sid) => Some(sid.lookup(self.db.upcast()).container.into()),
- AdtId::EnumId(eid) => Some(eid.lookup(self.db.upcast()).container.into()),
+ AdtId::StructId(sid) => Some(sid.lookup(db).container.into()),
+ AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()),
// Unions aren't yet supported
AdtId::UnionId(_) => None,
},
@@ -191,6 +222,12 @@ impl<'a> DeclValidator<'a> {
AttrDefId::GenericParamId(_) => None,
}
.is_some_and(|mid| self.allowed(mid, allow_name, true))
+ };
+ is_allowed(id)
+ // FIXME: this is a hack to avoid false positives in derive macros currently
+ || file_id_is_derive()
+ // go upwards one step or give up
+ || parent()
}
fn validate_func(&mut self, func: FunctionId) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 9f9a56ffa..cbca0e801 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -75,7 +75,7 @@ fn walk_unsafe(
Expr::Path(path) => {
let resolver = resolver_for_expr(db.upcast(), def, current);
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
- if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial {
+ if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
if db.static_data(id).mutable {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 1b4ee4613..f6d6b00d7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1809,6 +1809,25 @@ impl HirDisplay for Path {
}
}
+ // Convert trait's `Self` bound back to the surface syntax. Note there is no associated
+ // trait, so there can only be one path segment that `has_self_type`. The `Self` type
+ // itself can contain further qualified path through, which will be handled by recursive
+ // `hir_fmt`s.
+ //
+ // `trait_mod::Trait<Self = type_mod::Type, Args>::Assoc`
+ // =>
+ // `<type_mod::Type as trait_mod::Trait<Args>>::Assoc`
+ let trait_self_ty = self.segments().iter().find_map(|seg| {
+ let generic_args = seg.args_and_bindings?;
+ generic_args.has_self_type.then(|| &generic_args.args[0])
+ });
+ if let Some(ty) = trait_self_ty {
+ write!(f, "<")?;
+ ty.hir_fmt(f)?;
+ write!(f, " as ")?;
+ // Now format the path of the trait...
+ }
+
for (seg_idx, segment) in self.segments().iter().enumerate() {
if !matches!(self.kind(), PathKind::Plain) || seg_idx > 0 {
write!(f, "::")?;
@@ -1840,15 +1859,12 @@ impl HirDisplay for Path {
return Ok(());
}
- write!(f, "<")?;
let mut first = true;
- for arg in generic_args.args.iter() {
+ // Skip the `Self` bound if exists. It's handled outside the loop.
+ for arg in &generic_args.args[generic_args.has_self_type as usize..] {
if first {
first = false;
- if generic_args.has_self_type {
- // FIXME: Convert to `<Ty as Trait>` form.
- write!(f, "Self = ")?;
- }
+ write!(f, "<")?;
} else {
write!(f, ", ")?;
}
@@ -1857,6 +1873,7 @@ impl HirDisplay for Path {
for binding in generic_args.bindings.iter() {
if first {
first = false;
+ write!(f, "<")?;
} else {
write!(f, ", ")?;
}
@@ -1872,9 +1889,20 @@ impl HirDisplay for Path {
}
}
}
- write!(f, ">")?;
+
+ // There may be no generic arguments to print, in case of a trait having only a
+ // single `Self` bound which is converted to `<Ty as Trait>::Assoc`.
+ if !first {
+ write!(f, ">")?;
+ }
+
+ // Current position: `<Ty as Trait<Args>|`
+ if generic_args.has_self_type {
+ write!(f, ">")?;
+ }
}
}
+
Ok(())
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index b4915dbf0..78d3c667a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -194,7 +194,8 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum InferenceDiagnostic {
NoSuchField {
- expr: ExprId,
+ field: ExprOrPatId,
+ private: bool,
},
PrivateField {
expr: ExprId,
@@ -228,6 +229,11 @@ pub enum InferenceDiagnostic {
expected: usize,
found: usize,
},
+ MismatchedTupleStructPatArgCount {
+ pat: ExprOrPatId,
+ expected: usize,
+ found: usize,
+ },
ExpectedFunction {
call_expr: ExprId,
found: Ty,
@@ -1017,7 +1023,7 @@ impl<'a> InferenceContext<'a> {
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into());
let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path) {
- Some(ResolveValueResult::ValueNs(value)) => match value {
+ Some(ResolveValueResult::ValueNs(value, _)) => match value {
ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true);
let ty = self.db.ty(var.parent.into());
@@ -1033,12 +1039,14 @@ impl<'a> InferenceContext<'a> {
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => return (self.err_ty(), None),
},
- Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)),
+ Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
+ (typens, Some(unresolved))
+ }
None => return (self.err_ty(), None),
}
} else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
- Some(it) => it,
+ Some((it, idx, _)) => (it, idx),
None => return (self.err_ty(), None),
}
};
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index 9e1c74b16..a116d4447 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -39,8 +39,14 @@ impl CastCheck {
}
fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool {
- let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { return false; };
- let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { return false; };
- let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { return false; };
+ let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else {
+ return false;
+ };
+ let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else {
+ return false;
+ };
+ let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else {
+ return false;
+ };
table.coerce(expr_elt_ty, cast_inner_ty).is_ok()
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 1781f6c58..13d6b5643 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -322,7 +322,7 @@ impl InferenceContext<'_> {
Expr::Path(p) => {
let resolver = resolver_for_expr(self.db.upcast(), self.owner, tgt_expr);
if let Some(r) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) {
- if let ResolveValueResult::ValueNs(v) = r {
+ if let ResolveValueResult::ValueNs(v, _) = r {
if let ValueNs::LocalBinding(b) = v {
return Some(HirPlace { local: b, projections: vec![] });
}
@@ -452,6 +452,8 @@ impl InferenceContext<'_> {
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
+ Expr::OffsetOf(_) => (),
+ Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);
self.consume_expr(*then_branch);
@@ -467,13 +469,13 @@ impl InferenceContext<'_> {
Statement::Let { pat, type_ref: _, initializer, else_branch } => {
if let Some(else_branch) = else_branch {
self.consume_expr(*else_branch);
- if let Some(initializer) = initializer {
- self.consume_expr(*initializer);
- }
- return;
}
if let Some(initializer) = initializer {
- self.walk_expr(*initializer);
+ if 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);
}
@@ -620,6 +622,7 @@ impl InferenceContext<'_> {
| Expr::Tuple { exprs, is_assignee_expr: _ } => {
self.consume_exprs(exprs.iter().copied())
}
+
Expr::Missing
| Expr::Continue { .. }
| Expr::Path(_)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 8cbdae625..0c3c725a7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -5,9 +5,7 @@ use std::{
mem,
};
-use chalk_ir::{
- cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
-};
+use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKind};
use hir_def::{
generics::TypeOrConstParamData,
hir::{
@@ -516,9 +514,6 @@ impl InferenceContext<'_> {
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
- if let Some(variant) = def_id {
- self.write_variant_resolution(tgt_expr.into(), variant);
- }
if let Some(t) = expected.only_has_type(&mut self.table) {
self.unify(&ty, &t);
@@ -528,26 +523,56 @@ impl InferenceContext<'_> {
.as_adt()
.map(|(_, s)| s.clone())
.unwrap_or_else(|| Substitution::empty(Interner));
- let field_types = def_id.map(|it| self.db.field_types(it)).unwrap_or_default();
- let variant_data = def_id.map(|it| it.variant_data(self.db.upcast()));
- for field in fields.iter() {
- let field_def =
- variant_data.as_ref().and_then(|it| match it.field(&field.name) {
- Some(local_id) => Some(FieldId { parent: def_id.unwrap(), local_id }),
- None => {
- self.push_diagnostic(InferenceDiagnostic::NoSuchField {
- expr: field.expr,
- });
- None
- }
- });
- let field_ty = field_def.map_or(self.err_ty(), |it| {
- field_types[it.local_id].clone().substitute(Interner, &substs)
- });
- // Field type might have some unknown types
- // FIXME: we may want to emit a single type variable for all instance of type fields?
- let field_ty = self.insert_type_vars(field_ty);
- self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
+ if let Some(variant) = def_id {
+ self.write_variant_resolution(tgt_expr.into(), variant);
+ }
+ match def_id {
+ _ if fields.is_empty() => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
+ for field in fields.iter() {
+ let field_def = {
+ match variant_data.field(&field.name) {
+ Some(local_id) => {
+ if !visibilities[local_id].is_visible_from(
+ self.db.upcast(),
+ self.resolver.module(),
+ ) {
+ self.push_diagnostic(
+ InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: true,
+ },
+ );
+ }
+ Some(local_id)
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: field.expr.into(),
+ private: false,
+ });
+ None
+ }
+ }
+ };
+ let field_ty = field_def.map_or(self.err_ty(), |it| {
+ field_types[it].clone().substitute(Interner, &substs)
+ });
+
+ // Field type might have some unknown types
+ // FIXME: we may want to emit a single type variable for all instance of type fields?
+ let field_ty = self.insert_type_vars(field_ty);
+ self.infer_expr_coerce(field.expr, &Expectation::has_type(field_ty));
+ }
+ }
+ None => {
+ for field in fields.iter() {
+ self.infer_expr_coerce(field.expr, &Expectation::None);
+ }
+ }
}
if let Some(expr) = spread {
self.infer_expr(*expr, &Expectation::has_type(ty.clone()));
@@ -750,7 +775,7 @@ impl InferenceContext<'_> {
self.resolve_associated_type_with_params(
self_ty,
self.resolve_ops_index_output(),
- &[GenericArgData::Ty(index_ty).intern(Interner)],
+ &[index_ty.cast(Interner)],
)
} else {
self.err_ty()
@@ -845,6 +870,11 @@ impl InferenceContext<'_> {
});
expected
}
+ Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
+ Expr::InlineAsm(it) => {
+ self.infer_expr_no_expect(it.e);
+ self.result.standard_types.unit.clone()
+ }
};
// use a new type variable if we got unknown here
let ty = self.insert_type_vars_shallow(ty);
@@ -1124,7 +1154,7 @@ impl InferenceContext<'_> {
Expr::Underscore => rhs_ty.clone(),
_ => {
// `lhs` is a place expression, a unit struct, or an enum variant.
- let lhs_ty = self.infer_expr(lhs, &Expectation::none());
+ let lhs_ty = self.infer_expr_inner(lhs, &Expectation::none());
// This is the only branch where this function may coerce any type.
// We are returning early to avoid the unifiability check below.
@@ -1721,16 +1751,13 @@ impl InferenceContext<'_> {
for (id, data) in def_generics.iter().skip(substs.len()) {
match data {
TypeOrConstParamData::TypeParamData(_) => {
- substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner))
- }
- TypeOrConstParamData::ConstParamData(_) => {
- substs.push(
- GenericArgData::Const(self.table.new_const_var(
- self.db.const_param_ty(ConstParamId::from_unchecked(id)),
- ))
- .intern(Interner),
- )
+ substs.push(self.table.new_type_var().cast(Interner))
}
+ TypeOrConstParamData::ConstParamData(_) => substs.push(
+ self.table
+ .new_const_var(self.db.const_param_ty(ConstParamId::from_unchecked(id)))
+ .cast(Interner),
+ ),
}
}
assert_eq!(substs.len(), total_len);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 396ca0044..b8a1af96f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -35,6 +35,8 @@ impl InferenceContext<'_> {
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
match &self.body[tgt_expr] {
Expr::Missing => (),
+ Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
+ Expr::OffsetOf(_) => (),
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
self.infer_mut_expr(then_branch, Mutability::Not);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 5da0ab76b..4e28ec060 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -15,7 +15,8 @@ use crate::{
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
lower::lower_to_chalk_mutability,
primitive::UintTy,
- static_lifetime, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt, TyKind,
+ static_lifetime, InferenceDiagnostic, Interner, Scalar, Substitution, Ty, TyBuilder, TyExt,
+ TyKind,
};
/// Used to generalize patterns and assignee expressions.
@@ -74,29 +75,68 @@ impl InferenceContext<'_> {
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
}
+ if let Some(var) = &var_data {
+ let cmp = if ellipsis.is_some() { usize::gt } else { usize::ne };
+
+ if cmp(&subs.len(), &var.fields().len()) {
+ self.push_diagnostic(InferenceDiagnostic::MismatchedTupleStructPatArgCount {
+ pat: id.into(),
+ expected: var.fields().len(),
+ found: subs.len(),
+ });
+ }
+ }
+
self.unify(&ty, expected);
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
- let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
- let (pre, post) = match ellipsis {
- Some(idx) => subs.split_at(idx),
- None => (subs, &[][..]),
- };
- let post_idx_offset = field_tys.iter().count().saturating_sub(post.len());
-
- let pre_iter = pre.iter().enumerate();
- let post_iter = (post_idx_offset..).zip(post.iter());
- for (i, &subpat) in pre_iter.chain(post_iter) {
- let expected_ty = var_data
- .as_ref()
- .and_then(|d| d.field(&Name::new_tuple_field(i)))
- .map_or(self.err_ty(), |field| {
- field_tys[field].clone().substitute(Interner, &substs)
- });
- let expected_ty = self.normalize_associated_types_in(expected_ty);
- T::infer(self, subpat, &expected_ty, default_bm);
+ match def {
+ _ if subs.len() == 0 => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
+
+ let (pre, post) = match ellipsis {
+ Some(idx) => subs.split_at(idx),
+ None => (subs, &[][..]),
+ };
+ let post_idx_offset = field_types.iter().count().saturating_sub(post.len());
+
+ let pre_iter = pre.iter().enumerate();
+ let post_iter = (post_idx_offset..).zip(post.iter());
+
+ for (i, &subpat) in pre_iter.chain(post_iter) {
+ let field_def = {
+ match variant_data.field(&Name::new_tuple_field(i)) {
+ Some(local_id) => {
+ if !visibilities[local_id]
+ .is_visible_from(self.db.upcast(), self.resolver.module())
+ {
+ // FIXME(DIAGNOSE): private tuple field
+ }
+ Some(local_id)
+ }
+ None => None,
+ }
+ };
+
+ let expected_ty = field_def.map_or(self.err_ty(), |f| {
+ field_types[f].clone().substitute(Interner, &substs)
+ });
+ let expected_ty = self.normalize_associated_types_in(expected_ty);
+
+ T::infer(self, subpat, &expected_ty, default_bm);
+ }
+ }
+ None => {
+ let err_ty = self.err_ty();
+ for &inner in subs {
+ T::infer(self, inner, &err_ty, default_bm);
+ }
+ }
}
ty
@@ -109,7 +149,7 @@ impl InferenceContext<'_> {
expected: &Ty,
default_bm: T::BindingMode,
id: T,
- subs: impl Iterator<Item = (Name, T)>,
+ subs: impl Iterator<Item = (Name, T)> + ExactSizeIterator,
) -> Ty {
let (ty, def) = self.resolve_variant(path, false);
if let Some(variant) = def {
@@ -121,17 +161,51 @@ impl InferenceContext<'_> {
let substs =
ty.as_adt().map(|(_, s)| s.clone()).unwrap_or_else(|| Substitution::empty(Interner));
- let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default();
- let var_data = def.map(|it| it.variant_data(self.db.upcast()));
+ match def {
+ _ if subs.len() == 0 => {}
+ Some(def) => {
+ let field_types = self.db.field_types(def);
+ let variant_data = def.variant_data(self.db.upcast());
+ let visibilities = self.db.field_visibilities(def);
+
+ for (name, inner) in subs {
+ let field_def = {
+ match variant_data.field(&name) {
+ Some(local_id) => {
+ if !visibilities[local_id]
+ .is_visible_from(self.db.upcast(), self.resolver.module())
+ {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: inner.into(),
+ private: true,
+ });
+ }
+ Some(local_id)
+ }
+ None => {
+ self.push_diagnostic(InferenceDiagnostic::NoSuchField {
+ field: inner.into(),
+ private: false,
+ });
+ None
+ }
+ }
+ };
- for (name, inner) in subs {
- let expected_ty = var_data
- .as_ref()
- .and_then(|it| it.field(&name))
- .map_or(self.err_ty(), |f| field_tys[f].clone().substitute(Interner, &substs));
- let expected_ty = self.normalize_associated_types_in(expected_ty);
+ let expected_ty = field_def.map_or(self.err_ty(), |f| {
+ field_types[f].clone().substitute(Interner, &substs)
+ });
+ let expected_ty = self.normalize_associated_types_in(expected_ty);
- T::infer(self, inner, &expected_ty, default_bm);
+ T::infer(self, inner, &expected_ty, default_bm);
+ }
+ }
+ None => {
+ let err_ty = self.err_ty();
+ for (_, inner) in subs {
+ T::infer(self, inner, &err_ty, default_bm);
+ }
+ }
}
ty
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
index 79d9e21e7..c6bbf2f61 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
@@ -61,8 +61,8 @@ impl InferenceContext<'_> {
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial {
- ResolveValueResult::ValueNs(it) => (it, None),
- ResolveValueResult::Partial(def, remaining_index) => self
+ ResolveValueResult::ValueNs(it, _) => (it, None),
+ ResolveValueResult::Partial(def, remaining_index, _) => self
.resolve_assoc_item(def, path, remaining_index, id)
.map(|(it, substs)| (it, Some(substs)))?,
}
@@ -178,13 +178,30 @@ impl InferenceContext<'_> {
remaining_index: usize,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- assert!(remaining_index < path.segments().len());
// there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref.
- let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
- let remaining_segments = path.segments().skip(remaining_index);
+ let _d;
+ let (resolved_segment, remaining_segments) = match path {
+ Path::Normal { .. } => {
+ assert!(remaining_index < path.segments().len());
+ (
+ path.segments().get(remaining_index - 1).unwrap(),
+ path.segments().skip(remaining_index),
+ )
+ }
+ Path::LangItem(..) => (
+ PathSegment {
+ name: {
+ _d = hir_expand::name::known::Unknown;
+ &_d
+ },
+ args_and_bindings: None,
+ },
+ path.segments(),
+ ),
+ };
let is_before_last = remaining_segments.len() == 1;
match (def, is_before_last) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index 0fb71135b..0a68a9f3b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -10,7 +10,6 @@ use chalk_solve::infer::ParameterEnaVariableExt;
use either::Either;
use ena::unify::UnifyKey;
use hir_expand::name;
-use stdx::never;
use triomphe::Arc;
use super::{InferOk, InferResult, InferenceContext, TypeError};
@@ -92,15 +91,10 @@ pub(crate) fn unify(
let vars = Substitution::from_iter(
Interner,
tys.binders.iter(Interner).map(|it| match &it.kind {
- chalk_ir::VariableKind::Ty(_) => {
- GenericArgData::Ty(table.new_type_var()).intern(Interner)
- }
- chalk_ir::VariableKind::Lifetime => {
- GenericArgData::Ty(table.new_type_var()).intern(Interner)
- } // FIXME: maybe wrong?
- chalk_ir::VariableKind::Const(ty) => {
- GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
- }
+ chalk_ir::VariableKind::Ty(_) => table.new_type_var().cast(Interner),
+ // FIXME: maybe wrong?
+ chalk_ir::VariableKind::Lifetime => table.new_type_var().cast(Interner),
+ chalk_ir::VariableKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
}),
);
let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner);
@@ -111,10 +105,10 @@ pub(crate) fn unify(
// default any type vars that weren't unified back to their original bound vars
// (kind of hacky)
let find_var = |iv| {
- vars.iter(Interner).position(|v| match v.interned() {
- chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
- chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
- chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+ vars.iter(Interner).position(|v| match v.data(Interner) {
+ GenericArgData::Ty(ty) => ty.inference_var(Interner),
+ GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+ GenericArgData::Const(c) => c.inference_var(Interner),
} == Some(iv))
};
let fallback = |iv, kind, default, binder| match kind {
@@ -149,6 +143,9 @@ pub(crate) struct InferenceTable<'a> {
var_unification_table: ChalkInferenceTable,
type_variable_table: Vec<TypeVariableFlags>,
pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
+ /// Double buffer used in [`Self::resolve_obligations_as_possible`] to cut down on
+ /// temporary allocations.
+ resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>,
}
pub(crate) struct InferenceTableSnapshot {
@@ -165,6 +162,7 @@ impl<'a> InferenceTable<'a> {
var_unification_table: ChalkInferenceTable::new(),
type_variable_table: Vec::new(),
pending_obligations: Vec::new(),
+ resolve_obligations_buffer: Vec::new(),
}
}
@@ -516,10 +514,10 @@ impl<'a> InferenceTable<'a> {
pub(crate) fn resolve_obligations_as_possible(&mut self) {
let _span = profile::span("resolve_obligations_as_possible");
let mut changed = true;
- let mut obligations = Vec::new();
- while changed {
- changed = false;
+ let mut obligations = mem::take(&mut self.resolve_obligations_buffer);
+ while mem::take(&mut changed) {
mem::swap(&mut self.pending_obligations, &mut obligations);
+
for canonicalized in obligations.drain(..) {
if !self.check_changed(&canonicalized) {
self.pending_obligations.push(canonicalized);
@@ -534,6 +532,8 @@ impl<'a> InferenceTable<'a> {
self.register_obligation_in_env(uncanonical);
}
}
+ self.resolve_obligations_buffer = obligations;
+ self.resolve_obligations_buffer.clear();
}
pub(crate) fn fudge_inference<T: TypeFoldable<Interner>>(
@@ -611,9 +611,9 @@ impl<'a> InferenceTable<'a> {
fn check_changed(&mut self, canonicalized: &Canonicalized<InEnvironment<Goal>>) -> bool {
canonicalized.free_vars.iter().any(|var| {
let iv = match var.data(Interner) {
- chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner),
- chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
- chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner),
+ GenericArgData::Ty(ty) => ty.inference_var(Interner),
+ GenericArgData::Lifetime(lt) => lt.inference_var(Interner),
+ GenericArgData::Const(c) => c.inference_var(Interner),
}
.expect("free var is not inference var");
if self.var_unification_table.probe_var(iv).is_some() {
@@ -690,14 +690,10 @@ impl<'a> InferenceTable<'a> {
.fill(|it| {
let arg = match it {
ParamKind::Type => self.new_type_var(),
- ParamKind::Const(ty) => {
- never!("Tuple with const parameter");
- return GenericArgData::Const(self.new_const_var(ty.clone()))
- .intern(Interner);
- }
+ ParamKind::Const(_) => unreachable!("Tuple with const parameter"),
};
arg_tys.push(arg.clone());
- GenericArgData::Ty(arg).intern(Interner)
+ arg.cast(Interner)
})
.build();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index b15339d44..1a6106c02 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -24,7 +24,7 @@ pub use self::{
macro_rules! user_error {
($it: expr) => {
- return Err(LayoutError::UserError(format!($it)))
+ return Err(LayoutError::UserError(format!($it).into()))
};
}
@@ -50,7 +50,7 @@ pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutError {
- UserError(String),
+ UserError(Box<str>),
SizeOverflow,
TargetLayoutNotAvailable,
HasPlaceholder,
@@ -109,7 +109,8 @@ fn layout_of_simd_ty(
// * the homogeneous field type and the number of fields.
let (e_ty, e_len, is_array) = if let TyKind::Array(e_ty, _) = f0_ty.kind(Interner) {
// Extract the number of elements from the layout of the array field:
- let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else {
+ let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields
+ else {
user_error!("Array with non array layout");
};
@@ -233,9 +234,9 @@ pub fn layout_of_ty_query(
cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)?
}
TyKind::Array(element, count) => {
- let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(
- "unevaluated or mistyped const generic parameter".to_string(),
- ))? as u64;
+ let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from(
+ "unevaluated or mistyped const generic parameter",
+ )))? as u64;
let element = db.layout_of_ty(element.clone(), trait_env.clone())?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
index 1c92e80f3..85ef649b8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs
@@ -163,7 +163,7 @@ fn repr_discr(
return Err(LayoutError::UserError(
"Integer::repr_discr: `#[repr]` hint too small for \
discriminant range of enum "
- .to_string(),
+ .into(),
));
}
return Ok((discr, ity.is_signed()));
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index 333ad473a..ffdbb9de9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -212,14 +212,14 @@ fn recursive() {
}
check_fail(
r#"struct Goal(Goal);"#,
- LayoutError::UserError("infinite sized recursive type".to_string()),
+ LayoutError::UserError("infinite sized recursive type".into()),
);
check_fail(
r#"
struct Foo<T>(Foo<T>);
struct Goal(Foo<i32>);
"#,
- LayoutError::UserError("infinite sized recursive type".to_string()),
+ LayoutError::UserError("infinite sized recursive type".into()),
);
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
index 576e7f3fc..bbe855a14 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs
@@ -255,3 +255,17 @@ fn ellipsis_pattern() {
}
}
}
+
+#[test]
+fn regression_15623() {
+ size_and_align_expr! {
+ let a = 2;
+ let b = 3;
+ let c = 5;
+ move || {
+ let 0 = a else { return b; };
+ let y = c;
+ y
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index b3ca2a222..405bb001b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -52,12 +52,14 @@ use hir_expand::name;
use la_arena::{Arena, Idx};
use mir::{MirEvalError, VTableMap};
use rustc_hash::FxHashSet;
+use syntax::ast::{make, ConstArg};
use traits::FnTrait;
use triomphe::Arc;
use utils::Generics;
use crate::{
- consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics,
+ consteval::unknown_const, db::HirDatabase, display::HirDisplay, infer::unify::InferenceTable,
+ utils::generics,
};
pub use autoderef::autoderef;
@@ -719,3 +721,16 @@ where
value.visit_with(&mut collector, DebruijnIndex::INNERMOST);
collector.placeholders.into_iter().collect()
}
+
+pub fn known_const_to_ast(konst: &Const, db: &dyn HirDatabase) -> Option<ConstArg> {
+ if let ConstValue::Concrete(c) = &konst.interned().value {
+ match c.interned {
+ ConstScalar::UnevaluatedConst(GeneralConstId::InTypeConstId(cid), _) => {
+ return Some(cid.source(db.upcast()));
+ }
+ ConstScalar::Unknown => return None,
+ _ => (),
+ }
+ }
+ Some(make::expr_const_value(konst.display(db).to_string().as_str()))
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 2837f400b..9a61f1535 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -58,10 +58,9 @@ use crate::{
InTypeConstIdMetadata,
},
AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
- FnPointer, FnSig, FnSubst, GenericArgData, ImplTraitId, Interner, ParamKind, PolyFnSig,
- ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait,
- ReturnTypeImplTraits, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
- TyKind, WhereClause,
+ FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy,
+ QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits,
+ Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
};
#[derive(Debug)]
@@ -213,6 +212,19 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_ext(type_ref).0
}
+ pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const {
+ const_or_path_to_chalk(
+ self.db,
+ self.resolver,
+ self.owner,
+ const_type,
+ const_ref,
+ self.type_param_mode,
+ || self.generics(),
+ self.in_binders,
+ )
+ }
+
fn generics(&self) -> Generics {
generics(
self.db.upcast(),
@@ -242,17 +254,7 @@ impl<'a> TyLoweringContext<'a> {
}
TypeRef::Array(inner, len) => {
let inner_ty = self.lower_ty(inner);
- let const_len = const_or_path_to_chalk(
- self.db,
- self.resolver,
- self.owner,
- TyBuilder::usize(),
- len,
- self.type_param_mode,
- || self.generics(),
- self.in_binders,
- );
-
+ let const_len = self.lower_const(len, TyBuilder::usize());
TyKind::Array(inner_ty, const_len).intern(Interner)
}
TypeRef::Slice(inner) => {
@@ -391,11 +393,9 @@ impl<'a> TyLoweringContext<'a> {
let ty = {
let macro_call = macro_call.to_node(self.db.upcast());
let resolver = |path| {
- self.resolver.resolve_path_as_macro(
- self.db.upcast(),
- &path,
- Some(MacroSubNs::Bang),
- )
+ self.resolver
+ .resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
+ .map(|(it, _)| it)
};
match expander.enter_expand::<ast::Type>(self.db.upcast(), macro_call, resolver)
{
@@ -447,7 +447,7 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
- Some((it, None)) => it,
+ Some((it, None, _)) => it,
_ => return None,
};
match resolution {
@@ -627,7 +627,7 @@ impl<'a> TyLoweringContext<'a> {
return self.lower_ty_relative_path(ty, res, path.segments());
}
- let (resolution, remaining_index) =
+ let (resolution, remaining_index, _) =
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
@@ -847,18 +847,7 @@ impl<'a> TyLoweringContext<'a> {
arg,
&mut (),
|_, type_ref| self.lower_ty(type_ref),
- |_, c, ty| {
- const_or_path_to_chalk(
- self.db,
- self.resolver,
- self.owner,
- ty,
- c,
- self.type_param_mode,
- || self.generics(),
- self.in_binders,
- )
- },
+ |_, const_ref, ty| self.lower_const(const_ref, ty),
) {
had_explicit_args = true;
substs.push(x);
@@ -1604,24 +1593,35 @@ pub(crate) fn generic_defaults_query(
.iter()
.enumerate()
.map(|(idx, (id, p))| {
- let p = match p {
- TypeOrConstParamData::TypeParamData(p) => p,
- TypeOrConstParamData::ConstParamData(_) => {
- // FIXME: implement const generic defaults
- let val = unknown_const_as_generic(
- db.const_param_ty(ConstParamId::from_unchecked(id)),
+ match p {
+ TypeOrConstParamData::TypeParamData(p) => {
+ let mut ty = p
+ .default
+ .as_ref()
+ .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
+ // Each default can only refer to previous parameters.
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
+ }
+ TypeOrConstParamData::ConstParamData(p) => {
+ let mut val = p.default.as_ref().map_or_else(
+ || {
+ unknown_const_as_generic(
+ db.const_param_ty(ConstParamId::from_unchecked(id)),
+ )
+ },
+ |c| {
+ let c = ctx.lower_const(c, ctx.lower_ty(&p.ty));
+ c.cast(Interner)
+ },
);
- return make_binders(db, &generic_params, val);
+ // Each default can only refer to previous parameters, see above.
+ val = fallback_bound_vars(val, idx, parent_start_idx);
+ make_binders(db, &generic_params, val)
}
- };
- let mut ty =
- p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
-
- // Each default can only refer to previous parameters.
- // Type variable default referring to parameter coming
- // after it is forbidden (FIXME: report diagnostic)
- ty = fallback_bound_vars(ty, idx, parent_start_idx);
- crate::make_binders(db, &generic_params, ty.cast(Interner))
+ }
})
// FIXME: use `Arc::from_iter` when it becomes available
.collect::<Vec<_>>(),
@@ -1643,9 +1643,7 @@ pub(crate) fn generic_defaults_recover(
.iter_id()
.map(|id| {
let val = match id {
- Either::Left(_) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner),
Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
};
crate::make_binders(db, &generic_params, val)
@@ -1991,16 +1989,9 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
}
};
Some(match (arg, kind) {
- (GenericArg::Type(type_ref), ParamKind::Type) => {
- let ty = for_type(this, type_ref);
- GenericArgData::Ty(ty).intern(Interner)
- }
- (GenericArg::Const(c), ParamKind::Const(c_ty)) => {
- GenericArgData::Const(for_const(this, c, c_ty)).intern(Interner)
- }
- (GenericArg::Const(_), ParamKind::Type) => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ (GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, type_ref).cast(Interner),
+ (GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
+ (GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
(GenericArg::Type(t), ParamKind::Const(c_ty)) => {
// We want to recover simple idents, which parser detects them
// as types. Maybe here is not the best place to do it, but
@@ -2010,9 +2001,7 @@ pub(crate) fn generic_arg_to_chalk<'a, T>(
if p.kind == PathKind::Plain {
if let [n] = p.segments() {
let c = ConstRef::Path(n.clone());
- return Some(
- GenericArgData::Const(for_const(this, &c, c_ty)).intern(Interner),
- );
+ return Some(for_const(this, &c, c_ty).cast(Interner));
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 4723c25ed..e953058cc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -1,6 +1,6 @@
//! MIR definitions and implementation
-use std::{fmt::Display, iter};
+use std::{collections::hash_map::Entry, fmt::Display, iter};
use crate::{
consteval::usize_const,
@@ -37,6 +37,7 @@ pub use monomorphization::{
monomorphize_mir_body_bad, monomorphized_mir_body_for_closure_query,
monomorphized_mir_body_query, monomorphized_mir_body_recover,
};
+use rustc_hash::FxHashMap;
use smallvec::{smallvec, SmallVec};
use stdx::{impl_from, never};
use triomphe::Arc;
@@ -165,8 +166,8 @@ impl<V, T> ProjectionElem<V, T> {
TyKind::Adt(_, subst) => {
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
}
- _ => {
- never!("Only adt has field");
+ ty => {
+ never!("Only adt has field, found {:?}", ty);
return TyKind::Error.intern(Interner);
}
},
@@ -223,35 +224,93 @@ impl<V, T> ProjectionElem<V, T> {
type PlaceElem = ProjectionElem<LocalId, Ty>;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct ProjectionId(u32);
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ProjectionStore {
+ id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
+ proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
+}
+
+impl Default for ProjectionStore {
+ fn default() -> Self {
+ let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
+ // Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
+ this.intern(Box::new([]));
+ this
+ }
+}
+
+impl ProjectionStore {
+ fn shrink_to_fit(&mut self) {
+ self.id_to_proj.shrink_to_fit();
+ self.proj_to_id.shrink_to_fit();
+ }
+
+ fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
+ self.proj_to_id.get(projection).copied()
+ }
+
+ fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
+ let new_id = ProjectionId(self.proj_to_id.len() as u32);
+ match self.proj_to_id.entry(projection) {
+ Entry::Occupied(id) => *id.get(),
+ Entry::Vacant(e) => {
+ let key_clone = e.key().clone();
+ e.insert(new_id);
+ self.id_to_proj.insert(new_id, key_clone);
+ new_id
+ }
+ }
+ }
+}
+
+impl ProjectionId {
+ const EMPTY: ProjectionId = ProjectionId(0);
+
+ fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
+ store.id_to_proj.get(&self).unwrap()
+ }
+
+ fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
+ let mut current = self.lookup(store).to_vec();
+ current.push(projection);
+ store.intern(current.into())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Place {
pub local: LocalId,
- pub projection: Box<[PlaceElem]>,
+ pub projection: ProjectionId,
}
impl Place {
- fn is_parent(&self, child: &Place) -> bool {
- self.local == child.local && child.projection.starts_with(&self.projection)
+ fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
+ self.local == child.local
+ && child.projection.lookup(store).starts_with(&self.projection.lookup(store))
}
/// The place itself is not included
- fn iterate_over_parents(&self) -> impl Iterator<Item = Place> + '_ {
- (0..self.projection.len())
- .map(|x| &self.projection[0..x])
- .map(|x| Place { local: self.local, projection: x.to_vec().into() })
+ fn iterate_over_parents<'a>(
+ &'a self,
+ store: &'a ProjectionStore,
+ ) -> impl Iterator<Item = Place> + 'a {
+ let projection = self.projection.lookup(store);
+ (0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
+ Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
+ })
}
- fn project(&self, projection: PlaceElem) -> Place {
- Place {
- local: self.local,
- projection: self.projection.iter().cloned().chain([projection]).collect(),
- }
+ fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
+ Place { local: self.local, projection: self.projection.project(projection, store) }
}
}
impl From<LocalId> for Place {
fn from(local: LocalId) -> Self {
- Self { local, projection: vec![].into() }
+ Self { local, projection: ProjectionId::EMPTY }
}
}
@@ -368,7 +427,7 @@ pub enum TerminatorKind {
///
/// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after
/// deaggregation runs.
- Resume,
+ UnwindResume,
/// Indicates that the landing pad is finished and that the process should abort.
///
@@ -997,6 +1056,7 @@ pub struct BasicBlock {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MirBody {
+ pub projection_store: ProjectionStore,
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
@@ -1009,11 +1069,15 @@ pub struct MirBody {
}
impl MirBody {
- fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) {
- fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) {
+ fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
+ fn for_operand(
+ op: &mut Operand,
+ f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
+ store: &mut ProjectionStore,
+ ) {
match op {
Operand::Copy(p) | Operand::Move(p) => {
- f(p);
+ f(p, store);
}
Operand::Constant(_) | Operand::Static(_) => (),
}
@@ -1022,30 +1086,30 @@ impl MirBody {
for statement in &mut block.statements {
match &mut statement.kind {
StatementKind::Assign(p, r) => {
- f(p);
+ f(p, &mut self.projection_store);
match r {
Rvalue::ShallowInitBoxWithAlloc(_) => (),
Rvalue::ShallowInitBox(o, _)
| Rvalue::UnaryOp(_, o)
| Rvalue::Cast(_, o, _)
| Rvalue::Repeat(o, _)
- | Rvalue::Use(o) => for_operand(o, &mut f),
+ | Rvalue::Use(o) => for_operand(o, &mut f, &mut self.projection_store),
Rvalue::CopyForDeref(p)
| Rvalue::Discriminant(p)
| Rvalue::Len(p)
- | Rvalue::Ref(_, p) => f(p),
+ | Rvalue::Ref(_, p) => f(p, &mut self.projection_store),
Rvalue::CheckedBinaryOp(_, o1, o2) => {
- for_operand(o1, &mut f);
- for_operand(o2, &mut f);
+ for_operand(o1, &mut f, &mut self.projection_store);
+ for_operand(o2, &mut f, &mut self.projection_store);
}
Rvalue::Aggregate(_, ops) => {
for op in ops.iter_mut() {
- for_operand(op, &mut f);
+ for_operand(op, &mut f, &mut self.projection_store);
}
}
}
}
- StatementKind::Deinit(p) => f(p),
+ StatementKind::Deinit(p) => f(p, &mut self.projection_store),
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Nop => (),
@@ -1053,33 +1117,36 @@ impl MirBody {
}
match &mut block.terminator {
Some(x) => match &mut x.kind {
- TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, &mut f),
+ TerminatorKind::SwitchInt { discr, .. } => {
+ for_operand(discr, &mut f, &mut self.projection_store)
+ }
TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::GeneratorDrop
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable => (),
TerminatorKind::Drop { place, .. } => {
- f(place);
+ f(place, &mut self.projection_store);
}
TerminatorKind::DropAndReplace { place, value, .. } => {
- f(place);
- for_operand(value, &mut f);
+ f(place, &mut self.projection_store);
+ for_operand(value, &mut f, &mut self.projection_store);
}
TerminatorKind::Call { func, args, destination, .. } => {
- for_operand(func, &mut f);
- args.iter_mut().for_each(|x| for_operand(x, &mut f));
- f(destination);
+ for_operand(func, &mut f, &mut self.projection_store);
+ args.iter_mut()
+ .for_each(|x| for_operand(x, &mut f, &mut self.projection_store));
+ f(destination, &mut self.projection_store);
}
TerminatorKind::Assert { cond, .. } => {
- for_operand(cond, &mut f);
+ for_operand(cond, &mut f, &mut self.projection_store);
}
TerminatorKind::Yield { value, resume_arg, .. } => {
- for_operand(value, &mut f);
- f(resume_arg);
+ for_operand(value, &mut f, &mut self.projection_store);
+ f(resume_arg, &mut self.projection_store);
}
},
None => (),
@@ -1096,7 +1163,9 @@ impl MirBody {
binding_locals,
param_locals,
closures,
+ projection_store,
} = self;
+ projection_store.shrink_to_fit();
basic_blocks.shrink_to_fit();
locals.shrink_to_fit();
binding_locals.shrink_to_fit();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index ad98e8fa1..41fb12965 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -42,30 +42,27 @@ pub struct BorrowckResult {
fn all_mir_bodies(
db: &dyn HirDatabase,
def: DefWithBodyId,
-) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ mut cb: impl FnMut(Arc<MirBody>),
+) -> Result<(), MirLowerError> {
fn for_closure(
db: &dyn HirDatabase,
c: ClosureId,
- ) -> Box<dyn Iterator<Item = Result<Arc<MirBody>, MirLowerError>> + '_> {
+ cb: &mut impl FnMut(Arc<MirBody>),
+ ) -> Result<(), MirLowerError> {
match db.mir_body_for_closure(c) {
Ok(body) => {
- let closures = body.closures.clone();
- Box::new(
- iter::once(Ok(body))
- .chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
- )
+ cb(body.clone());
+ body.closures.iter().map(|&it| for_closure(db, it, cb)).collect()
}
- Err(e) => Box::new(iter::once(Err(e))),
+ Err(e) => Err(e),
}
}
match db.mir_body(def) {
Ok(body) => {
- let closures = body.closures.clone();
- Box::new(
- iter::once(Ok(body)).chain(closures.into_iter().flat_map(|it| for_closure(db, it))),
- )
+ cb(body.clone());
+ body.closures.iter().map(|&it| for_closure(db, it, &mut cb)).collect()
}
- Err(e) => Box::new(iter::once(Err(e))),
+ Err(e) => Err(e),
}
}
@@ -74,17 +71,15 @@ pub fn borrowck_query(
def: DefWithBodyId,
) -> Result<Arc<[BorrowckResult]>, MirLowerError> {
let _p = profile::span("borrowck_query");
- let r = all_mir_bodies(db, def)
- .map(|body| {
- let body = body?;
- Ok(BorrowckResult {
- mutability_of_locals: mutability_of_locals(db, &body),
- moved_out_of_ref: moved_out_of_ref(db, &body),
- mir_body: body,
- })
- })
- .collect::<Result<Vec<_>, MirLowerError>>()?;
- Ok(r.into())
+ let mut res = vec![];
+ all_mir_bodies(db, def, |body| {
+ res.push(BorrowckResult {
+ mutability_of_locals: mutability_of_locals(db, &body),
+ moved_out_of_ref: moved_out_of_ref(db, &body),
+ mir_body: body,
+ });
+ })?;
+ Ok(res.into())
}
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
@@ -93,7 +88,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
Operand::Copy(p) | Operand::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
let mut is_dereference_of_ref = false;
- for proj in &*p.projection {
+ for proj in p.projection.lookup(&body.projection_store) {
if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
is_dereference_of_ref = true;
}
@@ -125,6 +120,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
Operand::Constant(_) | Operand::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
+ db.unwind_if_cancelled();
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(_, r) => match r {
@@ -160,7 +156,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::GeneratorDrop
| TerminatorKind::Abort
| TerminatorKind::Return
@@ -183,6 +179,7 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
None => (),
}
}
+ result.shrink_to_fit();
result
}
@@ -199,7 +196,7 @@ enum ProjectionCase {
fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
let mut is_part_of = false;
let mut ty = body.locals[lvalue.local].ty.clone();
- for proj in lvalue.projection.iter() {
+ for proj in lvalue.projection.lookup(&body.projection_store).iter() {
match proj {
ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
ProjectionElem::Deref // It's direct in case of `Box<T>`
@@ -258,7 +255,7 @@ fn ever_initialized_map(
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(p, _) => {
- if p.projection.len() == 0 && p.local == l {
+ if p.projection.lookup(&body.projection_store).len() == 0 && p.local == l {
is_ever_initialized = true;
}
}
@@ -277,21 +274,37 @@ fn ever_initialized_map(
);
return;
};
- let targets = match &terminator.kind {
- TerminatorKind::Goto { target } => vec![*target],
- TerminatorKind::SwitchInt { targets, .. } => targets.all_targets().to_vec(),
- TerminatorKind::Resume
+ let mut process = |target, is_ever_initialized| {
+ if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
+ result[target].insert(l, is_ever_initialized);
+ dfs(db, body, target, l, result);
+ }
+ };
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
+ TerminatorKind::SwitchInt { targets, .. } => {
+ targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
+ }
+ TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
- | TerminatorKind::Unreachable => vec![],
+ | TerminatorKind::Unreachable => (),
TerminatorKind::Call { target, cleanup, destination, .. } => {
- if destination.projection.len() == 0 && destination.local == l {
+ if destination.projection.lookup(&body.projection_store).len() == 0
+ && destination.local == l
+ {
is_ever_initialized = true;
}
- target.into_iter().chain(cleanup.into_iter()).copied().collect()
+ target
+ .into_iter()
+ .chain(cleanup.into_iter())
+ .for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::Drop { target, unwind, place: _ } => {
- Some(target).into_iter().chain(unwind.into_iter()).copied().collect()
+ iter::once(target)
+ .into_iter()
+ .chain(unwind.into_iter())
+ .for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Assert { .. }
@@ -300,13 +313,7 @@ fn ever_initialized_map(
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet");
- vec![]
- }
- };
- for target in targets {
- if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
- result[target].insert(l, is_ever_initialized);
- dfs(db, body, target, l, result);
+ ()
}
}
}
@@ -315,6 +322,7 @@ fn ever_initialized_map(
dfs(db, body, body.start_block, l, &mut result);
}
for l in body.locals.iter().map(|it| it.0) {
+ db.unwind_if_cancelled();
if !result[body.start_block].contains_idx(l) {
result[body.start_block].insert(l, false);
dfs(db, body, body.start_block, l, &mut result);
@@ -371,7 +379,7 @@ fn mutability_of_locals(
};
match &terminator.kind {
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
@@ -384,7 +392,7 @@ fn mutability_of_locals(
| TerminatorKind::Assert { .. }
| TerminatorKind::Yield { .. } => (),
TerminatorKind::Call { destination, .. } => {
- if destination.projection.len() == 0 {
+ if destination.projection.lookup(&body.projection_store).len() == 0 {
if ever_init_map.get(destination.local).copied().unwrap_or_default() {
push_mut_span(destination.local, MirSpan::Unknown);
} else {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 9e30eed56..4364e0d32 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -10,7 +10,7 @@ use std::{
};
use base_db::{CrateId, FileId};
-use chalk_ir::Mutability;
+use chalk_ir::{cast::Cast, Mutability};
use either::Either;
use hir_def::{
builtin_type::BuiltinType,
@@ -40,14 +40,14 @@ use crate::{
name, static_lifetime,
traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst},
- CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap,
- Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+ CallableDefId, ClosureId, Const, ConstScalar, FnDefId, Interner, MemoryMap, Substitution,
+ TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
};
use super::{
return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError,
- MirSpan, Operand, Place, PlaceElem, ProjectionElem, Rvalue, StatementKind, TerminatorKind,
- UnOp,
+ MirSpan, Operand, Place, PlaceElem, ProjectionElem, ProjectionStore, Rvalue, StatementKind,
+ TerminatorKind, UnOp,
};
mod shim;
@@ -215,9 +215,7 @@ impl Interval {
}
fn write_from_interval(&self, memory: &mut Evaluator<'_>, interval: Interval) -> Result<()> {
- // FIXME: this could be more efficient
- let bytes = &interval.get(memory)?.to_vec();
- memory.write_memory(self.addr, bytes)
+ memory.copy_from_interval(self.addr, interval)
}
fn slice(self, range: Range<usize>) -> Interval {
@@ -341,7 +339,7 @@ pub enum MirEvalError {
InvalidVTableId(usize),
CoerceUnsizedError(Ty),
LangItemNotFound(LangItem),
- BrokenLayout(Layout),
+ BrokenLayout(Box<Layout>),
}
impl MirEvalError {
@@ -410,7 +408,7 @@ impl MirEvalError {
err.pretty_print(f, db, span_formatter)?;
}
MirEvalError::ConstEvalError(name, err) => {
- MirLowerError::ConstEvalError(name.clone(), err.clone()).pretty_print(
+ MirLowerError::ConstEvalError((**name).into(), err.clone()).pretty_print(
f,
db,
span_formatter,
@@ -485,17 +483,18 @@ struct DropFlags {
}
impl DropFlags {
- fn add_place(&mut self, p: Place) {
- if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) {
+ fn add_place(&mut self, p: Place, store: &ProjectionStore) {
+ if p.iterate_over_parents(store).any(|it| self.need_drop.contains(&it)) {
return;
}
- self.need_drop.retain(|it| !p.is_parent(it));
+ self.need_drop.retain(|it| !p.is_parent(it, store));
self.need_drop.insert(p);
}
- fn remove_place(&mut self, p: &Place) -> bool {
+ fn remove_place(&mut self, p: &Place, store: &ProjectionStore) -> bool {
// FIXME: replace parents with parts
- if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(&it)) {
+ if let Some(parent) = p.iterate_over_parents(store).find(|it| self.need_drop.contains(&it))
+ {
self.need_drop.remove(&parent);
return true;
}
@@ -656,7 +655,7 @@ impl Evaluator<'_> {
let mut addr = locals.ptr[p.local].addr;
let mut ty: Ty = locals.body.locals[p.local].ty.clone();
let mut metadata: Option<IntervalOrOwned> = None; // locals are always sized
- for proj in &*p.projection {
+ for proj in p.projection.lookup(&locals.body.projection_store) {
let prev_ty = ty.clone();
ty = self.projected_ty(ty, proj.clone());
match proj {
@@ -837,7 +836,9 @@ impl Evaluator<'_> {
let addr = self.place_addr(l, &locals)?;
let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
self.write_memory(addr, &result)?;
- locals.drop_flags.add_place(l.clone());
+ locals
+ .drop_flags
+ .add_place(l.clone(), &locals.body.projection_store);
}
StatementKind::Deinit(_) => not_supported!("de-init statement"),
StatementKind::StorageLive(_)
@@ -889,7 +890,9 @@ impl Evaluator<'_> {
)?,
it => not_supported!("unknown function type {it:?}"),
};
- locals.drop_flags.add_place(destination.clone());
+ locals
+ .drop_flags
+ .add_place(destination.clone(), &locals.body.projection_store);
if let Some(stack_frame) = stack_frame {
self.code_stack.push(my_stack_frame);
current_block_idx = stack_frame.locals.body.start_block;
@@ -970,7 +973,7 @@ impl Evaluator<'_> {
) -> Result<()> {
let mut remain_args = body.param_locals.len();
for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) {
- locals.drop_flags.add_place(l.into());
+ locals.drop_flags.add_place(l.into(), &locals.body.projection_store);
match value {
IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?,
IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?,
@@ -1629,7 +1632,7 @@ impl Evaluator<'_> {
if let Some((offset, size, value)) = tag {
match result.get_mut(offset..offset + size) {
Some(it) => it.copy_from_slice(&value.to_le_bytes()[0..size]),
- None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
+ None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))),
}
}
for (i, op) in values.enumerate() {
@@ -1637,7 +1640,7 @@ impl Evaluator<'_> {
let op = op.get(&self)?;
match result.get_mut(offset..offset + op.len()) {
Some(it) => it.copy_from_slice(op),
- None => return Err(MirEvalError::BrokenLayout(variant_layout.clone())),
+ None => return Err(MirEvalError::BrokenLayout(Box::new(variant_layout.clone()))),
}
}
Ok(result)
@@ -1646,7 +1649,7 @@ impl Evaluator<'_> {
fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> {
Ok(match it {
Operand::Copy(p) | Operand::Move(p) => {
- locals.drop_flags.remove_place(p);
+ locals.drop_flags.remove_place(p, &locals.body.projection_store);
self.eval_place(p, locals)?
}
Operand::Static(st) => {
@@ -1760,6 +1763,48 @@ impl Evaluator<'_> {
Ok(())
}
+ fn copy_from_interval(&mut self, addr: Address, r: Interval) -> Result<()> {
+ if r.size == 0 {
+ return Ok(());
+ }
+
+ let oob = || MirEvalError::UndefinedBehavior("out of bounds memory write".to_string());
+
+ match (addr, r.addr) {
+ (Stack(dst), Stack(src)) => {
+ if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
+ return Err(oob());
+ }
+ self.stack.copy_within(src..src + r.size, dst)
+ }
+ (Heap(dst), Heap(src)) => {
+ if self.stack.len() < src + r.size || self.stack.len() < dst + r.size {
+ return Err(oob());
+ }
+ self.heap.copy_within(src..src + r.size, dst)
+ }
+ (Stack(dst), Heap(src)) => {
+ self.stack
+ .get_mut(dst..dst + r.size)
+ .ok_or_else(oob)?
+ .copy_from_slice(self.heap.get(src..src + r.size).ok_or_else(oob)?);
+ }
+ (Heap(dst), Stack(src)) => {
+ self.heap
+ .get_mut(dst..dst + r.size)
+ .ok_or_else(oob)?
+ .copy_from_slice(self.stack.get(src..src + r.size).ok_or_else(oob)?);
+ }
+ _ => {
+ return Err(MirEvalError::UndefinedBehavior(format!(
+ "invalid memory write at address {addr:?}"
+ )))
+ }
+ }
+
+ Ok(())
+ }
+
fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<Option<(usize, usize)>> {
if let Some(layout) = self.layout_cache.borrow().get(ty) {
return Ok(layout
@@ -2007,7 +2052,28 @@ impl Evaluator<'_> {
}
}
AdtId::UnionId(_) => (),
- AdtId::EnumId(_) => (),
+ AdtId::EnumId(e) => {
+ if let Some((variant, layout)) = detect_variant_from_bytes(
+ &layout,
+ self.db,
+ self.trait_env.clone(),
+ self.read_memory(addr, layout.size.bytes_usize())?,
+ e,
+ ) {
+ let ev = EnumVariantId { parent: e, local_id: variant };
+ for (i, (_, ty)) in self.db.field_types(ev.into()).iter().enumerate() {
+ let offset = layout.fields.offset(i).bytes_usize();
+ let ty = ty.clone().substitute(Interner, subst);
+ self.patch_addresses(
+ patch_map,
+ old_vtable,
+ addr.offset(offset),
+ &ty,
+ locals,
+ )?;
+ }
+ }
+ }
},
TyKind::Tuple(_, subst) => {
for (id, ty) in subst.iter(Interner).enumerate() {
@@ -2248,7 +2314,7 @@ impl Evaluator<'_> {
interval: args_for_target[0].interval.slice(0..self.ptr_size()),
ty: ty.clone(),
};
- let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
+ let ty = ty.clone().cast(Interner);
let generics_for_target = Substitution::from_iter(
Interner,
generic_args.iter(Interner).enumerate().map(|(i, it)| {
@@ -2447,7 +2513,7 @@ impl Evaluator<'_> {
fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> {
let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
- if !locals.drop_flags.remove_place(place) {
+ if !locals.drop_flags.remove_place(place, &locals.body.projection_store) {
return Ok(());
}
let metadata = match metadata {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index b2e29fd34..803ef631f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -4,7 +4,10 @@
use std::cmp;
use chalk_ir::TyKind;
-use hir_def::resolver::HasResolver;
+use hir_def::{
+ builtin_type::{BuiltinInt, BuiltinUint},
+ resolver::HasResolver,
+};
use hir_expand::mod_path::ModPath;
use super::*;
@@ -136,7 +139,10 @@ impl Evaluator<'_> {
not_supported!("wrong generic arg kind for clone");
};
// Clone has special impls for tuples and function pointers
- if matches!(self_ty.kind(Interner), TyKind::Function(_) | TyKind::Tuple(..)) {
+ if matches!(
+ self_ty.kind(Interner),
+ TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..)
+ ) {
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
return Ok(true);
}
@@ -167,32 +173,26 @@ impl Evaluator<'_> {
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 (closure_owner, _) = self.db.lookup_intern_closure((*id).into());
+ let infer = self.db.infer(closure_owner);
+ let (captures, _) = infer.closure_info(id);
+ let layout = self.layout(&self_ty)?;
+ let ty_iter = captures.iter().map(|c| c.ty(subst));
+ self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
+ }
TyKind::Tuple(_, subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let layout = self.layout(&self_ty)?;
- for (i, ty) in subst.iter(Interner).enumerate() {
- let ty = ty.assert_ty_ref(Interner);
- let size = self.layout(ty)?.size.bytes_usize();
- let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
- let arg = IntervalAndTy {
- interval: Interval { addr: tmp, size: self.ptr_size() },
- ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone())
- .intern(Interner),
- };
- let offset = layout.fields.offset(i).bytes_usize();
- self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
- self.exec_clone(
- def,
- &[arg],
- ty.clone(),
- locals,
- destination.slice(offset..offset + size),
- span,
- )?;
- }
+ let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone());
+ self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
}
_ => {
self.exec_fn_with_args(
@@ -209,6 +209,37 @@ impl Evaluator<'_> {
Ok(())
}
+ fn exec_clone_for_fields(
+ &mut self,
+ ty_iter: impl Iterator<Item = Ty>,
+ layout: Arc<Layout>,
+ addr: Address,
+ def: FunctionId,
+ locals: &Locals,
+ destination: Interval,
+ span: MirSpan,
+ ) -> Result<()> {
+ for (i, ty) in ty_iter.enumerate() {
+ let size = self.layout(&ty)?.size.bytes_usize();
+ let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
+ let arg = IntervalAndTy {
+ interval: Interval { addr: tmp, size: self.ptr_size() },
+ ty: TyKind::Ref(Mutability::Not, static_lifetime(), ty.clone()).intern(Interner),
+ };
+ let offset = layout.fields.offset(i).bytes_usize();
+ self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
+ self.exec_clone(
+ def,
+ &[arg],
+ ty,
+ locals,
+ destination.slice(offset..offset + size),
+ span,
+ )?;
+ }
+ Ok(())
+ }
+
fn exec_alloc_fn(
&mut self,
alloc_fn: &str,
@@ -272,21 +303,36 @@ impl Evaluator<'_> {
BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_string())),
PanicFmt => {
let message = (|| {
- let resolver = self.db.crate_def_map(self.crate_id).crate_root().resolver(self.db.upcast());
+ let resolver = self
+ .db
+ .crate_def_map(self.crate_id)
+ .crate_root()
+ .resolver(self.db.upcast());
let Some(format_fn) = resolver.resolve_path_in_value_ns_fully(
self.db.upcast(),
- &hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments(
- hir_expand::mod_path::PathKind::Abs,
- [name![std], name![fmt], name![format]].into_iter(),
- )),
+ &hir_def::path::Path::from_known_path_with_no_generic(
+ ModPath::from_segments(
+ hir_expand::mod_path::PathKind::Abs,
+ [name![std], name![fmt], name![format]].into_iter(),
+ ),
+ ),
) else {
not_supported!("std::fmt::format not found");
};
- let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") };
- let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?;
- let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
+ let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else {
+ not_supported!("std::fmt::format is not a function")
+ };
+ let message_string = self.interpret_mir(
+ self.db
+ .mir_body(format_fn.into())
+ .map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
+ args.map(|x| IntervalOrOwned::Owned(x.clone())),
+ )?;
+ let addr =
+ Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
- Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned())
+ Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?)
+ .into_owned())
})()
.unwrap_or_else(|e| format!("Failed to render panic format args: {e:?}"));
Err(MirEvalError::Panic(message))
@@ -455,9 +501,7 @@ impl Evaluator<'_> {
}
"syscall" => {
let Some((id, rest)) = args.split_first() else {
- return Err(MirEvalError::TypeError(
- "syscall arg1 is not provided",
- ));
+ return Err(MirEvalError::TypeError("syscall arg1 is not provided"));
};
let id = from_bytes!(i64, id.get(self)?);
self.exec_syscall(id, rest, destination, locals, span)
@@ -473,6 +517,38 @@ impl Evaluator<'_> {
self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
Ok(())
}
+ "getenv" => {
+ let [name] = args else {
+ return Err(MirEvalError::TypeError("libc::write args are not provided"));
+ };
+ let mut name_buf = vec![];
+ let name = {
+ let mut index = Address::from_bytes(name.get(self)?)?;
+ loop {
+ let byte = self.read_memory(index, 1)?[0];
+ index = index.offset(1);
+ if byte == 0 {
+ break;
+ }
+ name_buf.push(byte);
+ }
+ String::from_utf8_lossy(&name_buf)
+ };
+ let value = self.db.crate_graph()[self.crate_id].env.get(&name);
+ match value {
+ None => {
+ // Write null as fail
+ self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
+ }
+ Some(mut value) => {
+ value.push('\0');
+ let addr = self.heap_allocate(value.len(), 1)?;
+ self.write_memory(addr, value.as_bytes())?;
+ self.write_memory(destination.addr, &addr.to_bytes())?;
+ }
+ }
+ Ok(())
+ }
_ => not_supported!("unknown external function {as_str}"),
}
}
@@ -650,7 +726,8 @@ impl Evaluator<'_> {
}
match name {
"size_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
@@ -658,14 +735,17 @@ impl Evaluator<'_> {
destination.write_from_bytes(self, &size.to_le_bytes()[0..destination.size])
}
"min_align_of" | "pref_align_of" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
return Err(MirEvalError::TypeError("align_of generic arg is not provided"));
};
let align = self.layout(ty)?.align.abi.bytes();
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
}
"size_of_val" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of_val generic arg is not provided"));
};
@@ -681,8 +761,12 @@ impl Evaluator<'_> {
}
}
"min_align_of_val" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
- return Err(MirEvalError::TypeError("min_align_of_val generic arg is not provided"));
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "min_align_of_val generic arg is not provided",
+ ));
};
let [arg] = args else {
return Err(MirEvalError::TypeError("min_align_of_val args are not provided"));
@@ -696,7 +780,8 @@ impl Evaluator<'_> {
}
}
"type_name" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
};
@@ -719,7 +804,8 @@ impl Evaluator<'_> {
.write_from_bytes(self, &len.to_le_bytes())
}
"needs_drop" => {
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError("size_of generic arg is not provided"));
};
@@ -771,9 +857,12 @@ impl Evaluator<'_> {
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
- return Err(MirEvalError::TypeError("ptr_offset_from generic arg is not provided"));
+ return Err(MirEvalError::TypeError(
+ "ptr_offset_from generic arg is not provided",
+ ));
};
let size = self.size_of_sized(ty, locals, "ptr_offset_from arg")? as i128;
let ans = ans / size;
@@ -880,7 +969,8 @@ impl Evaluator<'_> {
"copy_nonoverlapping args are not provided",
));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError(
"copy_nonoverlapping generic arg is not provided",
@@ -899,9 +989,45 @@ impl Evaluator<'_> {
let [ptr, offset] = args else {
return Err(MirEvalError::TypeError("offset args are not provided"));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
- else {
- return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ let ty = if name == "offset" {
+ let Some(ty0) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ let Some(ty1) =
+ generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError("offset generic arg is not provided"));
+ };
+ if !matches!(
+ ty1.as_builtin(),
+ Some(
+ BuiltinType::Int(BuiltinInt::Isize)
+ | BuiltinType::Uint(BuiltinUint::Usize)
+ )
+ ) {
+ return Err(MirEvalError::TypeError(
+ "offset generic arg is not usize or isize",
+ ));
+ }
+ match ty0.as_raw_ptr() {
+ Some((ty, _)) => ty,
+ None => {
+ return Err(MirEvalError::TypeError(
+ "offset generic arg is not a raw pointer",
+ ));
+ }
+ }
+ } else {
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "arith_offset generic arg is not provided",
+ ));
+ };
+ ty
};
let ptr = u128::from_le_bytes(pad16(ptr.get(self)?, false));
let offset = u128::from_le_bytes(pad16(offset.get(self)?, false));
@@ -1019,7 +1145,8 @@ impl Evaluator<'_> {
let [arg] = args else {
return Err(MirEvalError::TypeError("discriminant_value arg is not provided"));
};
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
return Err(MirEvalError::TypeError(
"discriminant_value generic arg is not provided",
@@ -1073,17 +1200,32 @@ impl Evaluator<'_> {
let addr = Address::from_bytes(arg.interval.get(self)?)?;
destination.write_from_interval(self, Interval { addr, size: destination.size })
}
+ "write_via_move" => {
+ let [ptr, val] = args else {
+ return Err(MirEvalError::TypeError("write_via_move args are not provided"));
+ };
+ let dst = Address::from_bytes(ptr.get(self)?)?;
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError(
+ "write_via_copy generic arg is not provided",
+ ));
+ };
+ let size = self.size_of_sized(ty, locals, "write_via_move ptr type")?;
+ Interval { addr: dst, size }.write_from_interval(self, val.interval)?;
+ Ok(())
+ }
"write_bytes" => {
let [dst, val, count] = args else {
return Err(MirEvalError::TypeError("write_bytes args are not provided"));
};
let count = from_bytes!(usize, count.get(self)?);
let val = from_bytes!(u8, val.get(self)?);
- let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ let Some(ty) =
+ generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
- return Err(MirEvalError::TypeError(
- "write_bytes generic arg is not provided",
- ));
+ return Err(MirEvalError::TypeError("write_bytes generic arg is not provided"));
};
let dst = Address::from_bytes(dst.get(self)?)?;
let size = self.size_of_sized(ty, locals, "copy_nonoverlapping ptr type")?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
index ec7463104..519006624 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim/simd.rs
@@ -45,7 +45,9 @@ impl Evaluator<'_> {
};
match try_const_usize(self.db, len) {
Some(len) => {
- let Some(ty) = subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner)) else {
+ let Some(ty) =
+ subst.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
return Err(MirEvalError::TypeError("simd type with no ty param"));
};
Ok((len as usize, ty.clone()))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index 46165cf3d..ff30dc6da 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -730,6 +730,48 @@ fn main() {
}
#[test]
+fn posix_getenv() {
+ check_pass(
+ r#"
+//- /main.rs env:foo=bar
+
+type c_char = u8;
+
+extern "C" {
+ pub fn getenv(s: *const c_char) -> *mut c_char;
+}
+
+fn should_not_reach() {
+ _ // FIXME: replace this function with panic when that works
+}
+
+fn main() {
+ let result = getenv(b"foo\0" as *const _);
+ if *result != b'b' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != b'a' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != b'r' {
+ should_not_reach();
+ }
+ let result = (result as usize + 1) as *const c_char;
+ if *result != 0 {
+ should_not_reach();
+ }
+ let result = getenv(b"not found\0" as *const _);
+ if result as usize != 0 {
+ should_not_reach();
+ }
+}
+"#,
+ );
+}
+
+#[test]
fn posix_tls() {
check_pass(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 718df8331..dd2dba717 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -15,7 +15,7 @@ use hir_def::{
path::Path,
resolver::{resolver_for_expr, HasResolver, ResolveValueResult, ValueNs},
AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId,
- TraitId, TypeOrConstParamId,
+ Lookup, TraitId, TypeOrConstParamId,
};
use hir_expand::name::Name;
use la_arena::ArenaMap;
@@ -71,7 +71,7 @@ struct MirLowerCtx<'a> {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MirLowerError {
- ConstEvalError(String, Box<ConstEvalError>),
+ ConstEvalError(Box<str>, Box<ConstEvalError>),
LayoutError(LayoutError),
IncompleteExpr,
IncompletePattern,
@@ -84,7 +84,7 @@ pub enum MirLowerError {
UnsizedTemporary(Ty),
MissingFunctionDefinition(DefWithBodyId, ExprId),
TypeMismatch(TypeMismatch),
- /// This should be never happen. Type mismatch should catch everything.
+ /// This should never happen. Type mismatch should catch everything.
TypeError(&'static str),
NotSupported(String),
ContinueWithoutLoop,
@@ -244,6 +244,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let locals = Arena::new();
let binding_locals: ArenaMap<BindingId, LocalId> = ArenaMap::new();
let mir = MirBody {
+ projection_store: ProjectionStore::default(),
basic_blocks,
locals,
start_block,
@@ -370,9 +371,15 @@ impl<'ctx> MirLowerCtx<'ctx> {
mut current: BasicBlockId,
) -> Result<Option<BasicBlockId>> {
match &self.body.exprs[expr_id] {
+ Expr::OffsetOf(_) => {
+ not_supported!("builtin#offset_of")
+ }
+ Expr::InlineAsm(_) => {
+ not_supported!("builtin#asm")
+ }
Expr::Missing => {
if let DefWithBodyId::FunctionId(f) = self.owner {
- let assoc = self.db.lookup_intern_function(f);
+ let assoc = f.lookup(self.db.upcast());
if let ItemContainerId::TraitId(t) = assoc.container {
let name = &self.db.function_data(f).name;
return Err(MirLowerError::TraitFunctionDefinition(t, name.clone()));
@@ -803,36 +810,34 @@ impl<'ctx> MirLowerCtx<'ctx> {
current = c;
operands[u32::from(field_id.into_raw()) as usize] = Some(op);
}
- self.push_assignment(
- current,
- place,
- Rvalue::Aggregate(
- AggregateKind::Adt(variant_id, subst),
- match spread_place {
- Some(sp) => operands
- .into_iter()
- .enumerate()
- .map(|(i, it)| match it {
- Some(it) => it,
- None => {
- let p =
- sp.project(ProjectionElem::Field(FieldId {
- parent: variant_id,
- local_id: LocalFieldId::from_raw(
- RawIdx::from(i as u32),
- ),
- }));
- Operand::Copy(p)
- }
- })
- .collect(),
- None => operands.into_iter().collect::<Option<_>>().ok_or(
- MirLowerError::TypeError("missing field in record literal"),
- )?,
- },
- ),
- expr_id.into(),
+ let rvalue = Rvalue::Aggregate(
+ AggregateKind::Adt(variant_id, subst),
+ match spread_place {
+ Some(sp) => operands
+ .into_iter()
+ .enumerate()
+ .map(|(i, it)| match it {
+ Some(it) => it,
+ None => {
+ let p = sp.project(
+ ProjectionElem::Field(FieldId {
+ parent: variant_id,
+ local_id: LocalFieldId::from_raw(RawIdx::from(
+ i as u32,
+ )),
+ }),
+ &mut self.result.projection_store,
+ );
+ Operand::Copy(p)
+ }
+ })
+ .collect(),
+ None => operands.into_iter().collect::<Option<_>>().ok_or(
+ MirLowerError::TypeError("missing field in record literal"),
+ )?,
+ },
);
+ self.push_assignment(current, place, rvalue, expr_id.into());
Ok(Some(current))
}
VariantId::UnionId(union_id) => {
@@ -841,10 +846,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
};
let local_id =
variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?;
- let place = place.project(PlaceElem::Field(FieldId {
- parent: union_id.into(),
- local_id,
- }));
+ let place = place.project(
+ PlaceElem::Field(FieldId { parent: union_id.into(), local_id }),
+ &mut self.result.projection_store,
+ );
self.lower_expr_to_place(*expr, place, current)
}
}
@@ -898,7 +903,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
else {
return Ok(None);
};
- let p = place.project(ProjectionElem::Deref);
+ let p = place.project(ProjectionElem::Deref, &mut self.result.projection_store);
self.push_assignment(current, p, operand.into(), expr_id.into());
Ok(Some(current))
}
@@ -1120,27 +1125,31 @@ impl<'ctx> MirLowerCtx<'ctx> {
for capture in captures.iter() {
let p = Place {
local: self.binding_local(capture.place.local)?,
- projection: capture
- .place
- .projections
- .clone()
- .into_iter()
- .map(|it| match it {
- ProjectionElem::Deref => ProjectionElem::Deref,
- ProjectionElem::Field(it) => ProjectionElem::Field(it),
- ProjectionElem::TupleOrClosureField(it) => {
- ProjectionElem::TupleOrClosureField(it)
- }
- ProjectionElem::ConstantIndex { offset, from_end } => {
- ProjectionElem::ConstantIndex { offset, from_end }
- }
- ProjectionElem::Subslice { from, to } => {
- ProjectionElem::Subslice { from, to }
- }
- ProjectionElem::OpaqueCast(it) => ProjectionElem::OpaqueCast(it),
- ProjectionElem::Index(it) => match it {},
- })
- .collect(),
+ projection: self.result.projection_store.intern(
+ capture
+ .place
+ .projections
+ .clone()
+ .into_iter()
+ .map(|it| match it {
+ ProjectionElem::Deref => ProjectionElem::Deref,
+ ProjectionElem::Field(it) => ProjectionElem::Field(it),
+ ProjectionElem::TupleOrClosureField(it) => {
+ ProjectionElem::TupleOrClosureField(it)
+ }
+ ProjectionElem::ConstantIndex { offset, from_end } => {
+ ProjectionElem::ConstantIndex { offset, from_end }
+ }
+ ProjectionElem::Subslice { from, to } => {
+ ProjectionElem::Subslice { from, to }
+ }
+ ProjectionElem::OpaqueCast(it) => {
+ ProjectionElem::OpaqueCast(it)
+ }
+ ProjectionElem::Index(it) => match it {},
+ })
+ .collect(),
+ ),
};
match &capture.kind {
CaptureKind::ByRef(bk) => {
@@ -1201,7 +1210,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
let Some(values) = elements
.iter()
.map(|it| {
- let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)? else {
+ let Some((o, c)) = self.lower_expr_to_some_operand(*it, current)?
+ else {
return Ok(None);
};
current = c;
@@ -1244,6 +1254,40 @@ impl<'ctx> MirLowerCtx<'ctx> {
}
}
+ fn lower_destructing_assignment(
+ &mut self,
+ mut current: BasicBlockId,
+ lhs: ExprId,
+ rhs: Place,
+ span: MirSpan,
+ ) -> Result<Option<BasicBlockId>> {
+ match &self.body.exprs[lhs] {
+ Expr::Tuple { exprs, is_assignee_expr: _ } => {
+ for (i, expr) in exprs.iter().enumerate() {
+ let rhs = rhs.project(
+ ProjectionElem::TupleOrClosureField(i),
+ &mut self.result.projection_store,
+ );
+ let Some(c) = self.lower_destructing_assignment(current, *expr, rhs, span)?
+ else {
+ return Ok(None);
+ };
+ current = c;
+ }
+ Ok(Some(current))
+ }
+ Expr::Underscore => Ok(Some(current)),
+ _ => {
+ let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)?
+ else {
+ return Ok(None);
+ };
+ self.push_assignment(current, lhs_place, Operand::Copy(rhs).into(), span);
+ Ok(Some(current))
+ }
+ }
+ }
+
fn lower_assignment(
&mut self,
current: BasicBlockId,
@@ -1251,17 +1295,22 @@ impl<'ctx> MirLowerCtx<'ctx> {
rhs: ExprId,
span: MirSpan,
) -> Result<Option<BasicBlockId>> {
- let Some((rhs_op, current)) =
- self.lower_expr_to_some_operand(rhs, current)?
- else {
+ let Some((rhs_op, current)) = self.lower_expr_to_some_operand(rhs, current)? else {
return Ok(None);
};
if matches!(&self.body.exprs[lhs], Expr::Underscore) {
return Ok(Some(current));
}
- let Some((lhs_place, current)) =
- self.lower_expr_as_place(current, lhs, false)?
- else {
+ if matches!(
+ &self.body.exprs[lhs],
+ Expr::Tuple { .. } | Expr::RecordLit { .. } | Expr::Call { .. }
+ ) {
+ let temp = self.temp(self.expr_ty_after_adjustments(rhs), current, rhs.into())?;
+ let temp = Place::from(temp);
+ self.push_assignment(current, temp.clone(), rhs_op.into(), span);
+ return self.lower_destructing_assignment(current, lhs, temp, span);
+ }
+ let Some((lhs_place, current)) = self.lower_expr_as_place(current, lhs, false)? else {
return Ok(None);
};
self.push_assignment(current, lhs_place, rhs_op.into(), span);
@@ -1276,17 +1325,21 @@ impl<'ctx> MirLowerCtx<'ctx> {
placeholder_subst
}
- fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> {
+ fn push_field_projection(&mut self, place: &mut Place, expr_id: ExprId) -> Result<()> {
if let Expr::Field { expr, name } = &self.body[expr_id] {
if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) {
let index = name
.as_tuple_index()
.ok_or(MirLowerError::TypeError("named field on tuple"))?;
- *place = place.project(ProjectionElem::TupleOrClosureField(index))
+ *place = place.project(
+ ProjectionElem::TupleOrClosureField(index),
+ &mut self.result.projection_store,
+ )
} else {
let field =
self.infer.field_resolution(expr_id).ok_or(MirLowerError::UnresolvedField)?;
- *place = place.project(ProjectionElem::Field(field));
+ *place =
+ place.project(ProjectionElem::Field(field), &mut self.result.projection_store);
}
} else {
not_supported!("")
@@ -1308,14 +1361,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
.resolve_path_in_value_ns(self.db.upcast(), c)
.ok_or_else(unresolved_name)?;
match pr {
- ResolveValueResult::ValueNs(v) => {
+ ResolveValueResult::ValueNs(v, _) => {
if let ValueNs::ConstId(c) = v {
self.lower_const_to_operand(Substitution::empty(Interner), c.into(), ty)
} else {
not_supported!("bad path in range pattern");
}
}
- ResolveValueResult::Partial(_, _) => {
+ ResolveValueResult::Partial(_, _, _) => {
not_supported!("associated constants in range pattern")
}
}
@@ -1403,7 +1456,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
let name = const_id.name(self.db.upcast());
self.db
.const_eval(const_id.into(), subst, None)
- .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))?
+ .map_err(|e| MirLowerError::ConstEvalError(name.into(), Box::new(e)))?
};
Ok(Operand::Constant(c))
}
@@ -1800,7 +1853,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
data.name.display(self.db.upcast()),
data.variants[variant.local_id].name.display(self.db.upcast())
);
- Err(MirLowerError::ConstEvalError(name, Box::new(e)))
+ Err(MirLowerError::ConstEvalError(name.into(), Box::new(e)))
}
}
}
@@ -1948,13 +2001,14 @@ pub fn mir_body_for_closure_query(
FnTrait::FnOnce => vec![],
FnTrait::FnMut | FnTrait::Fn => vec![ProjectionElem::Deref],
};
- ctx.result.walk_places(|p| {
+ ctx.result.walk_places(|p, store| {
if let Some(it) = upvar_map.get(&p.local) {
let r = it.iter().find(|it| {
- if p.projection.len() < it.0.place.projections.len() {
+ if p.projection.lookup(&store).len() < it.0.place.projections.len() {
return false;
}
- for (it, y) in p.projection.iter().zip(it.0.place.projections.iter()) {
+ for (it, y) in p.projection.lookup(&store).iter().zip(it.0.place.projections.iter())
+ {
match (it, y) {
(ProjectionElem::Deref, ProjectionElem::Deref) => (),
(ProjectionElem::Field(it), ProjectionElem::Field(y)) if it == y => (),
@@ -1972,13 +2026,18 @@ pub fn mir_body_for_closure_query(
p.local = closure_local;
let mut next_projs = closure_projection.clone();
next_projs.push(PlaceElem::TupleOrClosureField(it.1));
- let prev_projs = mem::take(&mut p.projection);
+ let prev_projs = p.projection;
if it.0.kind != CaptureKind::ByValue {
next_projs.push(ProjectionElem::Deref);
}
- next_projs
- .extend(prev_projs.iter().cloned().skip(it.0.place.projections.len()));
- p.projection = next_projs.into();
+ next_projs.extend(
+ prev_projs
+ .lookup(&store)
+ .iter()
+ .cloned()
+ .skip(it.0.place.projections.len()),
+ );
+ p.projection = store.intern(next_projs.into());
}
None => err = Some(p.clone()),
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index 213f151ab..8c078eb4a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -70,7 +70,7 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- it.0 = it.0.project(ProjectionElem::Deref);
+ it.0 = it.0.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some(it))
}
Adjust::Deref(Some(od)) => {
@@ -152,7 +152,10 @@ impl MirLowerCtx<'_> {
Operand::Static(s).into(),
expr_id.into(),
);
- Ok(Some((temp.project(ProjectionElem::Deref), current)))
+ Ok(Some((
+ temp.project(ProjectionElem::Deref, &mut self.result.projection_store),
+ current,
+ )))
}
_ => try_rvalue(self),
}
@@ -203,7 +206,7 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- r = r.project(ProjectionElem::Deref);
+ r = r.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((r, current)))
}
_ => try_rvalue(self),
@@ -267,7 +270,8 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- p_base = p_base.project(ProjectionElem::Index(l_index));
+ p_base = p_base
+ .project(ProjectionElem::Index(l_index), &mut self.result.projection_store);
Ok(Some((p_base, current)))
}
_ => try_rvalue(self),
@@ -308,7 +312,7 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- result = result.project(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((result, current)))
}
@@ -363,7 +367,7 @@ impl MirLowerCtx<'_> {
else {
return Ok(None);
};
- result = result.project(ProjectionElem::Deref);
+ result = result.project(ProjectionElem::Deref, &mut self.result.projection_store);
Ok(Some((result, current)))
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 3354cbd76..270f75ad9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -81,13 +81,16 @@ impl MirLowerCtx<'_> {
mode: MatchingMode,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
- cond_place.projection = cond_place
- .projection
- .iter()
- .cloned()
- .chain((0..cnt).map(|_| ProjectionElem::Deref))
- .collect::<Vec<_>>()
- .into();
+ cond_place.projection = self.result.projection_store.intern(
+ cond_place
+ .projection
+ .lookup(&self.result.projection_store)
+ .iter()
+ .cloned()
+ .chain((0..cnt).map(|_| ProjectionElem::Deref))
+ .collect::<Vec<_>>()
+ .into(),
+ );
Ok(match &self.body.pats[pattern] {
Pat::Missing => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
@@ -262,20 +265,23 @@ impl MirLowerCtx<'_> {
}
}
for (i, &pat) in prefix.iter().enumerate() {
- let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
- offset: i as u64,
- from_end: false,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::ConstantIndex { offset: i as u64, from_end: false },
+ &mut self.result.projection_store,
+ );
(current, current_else) =
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
}
if let Some(slice) = slice {
if mode == MatchingMode::Bind {
if let Pat::Bind { id, subpat: _ } = self.body[*slice] {
- let next_place = (&mut cond_place).project(ProjectionElem::Subslice {
- from: prefix.len() as u64,
- to: suffix.len() as u64,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::Subslice {
+ from: prefix.len() as u64,
+ to: suffix.len() as u64,
+ },
+ &mut self.result.projection_store,
+ );
(current, current_else) = self.pattern_match_binding(
id,
next_place,
@@ -287,10 +293,10 @@ impl MirLowerCtx<'_> {
}
}
for (i, &pat) in suffix.iter().enumerate() {
- let next_place = (&mut cond_place).project(ProjectionElem::ConstantIndex {
- offset: i as u64,
- from_end: true,
- });
+ let next_place = (&mut cond_place).project(
+ ProjectionElem::ConstantIndex { offset: i as u64, from_end: true },
+ &mut self.result.projection_store,
+ );
(current, current_else) =
self.pattern_match_inner(current, current_else, next_place, pat, mode)?;
}
@@ -323,7 +329,7 @@ impl MirLowerCtx<'_> {
break 'b (c, x.1);
}
}
- if let ResolveValueResult::ValueNs(v) = pr {
+ if let ResolveValueResult::ValueNs(v, _) = pr {
if let ValueNs::ConstId(c) = v {
break 'b (c, Substitution::empty(Interner));
}
@@ -412,13 +418,11 @@ impl MirLowerCtx<'_> {
mode,
)?
}
- Pat::Ref { pat, mutability: _ } => self.pattern_match_inner(
- current,
- current_else,
- cond_place.project(ProjectionElem::Deref),
- *pat,
- mode,
- )?,
+ Pat::Ref { pat, mutability: _ } => {
+ let cond_place =
+ cond_place.project(ProjectionElem::Deref, &mut self.result.projection_store);
+ self.pattern_match_inner(current, current_else, cond_place, *pat, mode)?
+ }
Pat::Box { .. } => not_supported!("box pattern"),
Pat::ConstBlock(_) => not_supported!("const block pattern"),
})
@@ -594,7 +598,7 @@ impl MirLowerCtx<'_> {
mode: MatchingMode,
) -> Result<(BasicBlockId, Option<BasicBlockId>)> {
for (proj, arg) in args {
- let cond_place = cond_place.project(proj);
+ let cond_place = cond_place.project(proj, &mut self.result.projection_store);
(current, current_else) =
self.pattern_match_inner(current, current_else, cond_place, arg, mode)?;
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
index c565228d9..df16d0d82 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs
@@ -265,7 +265,7 @@ impl Filler<'_> {
self.fill_operand(discr)?;
}
TerminatorKind::Goto { .. }
- | TerminatorKind::Resume
+ | TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 781ffaeca..0108859ff 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -329,7 +329,7 @@ impl<'a> MirPrettyCtx<'a> {
}
}
}
- f(self, p.local, &p.projection);
+ f(self, p.local, &p.projection.lookup(&self.body.projection_store));
}
fn operand(&mut self, r: &Operand) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 2ad7946c8..8140c4107 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3,55 +3,6 @@ use expect_test::expect;
use super::{check, check_infer, check_no_mismatches, check_types};
#[test]
-fn infer_box() {
- check_types(
- r#"
-//- /main.rs crate:main deps:std
-fn test() {
- let x = box 1;
- let t = (x, box x, box &1, box [1]);
- t;
-} //^ (Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; 1]>)
-
-//- /std.rs crate:std
-#[prelude_import] use prelude::*;
-mod prelude {}
-
-mod boxed {
- #[lang = "owned_box"]
- pub struct Box<T: ?Sized> {
- inner: *mut T,
- }
-}
-"#,
- );
-}
-
-#[test]
-fn infer_box_with_allocator() {
- check_types(
- r#"
-//- /main.rs crate:main deps:std
-fn test() {
- let x = box 1;
- let t = (x, box x, box &1, box [1]);
- t;
-} //^ (Box<i32, {unknown}>, Box<Box<i32, {unknown}>, {unknown}>, Box<&i32, {unknown}>, Box<[i32; 1], {unknown}>)
-
-//- /std.rs crate:std
-#[prelude_import] use prelude::*;
-mod boxed {
- #[lang = "owned_box"]
- pub struct Box<T: ?Sized, A: Allocator> {
- inner: *mut T,
- allocator: A,
- }
-}
-"#,
- );
-}
-
-#[test]
fn infer_adt_self() {
check_types(
r#"
@@ -2763,8 +2714,8 @@ impl<T> [T] {
}
fn test() {
- let vec = <[_]>::into_vec(box [1i32]);
- let v: Vec<Box<dyn B>> = <[_]> :: into_vec(box [box Astruct]);
+ let vec = <[_]>::into_vec(#[rustc_box] Box::new([1i32]));
+ let v: Vec<Box<dyn B>> = <[_]> :: into_vec(#[rustc_box] Box::new([#[rustc_box] Box::new(Astruct)]));
}
trait B{}
@@ -2774,20 +2725,20 @@ impl B for Astruct {}
expect![[r#"
604..608 'self': Box<[T], A>
637..669 '{ ... }': Vec<T, A>
- 683..796 '{ ...t]); }': ()
+ 683..853 '{ ...])); }': ()
693..696 'vec': Vec<i32, Global>
699..714 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
- 699..726 '<[_]>:...1i32])': Vec<i32, Global>
- 715..725 'box [1i32]': Box<[i32; 1], Global>
- 719..725 '[1i32]': [i32; 1]
- 720..724 '1i32': i32
- 736..737 'v': Vec<Box<dyn B, Global>, Global>
- 757..774 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
- 757..793 '<[_]> ...ruct])': Vec<Box<dyn B, Global>, Global>
- 775..792 'box [b...truct]': Box<[Box<dyn B, Global>; 1], Global>
- 779..792 '[box Astruct]': [Box<dyn B, Global>; 1]
- 780..791 'box Astruct': Box<Astruct, Global>
- 784..791 'Astruct': Astruct
+ 699..745 '<[_]>:...i32]))': Vec<i32, Global>
+ 715..744 '#[rust...1i32])': Box<[i32; 1], Global>
+ 737..743 '[1i32]': [i32; 1]
+ 738..742 '1i32': i32
+ 755..756 'v': Vec<Box<dyn B, Global>, Global>
+ 776..793 '<[_]> ...to_vec': fn into_vec<Box<dyn B, Global>, Global>(Box<[Box<dyn B, Global>], Global>) -> Vec<Box<dyn B, Global>, Global>
+ 776..850 '<[_]> ...ct)]))': Vec<Box<dyn B, Global>, Global>
+ 794..849 '#[rust...uct)])': Box<[Box<dyn B, Global>; 1], Global>
+ 816..848 '[#[rus...ruct)]': [Box<dyn B, Global>; 1]
+ 817..847 '#[rust...truct)': Box<Astruct, Global>
+ 839..846 'Astruct': Astruct
"#]],
)
}
@@ -3649,3 +3600,30 @@ fn main() {
"#,
);
}
+
+#[test]
+fn offset_of() {
+ check_types(
+ r#"
+fn main() {
+ builtin#offset_of((,), 0);
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^ usize
+}
+"#,
+ );
+}
+
+#[test]
+fn builtin_format_args() {
+ check(
+ r#"
+//- minicore: fmt
+fn main() {
+ let are = "are";
+ let count = 10;
+ builtin#format_args("hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type: Arguments<'_>
+}
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index 542df8b34..d36b885ec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -162,16 +162,16 @@ unsafe impl Allocator for Global {}
#[lang = "owned_box"]
#[fundamental]
-pub struct Box<T: ?Sized, A: Allocator = Global>;
+pub struct Box<T: ?Sized, A: Allocator = Global>(T);
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
fn send() -> Box<dyn Future<Output = ()> + Send + 'static>{
- box async move {}
+ Box(async move {})
}
fn not_send() -> Box<dyn Future<Output = ()> + 'static> {
- box async move {}
+ Box(async move {})
}
"#,
);
@@ -3057,7 +3057,7 @@ impl<T: ?Sized> core::ops::Deref for Box<T> {
fn foo() {
let s = None;
- let f: Box<dyn FnOnce(&Option<i32>)> = box (|ps| {});
+ let f: Box<dyn FnOnce(&Option<i32>)> = Box { inner: &mut (|ps| {}) };
f(&s);
}"#,
expect![[r#"
@@ -3068,19 +3068,19 @@ fn foo() {
186..197 '*self.inner': T
187..191 'self': &Box<T>
187..197 'self.inner': *mut T
- 218..308 '{ ...&s); }': ()
+ 218..324 '{ ...&s); }': ()
228..229 's': Option<i32>
232..236 'None': Option<i32>
246..247 'f': Box<dyn FnOnce(&Option<i32>)>
- 281..294 'box (|ps| {})': Box<impl Fn(&Option<i32>)>
- 286..293 '|ps| {}': impl Fn(&Option<i32>)
- 287..289 'ps': &Option<i32>
- 291..293 '{}': ()
- 300..301 'f': Box<dyn FnOnce(&Option<i32>)>
- 300..305 'f(&s)': ()
- 302..304 '&s': &Option<i32>
- 303..304 's': Option<i32>
- 281..294: expected Box<dyn FnOnce(&Option<i32>)>, got Box<impl Fn(&Option<i32>)>
+ 281..310 'Box { ... {}) }': Box<dyn FnOnce(&Option<i32>)>
+ 294..308 '&mut (|ps| {})': &mut impl Fn(&Option<i32>)
+ 300..307 '|ps| {}': impl Fn(&Option<i32>)
+ 301..303 'ps': &Option<i32>
+ 305..307 '{}': ()
+ 316..317 'f': Box<dyn FnOnce(&Option<i32>)>
+ 316..321 'f(&s)': ()
+ 318..320 '&s': &Option<i32>
+ 319..320 's': Option<i32>
"#]],
);
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index 0f2fb2c81..796490abd 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -1,38 +1,27 @@
//! Attributes & documentation for hir types.
use hir_def::{
- attr::{AttrsWithOwner, Documentation},
+ attr::AttrsWithOwner,
item_scope::ItemInNs,
- path::ModPath,
- resolver::HasResolver,
- AttrDefId, GenericParamId, ModuleDefId,
+ path::{ModPath, Path},
+ per_ns::Namespace,
+ resolver::{HasResolver, Resolver, TypeNs},
+ AssocItemId, AttrDefId, ModuleDefId,
};
-use hir_expand::hygiene::Hygiene;
+use hir_expand::{hygiene::Hygiene, name::Name};
use hir_ty::db::HirDatabase;
use syntax::{ast, AstNode};
use crate::{
- Adt, AssocItem, Const, ConstParam, Enum, ExternCrateDecl, Field, Function, GenericParam, Impl,
- LifetimeParam, Macro, Module, ModuleDef, Static, Struct, Trait, TraitAlias, TypeAlias,
- TypeParam, Union, Variant,
+ Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
+ Field, Function, GenericParam, Impl, LifetimeParam, Macro, Module, ModuleDef, Static, Struct,
+ Trait, TraitAlias, TypeAlias, TypeParam, Union, Variant, VariantDef,
};
pub trait HasAttrs {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner;
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<ModuleDef>;
-}
-
-#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
-pub enum Namespace {
- Types,
- Values,
- Macros,
+ #[doc(hidden)]
+ fn attr_id(self) -> AttrDefId;
}
macro_rules! impl_has_attrs {
@@ -42,13 +31,8 @@ macro_rules! impl_has_attrs {
let def = AttrDefId::$def_id(self.into());
db.attrs_with_owner(def)
}
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- let def = AttrDefId::$def_id(self.into());
- db.attrs(def).docs()
- }
- fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
- let def = AttrDefId::$def_id(self.into());
- resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
+ fn attr_id(self) -> AttrDefId {
+ AttrDefId::$def_id(self.into())
}
}
)*};
@@ -68,6 +52,7 @@ impl_has_attrs![
(Module, ModuleId),
(GenericParam, GenericParamId),
(Impl, ImplId),
+ (ExternCrateDecl, ExternCrateId),
];
macro_rules! impl_has_attrs_enum {
@@ -76,11 +61,8 @@ macro_rules! impl_has_attrs_enum {
fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
$enum::$variant(self).attrs(db)
}
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- $enum::$variant(self).docs(db)
- }
- fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
- $enum::$variant(self).resolve_doc_path(db, link, ns)
+ fn attr_id(self) -> AttrDefId {
+ $enum::$variant(self).attr_id()
}
}
)*};
@@ -97,70 +79,35 @@ impl HasAttrs for AssocItem {
AssocItem::TypeAlias(it) => it.attrs(db),
}
}
-
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- match self {
- AssocItem::Function(it) => it.docs(db),
- AssocItem::Const(it) => it.docs(db),
- AssocItem::TypeAlias(it) => it.docs(db),
- }
- }
-
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<ModuleDef> {
+ fn attr_id(self) -> AttrDefId {
match self {
- AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
- AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
- AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
+ AssocItem::Function(it) => it.attr_id(),
+ AssocItem::Const(it) => it.attr_id(),
+ AssocItem::TypeAlias(it) => it.attr_id(),
}
}
}
-impl HasAttrs for ExternCrateDecl {
- fn attrs(self, db: &dyn HirDatabase) -> AttrsWithOwner {
- let def = AttrDefId::ExternCrateId(self.into());
- db.attrs_with_owner(def)
- }
- fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
- let crate_docs = self.resolved_crate(db)?.root_module().attrs(db).docs().map(String::from);
- let def = AttrDefId::ExternCrateId(self.into());
- let decl_docs = db.attrs(def).docs().map(String::from);
- match (decl_docs, crate_docs) {
- (None, None) => None,
- (Some(decl_docs), None) => Some(decl_docs),
- (None, Some(crate_docs)) => Some(crate_docs),
- (Some(mut decl_docs), Some(crate_docs)) => {
- decl_docs.push('\n');
- decl_docs.push('\n');
- decl_docs += &crate_docs;
- Some(decl_docs)
- }
- }
- .map(Documentation::new)
- }
- fn resolve_doc_path(
- self,
- db: &dyn HirDatabase,
- link: &str,
- ns: Option<Namespace>,
- ) -> Option<ModuleDef> {
- let def = AttrDefId::ExternCrateId(self.into());
- resolve_doc_path(db, def, link, ns).map(ModuleDef::from)
- }
+/// Resolves the item `link` points to in the scope of `def`.
+pub fn resolve_doc_path_on(
+ db: &dyn HirDatabase,
+ def: impl HasAttrs,
+ link: &str,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ // AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
+ // AttrDefId::EnumVariantId(it) => it.parent.resolver(db.upcast()),
+
+ resolve_doc_path_on_(db, link, def.attr_id(), ns)
}
-/// Resolves the item `link` points to in the scope of `def`.
-fn resolve_doc_path(
+fn resolve_doc_path_on_(
db: &dyn HirDatabase,
- def: AttrDefId,
link: &str,
+ attr_id: AttrDefId,
ns: Option<Namespace>,
-) -> Option<ModuleDefId> {
- let resolver = match def {
+) -> Option<DocLinkDef> {
+ let resolver = match attr_id {
AttrDefId::ModuleId(it) => it.resolver(db.upcast()),
AttrDefId::FieldId(it) => it.parent.resolver(db.upcast()),
AttrDefId::AdtId(it) => it.resolver(db.upcast()),
@@ -176,16 +123,110 @@ fn resolve_doc_path(
AttrDefId::UseId(it) => it.resolver(db.upcast()),
AttrDefId::MacroId(it) => it.resolver(db.upcast()),
AttrDefId::ExternCrateId(it) => it.resolver(db.upcast()),
- AttrDefId::GenericParamId(it) => match it {
- GenericParamId::TypeParamId(it) => it.parent(),
- GenericParamId::ConstParamId(it) => it.parent(),
- GenericParamId::LifetimeParamId(it) => it.parent,
+ AttrDefId::GenericParamId(_) => return None,
+ };
+
+ let mut modpath = modpath_from_str(db, link)?;
+
+ let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
+ if resolved.is_none() {
+ let last_name = modpath.pop_segment()?;
+ resolve_assoc_or_field(db, resolver, modpath, last_name, ns)
+ } else {
+ let def = match ns {
+ Some(Namespace::Types) => resolved.take_types(),
+ Some(Namespace::Values) => resolved.take_values(),
+ Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
+ None => resolved.iter_items().next().map(|(it, _)| match it {
+ ItemInNs::Types(it) => it,
+ ItemInNs::Values(it) => it,
+ ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
+ }),
+ };
+ Some(DocLinkDef::ModuleDef(def?.into()))
+ }
+}
+
+fn resolve_assoc_or_field(
+ db: &dyn HirDatabase,
+ resolver: Resolver,
+ path: ModPath,
+ name: Name,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ let path = Path::from_known_path_with_no_generic(path);
+ // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
+ // trait itself.
+ let base_def = resolver.resolve_path_in_type_ns_fully(db.upcast(), &path)?;
+
+ let ty = match base_def {
+ TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
+ TypeNs::GenericParam(_) => {
+ // Even if this generic parameter has some trait bounds, rustdoc doesn't
+ // resolve `name` to trait items.
+ return None;
+ }
+ TypeNs::AdtId(id) | TypeNs::AdtSelfType(id) => Adt::from(id).ty(db),
+ TypeNs::EnumVariantId(id) => {
+ // Enum variants don't have path candidates.
+ let variant = Variant::from(id);
+ return resolve_field(db, variant.into(), name, ns);
+ }
+ TypeNs::TypeAliasId(id) => {
+ let alias = TypeAlias::from(id);
+ if alias.as_assoc_item(db).is_some() {
+ // We don't normalize associated type aliases, so we have nothing to
+ // resolve `name` to.
+ return None;
+ }
+ alias.ty(db)
+ }
+ TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
+ TypeNs::TraitId(id) => {
+ // Doc paths in this context may only resolve to an item of this trait
+ // (i.e. no items of its supertraits), so we need to handle them here
+ // independently of others.
+ return db.trait_data(id).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
+ let def = match *assoc_id {
+ AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
+ AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
+ AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
+ };
+ DocLinkDef::ModuleDef(def)
+ });
+ }
+ TypeNs::TraitAliasId(_) => {
+ // XXX: Do these get resolved?
+ return None;
}
- .resolver(db.upcast()),
};
- let modpath = {
- // FIXME: this is not how we should get a mod path here
+ // FIXME: Resolve associated items here, e.g. `Option::map`. Note that associated items take
+ // precedence over fields.
+
+ let variant_def = match ty.as_adt()? {
+ Adt::Struct(it) => it.into(),
+ Adt::Union(it) => it.into(),
+ Adt::Enum(_) => return None,
+ };
+ resolve_field(db, variant_def, name, ns)
+}
+
+fn resolve_field(
+ db: &dyn HirDatabase,
+ def: VariantDef,
+ name: Name,
+ ns: Option<Namespace>,
+) -> Option<DocLinkDef> {
+ if let Some(Namespace::Types | Namespace::Macros) = ns {
+ return None;
+ }
+ def.fields(db).into_iter().find(|f| f.name(db) == name).map(DocLinkDef::Field)
+}
+
+fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
+ // FIXME: this is not how we should get a mod path here.
+ let try_get_modpath = |link: &str| {
let ast_path = ast::SourceFile::parse(&format!("type T = {link};"))
.syntax_node()
.descendants()
@@ -193,23 +234,20 @@ fn resolve_doc_path(
if ast_path.syntax().text() != link {
return None;
}
- ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())?
+ ModPath::from_src(db.upcast(), ast_path, &Hygiene::new_unhygienic())
};
- let resolved = resolver.resolve_module_path_in_items(db.upcast(), &modpath);
- let resolved = if resolved.is_none() {
- resolver.resolve_module_path_in_trait_assoc_items(db.upcast(), &modpath)?
- } else {
- resolved
- };
- match ns {
- Some(Namespace::Types) => resolved.take_types(),
- Some(Namespace::Values) => resolved.take_values(),
- Some(Namespace::Macros) => resolved.take_macros().map(ModuleDefId::MacroId),
- None => resolved.iter_items().next().map(|it| match it {
- ItemInNs::Types(it) => it,
- ItemInNs::Values(it) => it,
- ItemInNs::Macros(it) => ModuleDefId::MacroId(it),
- }),
+ let full = try_get_modpath(link);
+ if full.is_some() {
+ return full;
}
+
+ // Tuple field names cannot be a part of `ModPath` usually, but rustdoc can
+ // resolve doc paths like `TupleStruct::0`.
+ // FIXME: Find a better way to handle these.
+ let (base, maybe_tuple_field) = link.rsplit_once("::")?;
+ let tuple_field = Name::new_tuple_field(maybe_tuple_field.parse().ok()?);
+ let mut modpath = try_get_modpath(base)?;
+ modpath.push_segment(tuple_field);
+ Some(modpath)
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 80c3bcdca..479138b67 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -43,6 +43,7 @@ diagnostics![
MacroExpansionParseError,
MalformedDerive,
MismatchedArgCount,
+ MismatchedTupleStructPatArgCount,
MissingFields,
MissingMatchArms,
MissingUnsafe,
@@ -172,7 +173,8 @@ pub struct MalformedDerive {
#[derive(Debug)]
pub struct NoSuchField {
- pub field: InFile<AstPtr<ast::RecordExprField>>,
+ pub field: InFile<Either<AstPtr<ast::RecordExprField>, AstPtr<ast::RecordPatField>>>,
+ pub private: bool,
}
#[derive(Debug)]
@@ -183,6 +185,13 @@ pub struct PrivateAssocItem {
}
#[derive(Debug)]
+pub struct MismatchedTupleStructPatArgCount {
+ pub expr_or_pat: InFile<Either<AstPtr<ast::Expr>, AstPtr<ast::Pat>>>,
+ pub expected: usize,
+ pub found: usize,
+}
+
+#[derive(Debug)]
pub struct ExpectedFunction {
pub call: InFile<AstPtr<ast::Expr>>,
pub found: Type,
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 9dfb98e45..ac171026d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -8,7 +8,6 @@ use hir_def::{
type_ref::{TypeBound, TypeRef},
AdtId, GenericDefId,
};
-use hir_expand::name;
use hir_ty::{
display::{
write_bounds_like_dyn_trait_with_prefix, write_visibility, HirDisplay, HirDisplayError,
@@ -19,8 +18,9 @@ use hir_ty::{
use crate::{
Adt, AsAssocItem, AssocItemContainer, Const, ConstParam, Enum, ExternCrateDecl, Field,
- Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, Static, Struct,
- Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam, Union, Variant,
+ Function, GenericParam, HasCrate, HasVisibility, LifetimeParam, Macro, Module, SelfParam,
+ Static, Struct, Trait, TraitAlias, TyBuilder, Type, TypeAlias, TypeOrConstParam, TypeParam,
+ Union, Variant,
};
impl HirDisplay for Function {
@@ -57,37 +57,21 @@ impl HirDisplay for Function {
f.write_char('(')?;
- let write_self_param = |ty: &TypeRef, f: &mut HirFormatter<'_>| match ty {
- TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
- TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
- {
- f.write_char('&')?;
- if let Some(lifetime) = lifetime {
- write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
- }
- if let hir_def::type_ref::Mutability::Mut = mut_ {
- f.write_str("mut ")?;
- }
- f.write_str("self")
- }
- _ => {
- f.write_str("self: ")?;
- ty.hir_fmt(f)
- }
- };
-
let mut first = true;
+ let mut skip_self = 0;
+ if let Some(self_param) = self.self_param(db) {
+ self_param.hir_fmt(f)?;
+ first = false;
+ skip_self = 1;
+ }
+
// FIXME: Use resolved `param.ty` once we no longer discard lifetimes
- for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)) {
+ for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) {
let local = param.as_local(db).map(|it| it.name(db));
if !first {
f.write_str(", ")?;
} else {
first = false;
- if local == Some(name!(self)) {
- write_self_param(type_ref, f)?;
- continue;
- }
}
match local {
Some(name) => write!(f, "{}: ", name.display(f.db.upcast()))?,
@@ -137,6 +121,31 @@ impl HirDisplay for Function {
}
}
+impl HirDisplay for SelfParam {
+ fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
+ let data = f.db.function_data(self.func);
+ let param = data.params.first().unwrap();
+ match &**param {
+ TypeRef::Path(p) if p.is_self_type() => f.write_str("self"),
+ TypeRef::Reference(inner, lifetime, mut_) if matches!(&**inner, TypeRef::Path(p) if p.is_self_type()) =>
+ {
+ f.write_char('&')?;
+ if let Some(lifetime) = lifetime {
+ write!(f, "{} ", lifetime.name.display(f.db.upcast()))?;
+ }
+ if let hir_def::type_ref::Mutability::Mut = mut_ {
+ f.write_str("mut ")?;
+ }
+ f.write_str("self")
+ }
+ ty => {
+ f.write_str("self: ")?;
+ ty.hir_fmt(f)
+ }
+ }
+ }
+}
+
impl HirDisplay for Adt {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
match self {
@@ -357,6 +366,11 @@ fn write_generic_params(
delim(f)?;
write!(f, "const {}: ", name.display(f.db.upcast()))?;
c.ty.hir_fmt(f)?;
+
+ if let Some(default) = &c.default {
+ f.write_str(" = ")?;
+ write!(f, "{}", default.display(f.db.upcast()))?;
+ }
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index bf041b61f..b215ed38f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -63,12 +63,13 @@ use hir_ty::{
all_super_traits, autoderef,
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
+ known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
primitive::UintTy,
traits::FnTrait,
- AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
+ AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId,
WhereClause,
@@ -87,13 +88,14 @@ use triomphe::Arc;
use crate::db::{DefDatabase, HirDatabase};
pub use crate::{
- attrs::{HasAttrs, Namespace},
+ attrs::{resolve_doc_path_on, HasAttrs},
diagnostics::{
AnyDiagnostic, BreakOutsideOfLoop, CaseType, ExpectedFunction, InactiveCode,
IncoherentImpl, IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError,
- MacroExpansionParseError, MalformedDerive, MismatchedArgCount, MissingFields,
- MissingMatchArms, MissingUnsafe, MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem,
- PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
+ MacroExpansionParseError, MalformedDerive, MismatchedArgCount,
+ MismatchedTupleStructPatArgCount, MissingFields, MissingMatchArms, MissingUnsafe,
+ MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField,
+ ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel,
UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField,
UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule,
UnresolvedProcMacro, UnusedMut,
@@ -114,13 +116,14 @@ pub use crate::{
pub use {
cfg::{CfgAtom, CfgExpr, CfgOptions},
hir_def::{
- attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation},
+ attr::{builtin::AttributeTemplate, AttrSourceMap, Attrs, AttrsWithOwner},
data::adt::StructKind,
find_path::PrefixKind,
import_map,
lang_item::LangItem,
nameres::{DefMap, ModuleSource},
path::{ModPath, PathKind},
+ per_ns::Namespace,
type_ref::{Mutability, TypeRef},
visibility::Visibility,
// FIXME: This is here since some queries take it as input that are used
@@ -128,7 +131,7 @@ pub use {
{AdtId, ModuleDefId},
},
hir_expand::{
- attrs::Attr,
+ attrs::{Attr, AttrId},
name::{known, Name},
ExpandResult, HirFileId, InFile, MacroFile, Origin,
},
@@ -561,8 +564,8 @@ impl Module {
emit_def_diagnostic(db, acc, diag);
}
- for decl in self.declarations(db) {
- match decl {
+ for def in self.declarations(db) {
+ match def {
ModuleDef::Module(m) => {
// Only add diagnostics from inline modules
if def_map[m.id.local_id].origin.is_inline() {
@@ -573,7 +576,7 @@ impl Module {
for diag in db.trait_data_with_diagnostics(t.id).1.iter() {
emit_def_diagnostic(db, acc, diag);
}
- acc.extend(decl.diagnostics(db))
+ acc.extend(def.diagnostics(db))
}
ModuleDef::Adt(adt) => {
match adt {
@@ -597,10 +600,10 @@ impl Module {
}
}
}
- acc.extend(decl.diagnostics(db))
+ acc.extend(def.diagnostics(db))
}
ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m),
- _ => acc.extend(decl.diagnostics(db)),
+ _ => acc.extend(def.diagnostics(db)),
}
}
self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m));
@@ -719,20 +722,18 @@ fn emit_def_diagnostic_(
) {
match diag {
DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => {
- let decl = declaration.to_node(db.upcast());
+ let decl = declaration.to_ptr(db.upcast());
acc.push(
UnresolvedModule {
- decl: InFile::new(declaration.file_id, AstPtr::new(&decl)),
+ decl: InFile::new(declaration.file_id, decl),
candidates: candidates.clone(),
}
.into(),
)
}
DefDiagnosticKind::UnresolvedExternCrate { ast } => {
- let item = ast.to_node(db.upcast());
- acc.push(
- UnresolvedExternCrate { decl: InFile::new(ast.file_id, AstPtr::new(&item)) }.into(),
- );
+ let item = ast.to_ptr(db.upcast());
+ acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into());
}
DefDiagnosticKind::UnresolvedImport { id, index } => {
@@ -747,14 +748,10 @@ fn emit_def_diagnostic_(
}
DefDiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
- let item = ast.to_node(db.upcast());
+ let item = ast.to_ptr(db.upcast());
acc.push(
- InactiveCode {
- node: ast.with_value(SyntaxNodePtr::new(&item).into()),
- cfg: cfg.clone(),
- opts: opts.clone(),
- }
- .into(),
+ InactiveCode { node: ast.with_value(item), cfg: cfg.clone(), opts: opts.clone() }
+ .into(),
);
}
DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => {
@@ -1273,7 +1270,7 @@ impl Adt {
.fill(|x| {
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
- ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+ ParamKind::Type => r.cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
@@ -1450,6 +1447,7 @@ impl DefWithBody {
}
pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) {
+ db.unwind_if_cancelled();
let krate = self.module(db).id.krate();
let (body, source_map) = db.body_with_source_map(self.into());
@@ -1505,11 +1503,19 @@ impl DefWithBody {
let infer = db.infer(self.into());
let source_map = Lazy::new(|| db.body_with_source_map(self.into()).1);
let expr_syntax = |expr| source_map.expr_syntax(expr).expect("unexpected synthetic");
+ let pat_syntax = |pat| source_map.pat_syntax(pat).expect("unexpected synthetic");
for d in &infer.diagnostics {
match d {
- &hir_ty::InferenceDiagnostic::NoSuchField { expr } => {
- let field = source_map.field_syntax(expr);
- acc.push(NoSuchField { field }.into())
+ &hir_ty::InferenceDiagnostic::NoSuchField { field: expr, private } => {
+ let expr_or_pat = match expr {
+ ExprOrPatId::ExprId(expr) => {
+ source_map.field_syntax(expr).map(Either::Left)
+ }
+ ExprOrPatId::PatId(pat) => {
+ source_map.pat_field_syntax(pat).map(Either::Right)
+ }
+ };
+ acc.push(NoSuchField { field: expr_or_pat, private }.into())
}
&hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => {
acc.push(
@@ -1525,10 +1531,7 @@ impl DefWithBody {
&hir_ty::InferenceDiagnostic::PrivateAssocItem { id, item } => {
let expr_or_pat = match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
- ExprOrPatId::PatId(pat) => source_map
- .pat_syntax(pat)
- .expect("unexpected synthetic")
- .map(Either::Right),
+ ExprOrPatId::PatId(pat) => pat_syntax(pat).map(Either::Right),
};
let item = item.into();
acc.push(PrivateAssocItem { expr_or_pat, item }.into())
@@ -1600,6 +1603,23 @@ impl DefWithBody {
.into(),
)
}
+ &hir_ty::InferenceDiagnostic::MismatchedTupleStructPatArgCount {
+ pat,
+ expected,
+ found,
+ } => {
+ let expr_or_pat = match pat {
+ ExprOrPatId::ExprId(expr) => expr_syntax(expr).map(Either::Left),
+ ExprOrPatId::PatId(pat) => source_map
+ .pat_syntax(pat)
+ .expect("unexpected synthetic")
+ .map(|it| it.unwrap_left())
+ .map(Either::Right),
+ };
+ acc.push(
+ MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into(),
+ )
+ }
}
}
for (pat_or_expr, mismatch) in infer.type_mismatches() {
@@ -2096,14 +2116,6 @@ impl SelfParam {
.unwrap_or(Access::Owned)
}
- pub fn display(self, db: &dyn HirDatabase) -> &'static str {
- match self.access(db) {
- Access::Shared => "&self",
- Access::Exclusive => "&mut self",
- Access::Owned => "self",
- }
- }
-
pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::SelfParam>> {
let InFile { file_id, value } = Function::from(self.func).source(db)?;
value
@@ -3142,12 +3154,8 @@ impl TypeParam {
}
pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
- let params = db.generic_defaults(self.id.parent());
- let local_idx = hir_ty::param_idx(db, self.id.into())?;
+ let ty = generic_arg_from_param(db, self.id.into())?;
let resolver = self.id.parent().resolver(db.upcast());
- let ty = params.get(local_idx)?.clone();
- let subst = TyBuilder::placeholder_subst(db, self.id.parent());
- let ty = ty.substitute(Interner, &subst);
match ty.data(Interner) {
GenericArgData::Ty(it) => {
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
@@ -3209,6 +3217,19 @@ impl ConstParam {
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::new(db, self.id.parent(), db.const_param_ty(self.id))
}
+
+ pub fn default(self, db: &dyn HirDatabase) -> Option<ast::ConstArg> {
+ let arg = generic_arg_from_param(db, self.id.into())?;
+ known_const_to_ast(arg.constant(Interner)?, db)
+ }
+}
+
+fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option<GenericArg> {
+ let params = db.generic_defaults(id.parent);
+ let local_idx = hir_ty::param_idx(db, id)?;
+ let ty = params.get(local_idx)?.clone();
+ let subst = TyBuilder::placeholder_subst(db, id.parent);
+ Some(ty.substitute(Interner, &subst))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -3716,7 +3737,7 @@ impl Type {
.fill(|x| {
let r = it.next().unwrap();
match x {
- ParamKind::Type => GenericArgData::Ty(r).intern(Interner),
+ ParamKind::Type => r.cast(Interner),
ParamKind::Const(ty) => {
// FIXME: this code is not covered in tests.
unknown_const_as_generic(ty.clone())
@@ -3749,9 +3770,7 @@ impl Type {
.fill(|it| {
// FIXME: this code is not covered in tests.
match it {
- ParamKind::Type => {
- GenericArgData::Ty(args.next().unwrap().ty.clone()).intern(Interner)
- }
+ ParamKind::Type => args.next().unwrap().ty.clone().cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
}
})
@@ -4414,14 +4433,13 @@ impl Callable {
Other => CallableKind::Other,
}
}
- pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(ast::SelfParam, Type)> {
+ pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<(SelfParam, Type)> {
let func = match self.callee {
Callee::Def(CallableDefId::FunctionId(it)) if self.is_bound_method => it,
_ => return None,
};
- let src = func.lookup(db.upcast()).source(db.upcast());
- let param_list = src.value.param_list()?;
- Some((param_list.self_param()?, self.ty.derived(self.sig.params()[0].clone())))
+ let func = Function { id: func };
+ Some((func.self_param(db)?, self.ty.derived(self.sig.params()[0].clone())))
}
pub fn n_params(&self) -> usize {
self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
@@ -4844,3 +4862,10 @@ pub enum ItemContainer {
ExternBlock(),
Crate(CrateId),
}
+
+/// Subset of `ide_db::Definition` that doc links can resolve to.
+pub enum DocLinkDef {
+ ModuleDef(ModuleDef),
+ Field(Field),
+ SelfType(Trait),
+}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index e99d2984c..a42e0978b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -127,148 +127,24 @@ impl<DB> fmt::Debug for Semantics<'_, DB> {
}
}
+impl<'db, DB> ops::Deref for Semantics<'db, DB> {
+ type Target = SemanticsImpl<'db>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.imp
+ }
+}
+
impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn new(db: &DB) -> Semantics<'_, DB> {
let impl_ = SemanticsImpl::new(db);
Semantics { db, imp: impl_ }
}
- pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
- self.imp.parse(file_id)
- }
-
- pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
- self.imp.parse_or_expand(file_id)
- }
-
- pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
- self.imp.expand(macro_call)
- }
-
- /// If `item` has an attribute macro attached to it, expands it.
- pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
- self.imp.expand_attr_macro(item)
- }
-
- pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
- self.imp.expand_derive_as_pseudo_attr_macro(attr)
- }
-
- pub fn resolve_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<Option<Macro>>> {
- self.imp.resolve_derive_macro(derive)
- }
-
- pub fn expand_derive_macro(&self, derive: &ast::Attr) -> Option<Vec<SyntaxNode>> {
- self.imp.expand_derive_macro(derive)
- }
-
- pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
- self.imp.is_attr_macro_call(item)
- }
-
- pub fn is_derive_annotated(&self, item: &ast::Adt) -> bool {
- self.imp.is_derive_annotated(item)
- }
-
- pub fn speculative_expand(
- &self,
- actual_macro_call: &ast::MacroCall,
- speculative_args: &ast::TokenTree,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand(actual_macro_call, speculative_args, token_to_map)
- }
-
- pub fn speculative_expand_attr_macro(
- &self,
- actual_macro_call: &ast::Item,
- speculative_args: &ast::Item,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand_attr(actual_macro_call, speculative_args, token_to_map)
- }
-
- pub fn speculative_expand_derive_as_pseudo_attr_macro(
- &self,
- actual_macro_call: &ast::Attr,
- speculative_args: &ast::Attr,
- token_to_map: SyntaxToken,
- ) -> Option<(SyntaxNode, SyntaxToken)> {
- self.imp.speculative_expand_derive_as_pseudo_attr_macro(
- actual_macro_call,
- speculative_args,
- token_to_map,
- )
- }
-
- /// Descend the token into macrocalls to its first mapped counterpart.
- pub fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
- self.imp.descend_into_macros_single(token)
- }
-
- /// Descend the token into macrocalls to all its mapped counterparts.
- pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros(token)
- }
-
- /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
- ///
- /// Returns the original non descended token if none of the mapped counterparts have the same text.
- pub fn descend_into_macros_with_same_text(
- &self,
- token: SyntaxToken,
- ) -> SmallVec<[SyntaxToken; 1]> {
- self.imp.descend_into_macros_with_same_text(token)
- }
-
- pub fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
- self.imp.descend_into_macros_with_kind_preference(token)
- }
-
- /// Maps a node down by mapping its first and last token down.
- pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
- self.imp.descend_node_into_attributes(node)
- }
-
- /// Search for a definition's source and cache its syntax tree
- pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
- where
- Def::Ast: AstNode,
- {
- self.imp.source(def)
- }
-
pub fn hir_file_for(&self, syntax_node: &SyntaxNode) -> HirFileId {
self.imp.find_file(syntax_node).file_id
}
- /// Attempts to map the node out of macro expanded files returning the original file range.
- /// If upmapping is not possible, this will fall back to the range of the macro call of the
- /// macro file the node resides in.
- pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
- self.imp.original_range(node)
- }
-
- /// Attempts to map the node out of macro expanded files returning the original file range.
- pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
- self.imp.original_range_opt(node)
- }
-
- /// Attempts to map the node out of macro expanded files.
- /// This only work for attribute expansions, as other ones do not have nodes as input.
- pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
- self.imp.original_ast_node(node)
- }
- /// Attempts to map the node out of macro expanded files.
- /// This only work for attribute expansions, as other ones do not have nodes as input.
- pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
- self.imp.original_syntax_node(node)
- }
-
- pub fn diagnostics_display_range(&self, diagnostics: InFile<SyntaxNodePtr>) -> FileRange {
- self.imp.diagnostics_display_range(diagnostics)
- }
-
pub fn token_ancestors_with_macros(
&self,
token: SyntaxToken,
@@ -276,19 +152,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it))
}
- /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
- pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
- self.imp.ancestors_with_macros(node)
- }
-
- pub fn ancestors_at_offset_with_macros(
- &self,
- node: &SyntaxNode,
- offset: TextSize,
- ) -> impl Iterator<Item = SyntaxNode> + '_ {
- self.imp.ancestors_at_offset_with_macros(node, offset)
- }
-
/// Find an AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
/// search up until it is of the target AstNode type
pub fn find_node_at_offset_with_macros<N: AstNode>(
@@ -319,53 +182,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
}
- pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
- self.imp.resolve_lifetime_param(lifetime)
- }
-
- pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
- self.imp.resolve_label(lifetime)
- }
-
- pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
- self.imp.resolve_type(ty)
- }
-
- pub fn resolve_trait(&self, trait_: &ast::Path) -> Option<Trait> {
- self.imp.resolve_trait(trait_)
- }
-
- pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
- self.imp.expr_adjustments(expr)
- }
-
- pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
- self.imp.type_of_expr(expr)
- }
-
- pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
- self.imp.type_of_pat(pat)
- }
-
- /// It also includes the changes that binding mode makes in the type. For example in
- /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
- /// of this function is `&mut Option<T>`
- pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
- self.imp.type_of_binding_in_pat(pat)
- }
-
- pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
- self.imp.type_of_self(param)
- }
-
- pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
- self.imp.pattern_adjustments(pat)
- }
-
- pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
- self.imp.binding_mode_of_pat(pat)
- }
-
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
self.imp.resolve_method_call(call).map(Function::from)
}
@@ -400,61 +216,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.resolve_try_expr(try_expr).map(Function::from)
}
- pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
- self.imp.resolve_method_call_as_callable(call)
- }
-
- pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
- self.imp.resolve_field(field)
- }
-
- pub fn resolve_record_field(
- &self,
- field: &ast::RecordExprField,
- ) -> Option<(Field, Option<Local>, Type)> {
- self.imp.resolve_record_field(field)
- }
-
- pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
- self.imp.resolve_record_pat_field(field)
- }
-
- pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
- self.imp.resolve_macro_call(macro_call)
- }
-
- pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
- self.imp.is_unsafe_macro_call(macro_call)
- }
-
- pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
- self.imp.resolve_attr_macro_call(item)
- }
-
- pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
- self.imp.resolve_path(path)
- }
-
pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option<VariantDef> {
self.imp.resolve_variant(record_lit).map(VariantDef::from)
}
- pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
- self.imp.resolve_bind_pat_to_const(pat)
- }
-
- pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
- self.imp.record_literal_missing_fields(literal)
- }
-
- pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
- self.imp.record_pattern_missing_fields(pattern)
- }
-
- pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
- self.imp.to_def(src)
- }
-
pub fn to_module_def(&self, file: FileId) -> Option<Module> {
self.imp.to_module_def(file).next()
}
@@ -462,39 +227,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn to_module_defs(&self, file: FileId) -> impl Iterator<Item = Module> {
self.imp.to_module_def(file)
}
-
- pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
- self.imp.scope(node)
- }
-
- pub fn scope_at_offset(
- &self,
- node: &SyntaxNode,
- offset: TextSize,
- ) -> Option<SemanticsScope<'db>> {
- self.imp.scope_at_offset(node, offset)
- }
-
- pub fn assert_contains_node(&self, node: &SyntaxNode) {
- self.imp.assert_contains_node(node)
- }
-
- pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
- self.imp.is_unsafe_method_call(method_call_expr)
- }
-
- pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
- self.imp.is_unsafe_ref_expr(ref_expr)
- }
-
- pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
- self.imp.is_unsafe_ident_pat(ident_pat)
- }
-
- /// Returns `true` if the `node` is inside an `unsafe` context.
- pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
- self.imp.is_inside_unsafe(expr)
- }
}
impl<'db> SemanticsImpl<'db> {
@@ -508,32 +240,33 @@ impl<'db> SemanticsImpl<'db> {
}
}
- fn parse(&self, file_id: FileId) -> ast::SourceFile {
+ pub fn parse(&self, file_id: FileId) -> ast::SourceFile {
let tree = self.db.parse(file_id).tree();
self.cache(tree.syntax().clone(), file_id.into());
tree
}
- fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
+ pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode {
let node = self.db.parse_or_expand(file_id);
self.cache(node.clone(), file_id);
node
}
- fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
+ pub fn expand(&self, macro_call: &ast::MacroCall) -> Option<SyntaxNode> {
let sa = self.analyze_no_infer(macro_call.syntax())?;
let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?;
let node = self.parse_or_expand(file_id);
Some(node)
}
- fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
+ /// If `item` has an attribute macro attached to it, expands it.
+ pub fn expand_attr_macro(&self, item: &ast::Item) -> Option<SyntaxNode> {
let src = self.wrap_node_infile(item.clone());
let macro_call_id = self.with_ctx(|ctx| ctx.item_to_macro_call(src))?;
Some(self.parse_or_expand(macro_call_id.as_file()))
}
- fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
+ pub fn expand_derive_as_pseudo_attr_macro(&self, attr: &ast::Attr) -> Option<SyntaxNode> {
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let src = self.wrap_node_infile(attr.clone());
let call_id = self.with_ctx(|ctx| {
@@ -542,7 +275,7 @@ impl<'db> SemanticsImpl<'db> {
Some(self.parse_or_expand(call_id.as_file()))
}
- fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
+ pub fn resolve_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<Option<Macro>>> {
let calls = self.derive_macro_calls(attr)?;
self.with_ctx(|ctx| {
Some(
@@ -556,7 +289,7 @@ impl<'db> SemanticsImpl<'db> {
})
}
- fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> {
+ pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option<Vec<SyntaxNode>> {
let res: Vec<_> = self
.derive_macro_calls(attr)?
.into_iter()
@@ -581,19 +314,21 @@ impl<'db> SemanticsImpl<'db> {
})
}
- fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
+ pub fn is_derive_annotated(&self, adt: &ast::Adt) -> bool {
let file_id = self.find_file(adt.syntax()).file_id;
let adt = InFile::new(file_id, adt);
self.with_ctx(|ctx| ctx.has_derives(adt))
}
- fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
+ pub fn is_attr_macro_call(&self, item: &ast::Item) -> bool {
let file_id = self.find_file(item.syntax()).file_id;
let src = InFile::new(file_id, item.clone());
self.with_ctx(|ctx| ctx.item_to_macro_call(src).is_some())
}
- fn speculative_expand(
+ /// Expand the macro call with a different token tree, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
+ pub fn speculative_expand(
&self,
actual_macro_call: &ast::MacroCall,
speculative_args: &ast::TokenTree,
@@ -606,7 +341,7 @@ impl<'db> SemanticsImpl<'db> {
let macro_call_id = macro_call.as_call_id(self.db.upcast(), krate, |path| {
resolver
.resolve_path_as_macro(self.db.upcast(), &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(self.db.upcast(), it))
+ .map(|(it, _)| macro_id_to_def_id(self.db.upcast(), it))
})?;
hir_expand::db::expand_speculative(
self.db.upcast(),
@@ -616,7 +351,9 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn speculative_expand_attr(
+ /// Expand the macro call with a different item as the input, mapping the `token_to_map` down into the
+ /// expansion. `token_to_map` should be a token from the `speculative args` node.
+ pub fn speculative_expand_attr_macro(
&self,
actual_macro_call: &ast::Item,
speculative_args: &ast::Item,
@@ -632,7 +369,7 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn speculative_expand_derive_as_pseudo_attr_macro(
+ pub fn speculative_expand_derive_as_pseudo_attr_macro(
&self,
actual_macro_call: &ast::Attr,
speculative_args: &ast::Attr,
@@ -651,8 +388,9 @@ impl<'db> SemanticsImpl<'db> {
)
}
- // This might not be the correct way to do this, but it works for now
- fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+ /// Maps a node down by mapping its first and last token down.
+ pub fn descend_node_into_attributes<N: AstNode>(&self, node: N) -> SmallVec<[N; 1]> {
+ // This might not be the correct way to do this, but it works for now
let mut res = smallvec![];
let tokens = (|| {
let first = skip_trivia_token(node.syntax().first_token()?, Direction::Next)?;
@@ -665,7 +403,7 @@ impl<'db> SemanticsImpl<'db> {
};
if first == last {
- self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(first, 0.into(), &mut |InFile { value, .. }| {
if let Some(node) = value.parent_ancestors().find_map(N::cast) {
res.push(node)
}
@@ -674,7 +412,7 @@ impl<'db> SemanticsImpl<'db> {
} else {
// Descend first and last token, then zip them to look for the node they belong to
let mut scratch: SmallVec<[_; 1]> = smallvec![];
- self.descend_into_macros_impl(first, &mut |token| {
+ self.descend_into_macros_impl(first, 0.into(), &mut |token| {
scratch.push(token);
false
});
@@ -682,6 +420,7 @@ impl<'db> SemanticsImpl<'db> {
let mut scratch = scratch.into_iter();
self.descend_into_macros_impl(
last,
+ 0.into(),
&mut |InFile { value: last, file_id: last_fid }| {
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
if first_fid == last_fid {
@@ -705,19 +444,33 @@ impl<'db> SemanticsImpl<'db> {
res
}
- fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+ /// Descend the token into its macro call if it is part of one, returning the tokens in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SmallVec<[SyntaxToken; 1]> {
let mut res = smallvec![];
- self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
res.push(value);
false
});
res
}
- fn descend_into_macros_with_same_text(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+ /// Descend the token into macrocalls to all its mapped counterparts that have the same text as the input token.
+ ///
+ /// Returns the original non descended token if none of the mapped counterparts have the same text.
+ pub fn descend_into_macros_with_same_text(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SmallVec<[SyntaxToken; 1]> {
let text = token.text();
let mut res = smallvec![];
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
if value.text() == text {
res.push(value);
}
@@ -729,7 +482,11 @@ impl<'db> SemanticsImpl<'db> {
res
}
- fn descend_into_macros_with_kind_preference(&self, token: SyntaxToken) -> SyntaxToken {
+ pub fn descend_into_macros_with_kind_preference(
+ &self,
+ token: SyntaxToken,
+ offset: TextSize,
+ ) -> SyntaxToken {
let fetch_kind = |token: &SyntaxToken| match token.parent() {
Some(node) => match node.kind() {
kind @ (SyntaxKind::NAME | SyntaxKind::NAME_REF) => {
@@ -741,7 +498,7 @@ impl<'db> SemanticsImpl<'db> {
};
let preferred_kind = fetch_kind(&token);
let mut res = None;
- self.descend_into_macros_impl(token.clone(), &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token.clone(), offset, &mut |InFile { value, .. }| {
if fetch_kind(&value) == preferred_kind {
res = Some(value);
true
@@ -755,9 +512,12 @@ impl<'db> SemanticsImpl<'db> {
res.unwrap_or(token)
}
- fn descend_into_macros_single(&self, token: SyntaxToken) -> SyntaxToken {
+ /// Descend the token into its macro call if it is part of one, returning the token in the
+ /// expansion that it is associated with. If `offset` points into the token's range, it will
+ /// be considered for the mapping in case of inline format args.
+ pub fn descend_into_macros_single(&self, token: SyntaxToken, offset: TextSize) -> SyntaxToken {
let mut res = token.clone();
- self.descend_into_macros_impl(token, &mut |InFile { value, .. }| {
+ self.descend_into_macros_impl(token, offset, &mut |InFile { value, .. }| {
res = value;
true
});
@@ -767,9 +527,13 @@ impl<'db> SemanticsImpl<'db> {
fn descend_into_macros_impl(
&self,
token: SyntaxToken,
+ // FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
+ // mapping, specifically for node downmapping
+ offset: TextSize,
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
) {
let _p = profile::span("descend_into_macros");
+ let relative_token_offset = token.text_range().start().checked_sub(offset);
let parent = match token.parent() {
Some(it) => it,
None => return,
@@ -796,7 +560,12 @@ impl<'db> SemanticsImpl<'db> {
self.cache(value, file_id);
}
- let mapped_tokens = expansion_info.map_token_down(self.db.upcast(), item, token)?;
+ let mapped_tokens = expansion_info.map_token_down(
+ self.db.upcast(),
+ item,
+ token,
+ relative_token_offset,
+ )?;
let len = stack.len();
// requeue the tokens we got from mapping our current token down
@@ -943,7 +712,7 @@ impl<'db> SemanticsImpl<'db> {
offset: TextSize,
) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
node.token_at_offset(offset)
- .map(move |token| self.descend_into_macros(token))
+ .map(move |token| self.descend_into_macros(token, offset))
.map(|descendants| {
descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
})
@@ -956,17 +725,23 @@ impl<'db> SemanticsImpl<'db> {
})
}
- fn original_range(&self, node: &SyntaxNode) -> FileRange {
+ /// Attempts to map the node out of macro expanded files returning the original file range.
+ /// If upmapping is not possible, this will fall back to the range of the macro call of the
+ /// macro file the node resides in.
+ pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
let node = self.find_file(node);
node.original_file_range(self.db.upcast())
}
- fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
+ /// Attempts to map the node out of macro expanded files returning the original file range.
+ pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
let node = self.find_file(node);
node.original_file_range_opt(self.db.upcast())
}
- fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
+ /// Attempts to map the node out of macro expanded files.
+ /// This only work for attribute expansions, as other ones do not have nodes as input.
+ pub fn original_ast_node<N: AstNode>(&self, node: N) -> Option<N> {
self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(
|InFile { file_id, value }| {
self.cache(find_root(value.syntax()), file_id);
@@ -975,7 +750,9 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
+ /// Attempts to map the node out of macro expanded files.
+ /// This only work for attribute expansions, as other ones do not have nodes as input.
+ pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
let InFile { file_id, .. } = self.find_file(node);
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
|InFile { file_id, value }| {
@@ -985,7 +762,7 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
+ pub fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
let root = self.parse_or_expand(src.file_id);
let node = src.map(|it| it.to_node(&root));
node.as_ref().original_file_range(self.db.upcast())
@@ -998,7 +775,8 @@ impl<'db> SemanticsImpl<'db> {
token.parent().into_iter().flat_map(move |parent| self.ancestors_with_macros(parent))
}
- fn ancestors_with_macros(
+ /// Iterates the ancestors of the given node, climbing up macro expansions while doing so.
+ pub fn ancestors_with_macros(
&self,
node: SyntaxNode,
) -> impl Iterator<Item = SyntaxNode> + Clone + '_ {
@@ -1016,7 +794,7 @@ impl<'db> SemanticsImpl<'db> {
.map(|it| it.value)
}
- fn ancestors_at_offset_with_macros(
+ pub fn ancestors_at_offset_with_macros(
&self,
node: &SyntaxNode,
offset: TextSize,
@@ -1026,7 +804,7 @@ impl<'db> SemanticsImpl<'db> {
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
}
- fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
+ pub fn resolve_lifetime_param(&self, lifetime: &ast::Lifetime) -> Option<LifetimeParam> {
let text = lifetime.text();
let lifetime_param = lifetime.syntax().ancestors().find_map(|syn| {
let gpl = ast::AnyHasGenericParams::cast(syn)?.generic_param_list()?;
@@ -1037,7 +815,7 @@ impl<'db> SemanticsImpl<'db> {
ToDef::to_def(self, src)
}
- fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
+ pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
let text = lifetime.text();
let label = lifetime.syntax().ancestors().find_map(|syn| {
let label = match_ast! {
@@ -1059,7 +837,7 @@ impl<'db> SemanticsImpl<'db> {
ToDef::to_def(self, src)
}
- fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
+ pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
let analyze = self.analyze(ty.syntax())?;
let ctx = LowerCtx::with_file_id(self.db.upcast(), analyze.file_id);
let ty = hir_ty::TyLoweringContext::new(
@@ -1071,7 +849,7 @@ impl<'db> SemanticsImpl<'db> {
Some(Type::new_with_resolver(self.db, &analyze.resolver, ty))
}
- fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
+ pub fn resolve_trait(&self, path: &ast::Path) -> Option<Trait> {
let analyze = self.analyze(path.syntax())?;
let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id);
let ctx = LowerCtx::with_hygiene(self.db.upcast(), &hygiene);
@@ -1082,7 +860,7 @@ impl<'db> SemanticsImpl<'db> {
}
}
- fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
+ pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option<Vec<Adjustment>> {
let mutability = |m| match m {
hir_ty::Mutability::Not => Mutability::Shared,
hir_ty::Mutability::Mut => Mutability::Mut,
@@ -1126,33 +904,36 @@ impl<'db> SemanticsImpl<'db> {
})
}
- fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
+ pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
self.analyze(expr.syntax())?
.type_of_expr(self.db, expr)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
- fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
+ pub fn type_of_pat(&self, pat: &ast::Pat) -> Option<TypeInfo> {
self.analyze(pat.syntax())?
.type_of_pat(self.db, pat)
.map(|(ty, coerced)| TypeInfo { original: ty, adjusted: coerced })
}
- fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
+ /// It also includes the changes that binding mode makes in the type. For example in
+ /// `let ref x @ Some(_) = None` the result of `type_of_pat` is `Option<T>` but the result
+ /// of this function is `&mut Option<T>`
+ pub fn type_of_binding_in_pat(&self, pat: &ast::IdentPat) -> Option<Type> {
self.analyze(pat.syntax())?.type_of_binding_in_pat(self.db, pat)
}
- fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
+ pub fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.analyze(param.syntax())?.type_of_self(self.db, param)
}
- fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
+ pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
self.analyze(pat.syntax())
.and_then(|it| it.pattern_adjustments(self.db, pat))
.unwrap_or_default()
}
- fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
+ pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
}
@@ -1187,32 +968,32 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
}
- fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
+ pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
}
- fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
+ pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<Field> {
self.analyze(field.syntax())?.resolve_field(self.db, field)
}
- fn resolve_record_field(
+ pub fn resolve_record_field(
&self,
field: &ast::RecordExprField,
) -> Option<(Field, Option<Local>, Type)> {
self.analyze(field.syntax())?.resolve_record_field(self.db, field)
}
- fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
+ pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
}
- fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
+ pub fn resolve_macro_call(&self, macro_call: &ast::MacroCall) -> Option<Macro> {
let sa = self.analyze(macro_call.syntax())?;
let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call);
sa.resolve_macro_call(self.db, macro_call)
}
- fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
+ pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
let sa = match self.analyze(macro_call.syntax()) {
Some(it) => it,
None => return false,
@@ -1221,7 +1002,7 @@ impl<'db> SemanticsImpl<'db> {
sa.is_unsafe_macro_call(self.db, macro_call)
}
- fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
+ pub fn resolve_attr_macro_call(&self, item: &ast::Item) -> Option<Macro> {
let item_in_file = self.wrap_node_infile(item.clone());
let id = self.with_ctx(|ctx| {
let macro_call_id = ctx.item_to_macro_call(item_in_file)?;
@@ -1230,7 +1011,7 @@ impl<'db> SemanticsImpl<'db> {
Some(Macro { id })
}
- fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
+ pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
self.analyze(path.syntax())?.resolve_path(self.db, path)
}
@@ -1238,17 +1019,17 @@ impl<'db> SemanticsImpl<'db> {
self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit)
}
- fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
+ pub fn resolve_bind_pat_to_const(&self, pat: &ast::IdentPat) -> Option<ModuleDef> {
self.analyze(pat.syntax())?.resolve_bind_pat_to_const(self.db, pat)
}
- fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
+ pub fn record_literal_missing_fields(&self, literal: &ast::RecordExpr) -> Vec<(Field, Type)> {
self.analyze(literal.syntax())
.and_then(|it| it.record_literal_missing_fields(self.db, literal))
.unwrap_or_default()
}
- fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
+ pub fn record_pattern_missing_fields(&self, pattern: &ast::RecordPat) -> Vec<(Field, Type)> {
self.analyze(pattern.syntax())
.and_then(|it| it.record_pattern_missing_fields(self.db, pattern))
.unwrap_or_default()
@@ -1260,7 +1041,7 @@ impl<'db> SemanticsImpl<'db> {
f(&mut ctx)
}
- fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
+ pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
let src = self.find_file(src.syntax()).with_value(src).cloned();
T::to_def(self, src)
}
@@ -1269,7 +1050,7 @@ impl<'db> SemanticsImpl<'db> {
self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
}
- fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
+ pub fn scope(&self, node: &SyntaxNode) -> Option<SemanticsScope<'db>> {
self.analyze_no_infer(node).map(|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
db: self.db,
file_id,
@@ -1277,7 +1058,11 @@ impl<'db> SemanticsImpl<'db> {
})
}
- fn scope_at_offset(&self, node: &SyntaxNode, offset: TextSize) -> Option<SemanticsScope<'db>> {
+ pub fn scope_at_offset(
+ &self,
+ node: &SyntaxNode,
+ offset: TextSize,
+ ) -> Option<SemanticsScope<'db>> {
self.analyze_with_offset_no_infer(node, offset).map(
|SourceAnalyzer { file_id, resolver, .. }| SemanticsScope {
db: self.db,
@@ -1287,7 +1072,8 @@ impl<'db> SemanticsImpl<'db> {
)
}
- fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
+ /// Search for a definition's source and cache its syntax tree
+ pub fn source<Def: HasSource>(&self, def: Def) -> Option<InFile<Def::Ast>>
where
Def::Ast: AstNode,
{
@@ -1352,7 +1138,7 @@ impl<'db> SemanticsImpl<'db> {
assert!(prev == None || prev == Some(file_id))
}
- fn assert_contains_node(&self, node: &SyntaxNode) {
+ pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.find_file(node);
}
@@ -1388,7 +1174,7 @@ impl<'db> SemanticsImpl<'db> {
InFile::new(file_id, node)
}
- fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
+ pub fn is_unsafe_method_call(&self, method_call_expr: &ast::MethodCallExpr) -> bool {
method_call_expr
.receiver()
.and_then(|expr| {
@@ -1411,7 +1197,7 @@ impl<'db> SemanticsImpl<'db> {
.unwrap_or(false)
}
- fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+ pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
ref_expr
.expr()
.and_then(|expr| {
@@ -1430,7 +1216,7 @@ impl<'db> SemanticsImpl<'db> {
// more than it should with the current implementation.
}
- fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+ pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
if ident_pat.ref_token().is_none() {
return false;
}
@@ -1473,7 +1259,8 @@ impl<'db> SemanticsImpl<'db> {
.unwrap_or(false)
}
- fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
+ /// Returns `true` if the `node` is inside an `unsafe` context.
+ pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
let Some(enclosing_item) =
expr.syntax().ancestors().find_map(Either::<ast::Item, ast::Variant>::cast)
else {
@@ -1683,6 +1470,14 @@ impl SemanticsScope<'_> {
|name, id| cb(name, id.into()),
)
}
+
+ pub fn extern_crates(&self) -> impl Iterator<Item = (Name, Module)> + '_ {
+ self.resolver.extern_crates_in_scope().map(|(name, id)| (name, Module { id }))
+ }
+
+ pub fn extern_crate_decls(&self) -> impl Iterator<Item = Name> + '_ {
+ self.resolver.extern_crate_decls_in_scope(self.db.upcast())
+ }
}
#[derive(Debug)]
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 3499daf11..f29fb1edf 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -487,7 +487,7 @@ impl SourceAnalyzer {
let path = macro_call.value.path().and_then(|ast| Path::from_src(ast, &ctx))?;
self.resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Bang))
- .map(|it| it.into())
+ .map(|(it, _)| it.into())
}
pub(crate) fn resolve_bind_pat_to_const(
@@ -760,7 +760,7 @@ impl SourceAnalyzer {
let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
self.resolver
.resolve_path_as_macro(db.upcast(), &path, Some(MacroSubNs::Bang))
- .map(|it| macro_id_to_def_id(db.upcast(), it))
+ .map(|(it, _)| macro_id_to_def_id(db.upcast(), it))
})?;
Some(macro_call_id.as_file()).filter(|it| it.expansion_level(db.upcast()) < 64)
}
@@ -966,6 +966,7 @@ pub(crate) fn resolve_hir_path_as_attr_macro(
) -> Option<Macro> {
resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, Some(MacroSubNs::Attr))
+ .map(|(it, _)| it)
.map(Into::into)
}
@@ -983,7 +984,7 @@ fn resolve_hir_path_(
res.map(|ty_ns| (ty_ns, path.segments().first()))
}
None => {
- let (ty, remaining_idx) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
+ let (ty, remaining_idx, _) = resolver.resolve_path_in_type_ns(db.upcast(), path)?;
match remaining_idx {
Some(remaining_idx) => {
if remaining_idx + 1 == path.segments().len() {
@@ -1067,7 +1068,7 @@ fn resolve_hir_path_(
let macros = || {
resolver
.resolve_path_as_macro(db.upcast(), path.mod_path()?, None)
- .map(|def| PathResolution::Def(ModuleDef::Macro(def.into())))
+ .map(|(def, _)| PathResolution::Def(ModuleDef::Macro(def.into())))
};
if prefer_value_ns { values().or_else(types) } else { types().or_else(values) }
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 43d957412..ca7874c36 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -2,8 +2,10 @@
use base_db::FileRange;
use hir_def::{
- src::HasSource, AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId,
- ModuleDefId, ModuleId, TraitId,
+ item_scope::ItemInNs,
+ src::{HasChildSource, HasSource},
+ AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
+ TraitId,
};
use hir_expand::{HirFileId, InFile};
use hir_ty::db::HirDatabase;
@@ -167,6 +169,40 @@ impl<'a> SymbolCollector<'a> {
self.collect_from_impl(impl_id);
}
+ // Record renamed imports.
+ // In case it imports multiple items under different namespaces we just pick one arbitrarily
+ // for now.
+ for id in scope.imports() {
+ let loc = id.import.lookup(self.db.upcast());
+ loc.id.item_tree(self.db.upcast());
+ let source = id.import.child_source(self.db.upcast());
+ let Some(use_tree_src) = source.value.get(id.idx) else { continue };
+ let Some(rename) = use_tree_src.rename() else { continue };
+ let Some(name) = rename.name() else { continue };
+
+ let res = scope.fully_resolve_import(self.db.upcast(), id);
+ res.iter_items().for_each(|(item, _)| {
+ let def = match item {
+ ItemInNs::Types(def) | ItemInNs::Values(def) => def,
+ ItemInNs::Macros(def) => ModuleDefId::from(def),
+ }
+ .into();
+ let dec_loc = DeclarationLocation {
+ hir_file_id: source.file_id,
+ ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
+ name_ptr: SyntaxNodePtr::new(name.syntax()),
+ };
+
+ self.symbols.push(FileSymbol {
+ name: name.text().into(),
+ def,
+ container_name: self.current_container_name.clone(),
+ loc: dec_loc,
+ is_alias: false,
+ });
+ });
+ }
+
for const_id in scope.unnamed_consts() {
self.collect_from_body(const_id);
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 6aca716bb..c0e5429a2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -422,7 +422,7 @@ impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
check_assist(
add_missing_default_members,
r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
bar: [i32, N]
}
@@ -439,7 +439,7 @@ impl<const X: usize, Y, Z> Foo<X, Z> for S<Y> {
$0
}"#,
r#"
-struct Bar<const: N: bool> {
+struct Bar<const N: usize> {
bar: [i32, N]
}
@@ -484,6 +484,107 @@ impl<X> Foo<42, {20 + 22}, X> for () {
}
#[test]
+ fn test_const_substitution_with_defaults() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> bool { M }
+ fn get_p(&self) -> char { P }
+ fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+ $0
+}"#,
+ r#"
+trait Foo<T, const N: usize = 42, const M: bool = false, const P: char = 'a'> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> bool { M }
+ fn get_p(&self) -> char { P }
+ fn get_array(&self, arg: &T) -> [bool; N] { [M; N] }
+}
+
+impl<X> Foo<X> for () {
+ $0fn get_n(&self) -> usize { 42 }
+
+ fn get_m(&self) -> bool { false }
+
+ fn get_p(&self) -> char { 'a' }
+
+ fn get_array(&self, arg: &X) -> [bool; 42] { [false; 42] }
+}"#,
+ );
+ }
+
+ #[test]
+ fn test_const_substitution_with_defaults_2() {
+ check_assist(
+ add_missing_impl_members,
+ r#"
+mod m {
+ pub const LEN: usize = 42;
+ pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+ fn get_t(&self) -> T;
+ }
+}
+
+impl m::Foo for () {
+ $0
+}"#,
+ r#"
+mod m {
+ pub const LEN: usize = 42;
+ pub trait Foo<const M: usize = LEN, const N: usize = M, T = [bool; N]> {
+ fn get_t(&self) -> T;
+ }
+}
+
+impl m::Foo for () {
+ fn get_t(&self) -> [bool; m::LEN] {
+ ${0:todo!()}
+ }
+}"#,
+ )
+ }
+
+ #[test]
+ fn test_const_substitution_with_defaults_3() {
+ check_assist(
+ add_missing_default_members,
+ r#"
+mod m {
+ pub const VAL: usize = 0;
+
+ pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> usize { M }
+ }
+}
+
+impl m::Foo for () {
+ $0
+}"#,
+ r#"
+mod m {
+ pub const VAL: usize = 0;
+
+ pub trait Foo<const N: usize = {40 + 2}, const M: usize = {VAL + 1}> {
+ fn get_n(&self) -> usize { N }
+ fn get_m(&self) -> usize { M }
+ }
+}
+
+impl m::Foo for () {
+ $0fn get_n(&self) -> usize { {40 + 2} }
+
+ fn get_m(&self) -> usize { {m::VAL + 1} }
+}"#,
+ )
+ }
+
+ #[test]
fn test_cursor_after_empty_impl_def() {
check_assist(
add_missing_impl_members,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index 57cfa17cc..66bc2f6da 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,10 @@
use std::collections::VecDeque;
-use syntax::ast::{self, AstNode};
+use syntax::{
+ ast::{self, AstNode, Expr::BinExpr},
+ ted::{self, Position},
+ SyntaxKind,
+};
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
@@ -23,121 +27,117 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
// }
// ```
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
- let expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
- let op = expr.op_kind()?;
- let op_range = expr.op_token()?.text_range();
+ let mut bin_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
+ let op = bin_expr.op_kind()?;
+ let op_range = bin_expr.op_token()?.text_range();
- let opposite_op = match op {
- ast::BinaryOp::LogicOp(ast::LogicOp::And) => "||",
- ast::BinaryOp::LogicOp(ast::LogicOp::Or) => "&&",
- _ => return None,
- };
-
- let cursor_in_range = op_range.contains_range(ctx.selection_trimmed());
- if !cursor_in_range {
+ // Is the cursor on the expression's logical operator?
+ if !op_range.contains_range(ctx.selection_trimmed()) {
return None;
}
- let mut expr = expr;
-
// Walk up the tree while we have the same binary operator
- while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
- match expr.op_kind() {
+ while let Some(parent_expr) = bin_expr.syntax().parent().and_then(ast::BinExpr::cast) {
+ match parent_expr.op_kind() {
Some(parent_op) if parent_op == op => {
- expr = parent_expr;
+ bin_expr = parent_expr;
}
_ => break,
}
}
- let mut expr_stack = vec![expr.clone()];
- let mut terms = Vec::new();
- let mut op_ranges = Vec::new();
-
- // Find all the children with the same binary operator
- while let Some(expr) = expr_stack.pop() {
- let mut traverse_bin_expr_arm = |expr| {
- if let ast::Expr::BinExpr(bin_expr) = expr {
- if let Some(expr_op) = bin_expr.op_kind() {
- if expr_op == op {
- expr_stack.push(bin_expr);
- } else {
- terms.push(ast::Expr::BinExpr(bin_expr));
- }
+ let op = bin_expr.op_kind()?;
+ let inv_token = match op {
+ ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2,
+ ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2,
+ _ => return None,
+ };
+
+ let demorganed = bin_expr.clone_subtree().clone_for_update();
+
+ ted::replace(demorganed.op_token()?, ast::make::token(inv_token));
+ let mut exprs = VecDeque::from(vec![
+ (bin_expr.lhs()?, demorganed.lhs()?),
+ (bin_expr.rhs()?, demorganed.rhs()?),
+ ]);
+
+ while let Some((expr, dm)) = exprs.pop_front() {
+ if let BinExpr(bin_expr) = &expr {
+ if let BinExpr(cbin_expr) = &dm {
+ if op == bin_expr.op_kind()? {
+ ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token));
+ exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
+ exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
} else {
- terms.push(ast::Expr::BinExpr(bin_expr));
+ let mut inv = invert_boolean_expression(expr);
+ if inv.needs_parens_in(dm.syntax().parent()?) {
+ inv = ast::make::expr_paren(inv).clone_for_update();
+ }
+ ted::replace(dm.syntax(), inv.syntax());
}
} else {
- terms.push(expr);
+ return None;
}
- };
-
- op_ranges.extend(expr.op_token().map(|t| t.text_range()));
- traverse_bin_expr_arm(expr.lhs()?);
- traverse_bin_expr_arm(expr.rhs()?);
+ } else {
+ let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update();
+ if inv.needs_parens_in(dm.syntax().parent()?) {
+ inv = ast::make::expr_paren(inv).clone_for_update();
+ }
+ ted::replace(dm.syntax(), inv.syntax());
+ }
}
+ let dm_lhs = demorganed.lhs()?;
+
acc.add(
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
"Apply De Morgan's law",
op_range,
|edit| {
- terms.sort_by_key(|t| t.syntax().text_range().start());
- let mut terms = VecDeque::from(terms);
-
- let paren_expr = expr.syntax().parent().and_then(ast::ParenExpr::cast);
-
+ let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
let neg_expr = paren_expr
.clone()
.and_then(|paren_expr| paren_expr.syntax().parent())
.and_then(ast::PrefixExpr::cast)
.and_then(|prefix_expr| {
- if prefix_expr.op_kind().unwrap() == ast::UnaryOp::Not {
+ if prefix_expr.op_kind()? == ast::UnaryOp::Not {
Some(prefix_expr)
} else {
None
}
});
- for op_range in op_ranges {
- edit.replace(op_range, opposite_op);
- }
-
if let Some(paren_expr) = paren_expr {
- for term in terms {
- let range = term.syntax().text_range();
- let not_term = invert_boolean_expression(term);
-
- edit.replace(range, not_term.syntax().text());
- }
-
if let Some(neg_expr) = neg_expr {
cov_mark::hit!(demorgan_double_negation);
- edit.replace(neg_expr.op_token().unwrap().text_range(), "");
+ edit.replace_ast(ast::Expr::PrefixExpr(neg_expr), demorganed.into());
} else {
cov_mark::hit!(demorgan_double_parens);
- edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!(");
+ ted::insert_all_raw(
+ Position::before(dm_lhs.syntax()),
+ vec![
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+ ],
+ );
+
+ ted::append_child_raw(
+ demorganed.syntax(),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::R_PAREN)),
+ );
+
+ edit.replace_ast(ast::Expr::ParenExpr(paren_expr), demorganed.into());
}
} else {
- if let Some(lhs) = terms.pop_front() {
- let lhs_range = lhs.syntax().text_range();
- let not_lhs = invert_boolean_expression(lhs);
-
- edit.replace(lhs_range, format!("!({not_lhs}"));
- }
-
- if let Some(rhs) = terms.pop_back() {
- let rhs_range = rhs.syntax().text_range();
- let not_rhs = invert_boolean_expression(rhs);
-
- edit.replace(rhs_range, format!("{not_rhs})"));
- }
-
- for term in terms {
- let term_range = term.syntax().text_range();
- let not_term = invert_boolean_expression(term);
- edit.replace(term_range, not_term.to_string());
- }
+ ted::insert_all_raw(
+ Position::before(dm_lhs.syntax()),
+ vec![
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::BANG)),
+ syntax::NodeOrToken::Token(ast::make::token(SyntaxKind::L_PAREN)),
+ ],
+ );
+ ted::append_child_raw(demorganed.syntax(), ast::make::token(SyntaxKind::R_PAREN));
+ edit.replace_ast(bin_expr, demorganed);
}
},
)
@@ -145,9 +145,8 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
#[cfg(test)]
mod tests {
- use crate::tests::{check_assist, check_assist_not_applicable};
-
use super::*;
+ use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn demorgan_handles_leq() {
@@ -213,7 +212,7 @@ fn f() { !(S <= S || S < S) }
#[test]
fn demorgan_doesnt_double_negation() {
cov_mark::check!(demorgan_double_negation);
- check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }")
+ check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { !x && !x }")
}
#[test]
@@ -222,13 +221,38 @@ fn f() { !(S <= S || S < S) }
check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
}
- // https://github.com/rust-lang/rust-analyzer/issues/10963
+ // FIXME : This needs to go.
+ // // https://github.com/rust-lang/rust-analyzer/issues/10963
+ // #[test]
+ // fn demorgan_doesnt_hang() {
+ // check_assist(
+ // apply_demorgan,
+ // "fn f() { 1 || 3 &&$0 4 || 5 }",
+ // "fn f() { !(!1 || !3 || !4) || 5 }",
+ // )
+ // }
+
+ #[test]
+ fn demorgan_keep_pars_for_op_precedence() {
+ check_assist(
+ apply_demorgan,
+ "fn main() {
+ let _ = !(!a ||$0 !(b || c));
+}
+",
+ "fn main() {
+ let _ = a && (b || c);
+}
+",
+ );
+ }
+
#[test]
- fn demorgan_doesnt_hang() {
+ fn demorgan_removes_pars_in_eq_precedence() {
check_assist(
apply_demorgan,
- "fn f() { 1 || 3 &&$0 4 || 5 }",
- "fn f() { !(!1 || !3 || !4) || 5 }",
+ "fn() { let x = a && !(!b |$0| !c); }",
+ "fn() { let x = a && b && c; }",
)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
new file mode 100644
index 000000000..45c1f0cca
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/bind_unused_param.rs
@@ -0,0 +1,159 @@
+use crate::assist_context::{AssistContext, Assists};
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ defs::Definition,
+ LineIndexDatabase,
+};
+use syntax::{
+ ast::{self, edit_in_place::Indent},
+ AstNode,
+};
+
+// Assist: bind_unused_param
+//
+// Binds unused function parameter to an underscore.
+//
+// ```
+// fn some_function(x: i32$0) {}
+// ```
+// ->
+// ```
+// fn some_function(x: i32) {
+// let _ = x;
+// }
+// ```
+pub(crate) fn bind_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let param: ast::Param = ctx.find_node_at_offset()?;
+
+ let Some(ast::Pat::IdentPat(ident_pat)) = param.pat() else { return None };
+
+ let param_def = {
+ let local = ctx.sema.to_def(&ident_pat)?;
+ Definition::Local(local)
+ };
+ if param_def.usages(&ctx.sema).at_least_one() {
+ cov_mark::hit!(keep_used);
+ return None;
+ }
+
+ let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
+ let stmt_list = func.body()?.stmt_list()?;
+ let l_curly_range = stmt_list.l_curly_token()?.text_range();
+ let r_curly_range = stmt_list.r_curly_token()?.text_range();
+
+ acc.add(
+ AssistId("bind_unused_param", AssistKind::QuickFix),
+ &format!("Bind as `let _ = {};`", &ident_pat),
+ param.syntax().text_range(),
+ |builder| {
+ let line_index = ctx.db().line_index(ctx.file_id());
+
+ let indent = func.indent_level();
+ let text_indent = indent + 1;
+ let mut text = format!("\n{text_indent}let _ = {ident_pat};");
+
+ let left_line = line_index.line_col(l_curly_range.end()).line;
+ let right_line = line_index.line_col(r_curly_range.start()).line;
+
+ if left_line == right_line {
+ cov_mark::hit!(single_line);
+ text.push_str(&format!("\n{indent}"));
+ }
+
+ builder.insert(l_curly_range.end(), text);
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn bind_unused_empty_block() {
+ cov_mark::check!(single_line);
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo($0y: i32) {}
+"#,
+ r#"
+fn foo(y: i32) {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn bind_unused_empty_block_with_newline() {
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo($0y: i32) {
+}
+"#,
+ r#"
+fn foo(y: i32) {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn bind_unused_generic() {
+ check_assist(
+ bind_unused_param,
+ r#"
+fn foo<T>($0y: T)
+where T : Default {
+}
+"#,
+ r#"
+fn foo<T>(y: T)
+where T : Default {
+ let _ = y;
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn trait_impl() {
+ check_assist(
+ bind_unused_param,
+ r#"
+trait Trait {
+ fn foo(x: i32);
+}
+impl Trait for () {
+ fn foo($0x: i32) {}
+}
+"#,
+ r#"
+trait Trait {
+ fn foo(x: i32);
+}
+impl Trait for () {
+ fn foo(x: i32) {
+ let _ = x;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn keep_used() {
+ cov_mark::check!(keep_used);
+ check_assist_not_applicable(
+ bind_unused_param,
+ r#"
+fn foo(x: i32, $0y: i32) { y; }
+"#,
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
index 1af52c592..d231708c5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs
@@ -103,7 +103,6 @@ pub(crate) fn convert_if_to_bool_then(acc: &mut Assists, ctx: &AssistContext<'_>
cond,
ast::Expr::BinExpr(_)
| ast::Expr::BlockExpr(_)
- | ast::Expr::BoxExpr(_)
| ast::Expr::BreakExpr(_)
| ast::Expr::CastExpr(_)
| ast::Expr::ClosureExpr(_)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index fe1cb6fce..76f021ed9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -161,9 +161,9 @@ fn process_struct_name_reference(
let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
// A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
let full_path =
- path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last().unwrap();
+ path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?;
- if full_path.segment().unwrap().name_ref()? != *name_ref {
+ if full_path.segment()?.name_ref()? != *name_ref {
// `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
// struct we want to edit.
return None;
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
index dcb96ab8a..7d0e42476 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs
@@ -58,7 +58,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
return None;
}
- let bound_ident = pat.fields().next().unwrap();
+ let bound_ident = pat.fields().next()?;
if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
return None;
}
@@ -108,6 +108,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?;
+ let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
+
+ let end_of_then = then_block_items.syntax().last_child_or_token()?;
+ let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
+ end_of_then.prev_sibling_or_token()?
+ } else {
+ end_of_then
+ };
+
let target = if_expr.syntax().text_range();
acc.add(
AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
@@ -141,16 +150,6 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<'
}
};
- let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update();
-
- let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
- let end_of_then =
- if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
- end_of_then.prev_sibling_or_token().unwrap()
- } else {
- end_of_then
- };
-
let then_statements = replacement
.children_with_tokens()
.chain(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
index 4f3b6e0c2..31a1ff496 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs
@@ -15,26 +15,13 @@ use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
// Move an expression out of a format string.
//
// ```
-// macro_rules! format_args {
-// ($lit:literal $(tt:tt)*) => { 0 },
-// }
-// macro_rules! print {
-// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-// }
-//
+// # //- minicore: fmt
// fn main() {
// print!("{var} {x + 1}$0");
// }
// ```
// ->
// ```
-// macro_rules! format_args {
-// ($lit:literal $(tt:tt)*) => { 0 },
-// }
-// macro_rules! print {
-// ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-// }
-//
// fn main() {
// print!("{var} {}"$0, x + 1);
// }
@@ -48,7 +35,7 @@ pub(crate) fn extract_expressions_from_format_string(
let tt = fmt_string.syntax().parent().and_then(ast::TokenTree::cast)?;
let expanded_t = ast::String::cast(
- ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone()),
+ ctx.sema.descend_into_macros_with_kind_preference(fmt_string.syntax().clone(), 0.into()),
)?;
if !is_format_string(&expanded_t) {
return None;
@@ -158,37 +145,21 @@ mod tests {
use super::*;
use crate::tests::check_assist;
- const MACRO_DECL: &'static str = r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-"#;
-
- fn add_macro_decl(s: &'static str) -> String {
- MACRO_DECL.to_string() + s
- }
-
#[test]
fn multiple_middle_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, y + 2, x + 1, 2);
}
"#,
- ),
);
}
@@ -196,20 +167,17 @@ fn main() {
fn single_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{obj.value:b}$0",);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b}"$0, obj.value);
}
"#,
- ),
);
}
@@ -217,20 +185,17 @@ fn main() {
fn multiple_middle_placeholders_arg() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {} {}$0", y + 2, 2);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
}
"#,
- ),
);
}
@@ -238,20 +203,17 @@ fn main() {
fn multiple_trailing_args() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -259,20 +221,17 @@ fn main() {
fn improper_commas() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
}
"#,
- ),
);
}
@@ -280,20 +239,17 @@ fn main() {
fn nested_tt() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
print!("My name is {} {x$0 + x}", stringify!(Paperino))
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
print!("My name is {} {}"$0, stringify!(Paperino), x + x)
}
"#,
- ),
);
}
@@ -301,22 +257,19 @@ fn main() {
fn extract_only_expressions() {
check_assist(
extract_expressions_from_format_string,
- &add_macro_decl(
- r#"
+ r#"
+//- minicore: fmt
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {x$0 + x}")
}
"#,
- ),
- &add_macro_decl(
- r#"
+ r#"
fn main() {
let var = 1 + 1;
print!("foobar {var} {var:?} {}"$0, x + x)
}
"#,
- ),
);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index b8b781ea4..de591cfde 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -531,7 +531,7 @@ impl FunctionBody {
fn extracted_from_trait_impl(&self) -> bool {
match self.node().ancestors().find_map(ast::Impl::cast) {
- Some(c) => return c.trait_().is_some(),
+ Some(c) => c.trait_().is_some(),
None => false,
}
}
@@ -750,7 +750,7 @@ impl FunctionBody {
.descendants_with_tokens()
.filter_map(SyntaxElement::into_token)
.filter(|it| matches!(it.kind(), SyntaxKind::IDENT | T![self]))
- .flat_map(|t| sema.descend_into_macros(t))
+ .flat_map(|t| sema.descend_into_macros(t, 0.into()))
.for_each(|t| add_name_if_local(t.parent().and_then(ast::NameRef::cast)));
}
}
@@ -810,7 +810,7 @@ impl FunctionBody {
(true, konst.body(), Some(sema.to_def(&konst)?.ty(sema.db)))
},
ast::ConstParam(cp) => {
- (true, cp.default_val(), Some(sema.to_def(&cp)?.ty(sema.db)))
+ (true, cp.default_val()?.expr(), Some(sema.to_def(&cp)?.ty(sema.db)))
},
ast::ConstBlockPat(cbp) => {
let expr = cbp.block_expr().map(ast::Expr::BlockExpr);
@@ -1048,23 +1048,17 @@ impl GenericParent {
fn generic_parents(parent: &SyntaxNode) -> Vec<GenericParent> {
let mut list = Vec::new();
if let Some(parent_item) = parent.ancestors().find_map(ast::Item::cast) {
- match parent_item {
- ast::Item::Fn(ref fn_) => {
- if let Some(parent_parent) = parent_item
- .syntax()
- .parent()
- .and_then(|it| it.parent())
- .and_then(ast::Item::cast)
- {
- match parent_parent {
- ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
- ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
- _ => (),
- }
+ if let ast::Item::Fn(ref fn_) = parent_item {
+ if let Some(parent_parent) =
+ parent_item.syntax().parent().and_then(|it| it.parent()).and_then(ast::Item::cast)
+ {
+ match parent_parent {
+ ast::Item::Impl(impl_) => list.push(GenericParent::Impl(impl_)),
+ ast::Item::Trait(trait_) => list.push(GenericParent::Trait(trait_)),
+ _ => (),
}
- list.push(GenericParent::Fn(fn_.clone()));
}
- _ => (),
+ list.push(GenericParent::Fn(fn_.clone()));
}
}
list
@@ -1385,31 +1379,30 @@ enum FlowHandler {
impl FlowHandler {
fn from_ret_ty(fun: &Function, ret_ty: &FunType) -> FlowHandler {
- match &fun.control_flow.kind {
- None => FlowHandler::None,
- Some(flow_kind) => {
- let action = flow_kind.clone();
- if let FunType::Unit = ret_ty {
- match flow_kind {
- FlowKind::Return(None)
- | FlowKind::Break(_, None)
- | FlowKind::Continue(_) => FlowHandler::If { action },
- FlowKind::Return(_) | FlowKind::Break(_, _) => {
- FlowHandler::IfOption { action }
- }
- FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
- }
- } else {
- match flow_kind {
- FlowKind::Return(None)
- | FlowKind::Break(_, None)
- | FlowKind::Continue(_) => FlowHandler::MatchOption { none: action },
- FlowKind::Return(_) | FlowKind::Break(_, _) => {
- FlowHandler::MatchResult { err: action }
- }
- FlowKind::Try { kind } => FlowHandler::Try { kind: kind.clone() },
- }
+ if fun.contains_tail_expr {
+ return FlowHandler::None;
+ }
+ let Some(action) = fun.control_flow.kind.clone() else {
+ return FlowHandler::None;
+ };
+
+ if let FunType::Unit = ret_ty {
+ match action {
+ FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+ FlowHandler::If { action }
+ }
+ FlowKind::Return(_) | FlowKind::Break(_, _) => FlowHandler::IfOption { action },
+ FlowKind::Try { kind } => FlowHandler::Try { kind },
+ }
+ } else {
+ match action {
+ FlowKind::Return(None) | FlowKind::Break(_, None) | FlowKind::Continue(_) => {
+ FlowHandler::MatchOption { none: action }
+ }
+ FlowKind::Return(_) | FlowKind::Break(_, _) => {
+ FlowHandler::MatchResult { err: action }
}
+ FlowKind::Try { kind } => FlowHandler::Try { kind },
}
}
}
@@ -1654,11 +1647,7 @@ impl Function {
fn make_ret_ty(&self, ctx: &AssistContext<'_>, module: hir::Module) -> Option<ast::RetType> {
let fun_ty = self.return_type(ctx);
- let handler = if self.contains_tail_expr {
- FlowHandler::None
- } else {
- FlowHandler::from_ret_ty(self, &fun_ty)
- };
+ let handler = FlowHandler::from_ret_ty(self, &fun_ty);
let ret_ty = match &handler {
FlowHandler::None => {
if matches!(fun_ty, FunType::Unit) {
@@ -1728,16 +1717,12 @@ fn make_body(
fun: &Function,
) -> ast::BlockExpr {
let ret_ty = fun.return_type(ctx);
- let handler = if fun.contains_tail_expr {
- FlowHandler::None
- } else {
- FlowHandler::from_ret_ty(fun, &ret_ty)
- };
+ let handler = FlowHandler::from_ret_ty(fun, &ret_ty);
let block = match &fun.body {
FunctionBody::Expr(expr) => {
let expr = rewrite_body_segment(ctx, &fun.params, &handler, expr.syntax());
- let expr = ast::Expr::cast(expr).unwrap();
+ let expr = ast::Expr::cast(expr).expect("Body segment should be an expr");
match expr {
ast::Expr::BlockExpr(block) => {
// If the extracted expression is itself a block, there is no need to wrap it inside another block.
@@ -1877,9 +1862,8 @@ fn with_tail_expr(block: ast::BlockExpr, tail_expr: ast::Expr) -> ast::BlockExpr
if let Some(stmt_list) = block.stmt_list() {
stmt_list.syntax().children_with_tokens().for_each(|node_or_token| {
- match &node_or_token {
- syntax::NodeOrToken::Token(_) => elements.push(node_or_token),
- _ => (),
+ if let syntax::NodeOrToken::Token(_) = &node_or_token {
+ elements.push(node_or_token)
};
});
}
@@ -1943,12 +1927,18 @@ fn fix_param_usages(ctx: &AssistContext<'_>, params: &[Param], syntax: &SyntaxNo
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::MutRef && node.mut_token().is_some() =>
{
- ted::replace(node.syntax(), node.expr().unwrap().syntax());
+ ted::replace(
+ node.syntax(),
+ node.expr().expect("RefExpr::expr() cannot be None").syntax(),
+ );
}
Some(ast::Expr::RefExpr(node))
if param.kind() == ParamKind::SharedRef && node.mut_token().is_none() =>
{
- ted::replace(node.syntax(), node.expr().unwrap().syntax());
+ ted::replace(
+ node.syntax(),
+ node.expr().expect("RefExpr::expr() cannot be None").syntax(),
+ );
}
Some(_) | None => {
let p = &make::expr_prefix(T![*], usage.clone()).clone_for_update();
@@ -4471,7 +4461,7 @@ async fn foo() -> Result<(), ()> {
"#,
r#"
async fn foo() -> Result<(), ()> {
- fun_name().await?
+ fun_name().await
}
async fn $0fun_name() -> Result<(), ()> {
@@ -4690,7 +4680,7 @@ fn $0fun_name() {
check_assist(
extract_function,
r#"
-//- minicore: result
+//- minicore: result, try
fn foo() -> Result<(), i64> {
$0Result::<i32, i64>::Ok(0)?;
Ok(())$0
@@ -4698,7 +4688,7 @@ fn foo() -> Result<(), i64> {
"#,
r#"
fn foo() -> Result<(), i64> {
- fun_name()?
+ fun_name()
}
fn $0fun_name() -> Result<(), i64> {
@@ -5754,6 +5744,34 @@ fn $0fun_name<T, V>(t: T, v: V) -> i32 where T: Into<i32> + Copy, V: Into<i32> {
}
#[test]
+ fn tail_expr_no_extra_control_flow() {
+ check_assist(
+ extract_function,
+ r#"
+//- minicore: result
+fn fallible() -> Result<(), ()> {
+ $0if true {
+ return Err(());
+ }
+ Ok(())$0
+}
+"#,
+ r#"
+fn fallible() -> Result<(), ()> {
+ fun_name()
+}
+
+fn $0fun_name() -> Result<(), ()> {
+ if true {
+ return Err(());
+ }
+ Ok(())
+}
+"#,
+ );
+ }
+
+ #[test]
fn non_tail_expr_of_tail_expr_loop() {
check_assist(
extract_function,
@@ -5800,12 +5818,6 @@ fn $0fun_name() -> ControlFlow<()> {
extract_function,
r#"
//- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
let a = $0if true {
@@ -5820,12 +5832,6 @@ fn f() -> Option<()> {
}
"#,
r#"
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
let a = fun_name()?;;
@@ -5852,12 +5858,6 @@ fn $0fun_name() -> Option<()> {
extract_function,
r#"
//- minicore: option, try
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
$0{
@@ -5874,15 +5874,9 @@ fn f() -> Option<()> {
}
"#,
r#"
-impl<T> core::ops::Try for Option<T> {
- type Output = T;
- type Residual = Option<!>;
-}
-impl<T> core::ops::FromResidual for Option<T> {}
-
fn f() -> Option<()> {
if true {
- fun_name()?
+ fun_name()
} else {
None
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 31fc69562..bbac0a26e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -95,6 +95,9 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else {
continue;
};
+
+ let field = make::ext::field_from_idents(["self", &field_name])?;
+
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
@@ -115,11 +118,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(list) => convert_param_list_to_arg_list(list),
None => make::arg_list([]),
};
- let tail_expr = make::expr_method_call(
- make::ext::field_from_idents(["self", &field_name]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
- make::name_ref(&name),
- arg_list,
- );
+ let tail_expr = make::expr_method_call(field, make::name_ref(&name), arg_list);
let ret_type = method_source.ret_type();
let is_async = method_source.async_token().is_some();
let is_const = method_source.const_token().is_some();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
index 747f70f9f..53ba144ba 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs
@@ -27,13 +27,19 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let cap = ctx.config.snippet_cap?;
let nominal = ctx.find_node_at_offset::<ast::Adt>()?;
let target = nominal.syntax().text_range();
+ let derive_attr = nominal
+ .attrs()
+ .filter_map(|x| x.as_simple_call())
+ .filter(|(name, _arg)| name == "derive")
+ .map(|(_name, arg)| arg)
+ .next();
+
+ let delimiter = match &derive_attr {
+ None => None,
+ Some(tt) => Some(tt.right_delimiter_token()?),
+ };
+
acc.add(AssistId("generate_derive", AssistKind::Generate), "Add `#[derive]`", target, |edit| {
- let derive_attr = nominal
- .attrs()
- .filter_map(|x| x.as_simple_call())
- .filter(|(name, _arg)| name == "derive")
- .map(|(_name, arg)| arg)
- .next();
match derive_attr {
None => {
let derive = make::attr_outer(make::meta_token_tree(
@@ -45,16 +51,23 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
let nominal = edit.make_mut(nominal);
nominal.add_attr(derive.clone());
+ let delimiter = derive
+ .meta()
+ .expect("make::attr_outer was expected to have Meta")
+ .token_tree()
+ .expect("failed to get token tree out of Meta")
+ .r_paren_token()
+ .expect("make::attr_outer was expected to have a R_PAREN");
+
+ edit.add_tabstop_before_token(cap, delimiter);
+ }
+ Some(_) => {
+ // Just move the cursor.
edit.add_tabstop_before_token(
cap,
- derive.meta().unwrap().token_tree().unwrap().r_paren_token().unwrap(),
+ delimiter.expect("Right delim token could not be found."),
);
}
- Some(tt) => {
- // Just move the cursor.
- let tt = edit.make_mut(tt);
- edit.add_tabstop_before_token(cap, tt.right_delimiter_token().unwrap());
- }
};
})
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs
new file mode 100644
index 000000000..663df266b
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/into_to_qualified_from.rs
@@ -0,0 +1,205 @@
+use hir::{AsAssocItem, HirDisplay};
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ famous_defs::FamousDefs,
+};
+use syntax::{ast, AstNode};
+
+use crate::assist_context::{AssistContext, Assists};
+
+// Assist: into_to_qualified_from
+//
+// Convert an `into` method call to a fully qualified `from` call.
+//
+// ```
+// //- minicore: from
+// struct B;
+// impl From<i32> for B {
+// fn from(a: i32) -> Self {
+// B
+// }
+// }
+//
+// fn main() -> () {
+// let a = 3;
+// let b: B = a.in$0to();
+// }
+// ```
+// ->
+// ```
+// struct B;
+// impl From<i32> for B {
+// fn from(a: i32) -> Self {
+// B
+// }
+// }
+//
+// fn main() -> () {
+// let a = 3;
+// let b: B = B::from(a);
+// }
+// ```
+pub(crate) fn into_to_qualified_from(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?;
+ let nameref = method_call.name_ref()?;
+ let receiver = method_call.receiver()?;
+ let db = ctx.db();
+ let sema = &ctx.sema;
+ let fnc = sema.resolve_method_call(&method_call)?;
+ let scope = sema.scope(method_call.syntax())?;
+ // Check if the method call refers to Into trait.
+ if fnc.as_assoc_item(db)?.containing_trait_impl(db)?
+ == FamousDefs(sema, scope.krate()).core_convert_Into()?
+ {
+ let type_call = sema.type_of_expr(&method_call.clone().into())?;
+ let type_call_disp =
+ type_call.adjusted().display_source_code(db, scope.module().into(), true).ok()?;
+
+ acc.add(
+ AssistId("into_to_qualified_from", AssistKind::Generate),
+ "Convert `into` to fully qualified `from`",
+ nameref.syntax().text_range(),
+ |edit| {
+ edit.replace(
+ method_call.syntax().text_range(),
+ format!("{}::from({})", type_call_disp, receiver),
+ );
+ },
+ );
+ }
+
+ Some(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_assist;
+
+ use super::into_to_qualified_from;
+
+ #[test]
+ fn two_types_in_same_mod() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = a.in$0to();
+}"#,
+ r#"
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = B::from(a);
+}"#,
+ )
+ }
+
+ #[test]
+ fn fromed_in_child_mod_imported() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+use C::B;
+
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = a.in$0to();
+}"#,
+ r#"
+use C::B;
+
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: B = B::from(a);
+}"#,
+ )
+ }
+
+ #[test]
+ fn fromed_in_child_mod_not_imported() {
+ check_assist(
+ into_to_qualified_from,
+ r#"
+//- minicore: from
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: C::B = a.in$0to();
+}"#,
+ r#"
+struct A;
+
+mod C {
+ use crate::A;
+
+ pub(super) struct B;
+ impl From<A> for B {
+ fn from(a: A) -> Self {
+ B
+ }
+ }
+}
+
+fn main() -> () {
+ let a: A = A;
+ let b: C::B = C::B::from(a);
+}"#,
+ )
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index 5cc110cf1..6ed9bd85f 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -76,12 +76,19 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
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()) {
- let name = make::name_ref(&name);
+ let name_ref = make::name_ref(&name);
for usage in usages {
let Some(usage) = usage.name.as_name_ref().cloned() else { continue };
- let usage = edit.make_mut(usage);
- ted::replace(usage.syntax(), name.clone_for_update().syntax());
+ if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage) {
+ let record_field = edit.make_mut(record_field);
+ let name_expr =
+ make::expr_path(make::path_from_text(&name)).clone_for_update();
+ record_field.replace_expr(name_expr);
+ } else {
+ let usage = edit.make_mut(usage);
+ ted::replace(usage.syntax(), name_ref.clone_for_update().syntax());
+ }
}
}
@@ -120,8 +127,7 @@ fn is_body_const(sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr) -> bool {
is_const &=
sema.resolve_method_call(&call).map(|it| it.is_const(sema.db)).unwrap_or(true)
}
- ast::Expr::BoxExpr(_)
- | ast::Expr::ForExpr(_)
+ ast::Expr::ForExpr(_)
| ast::Expr::ReturnExpr(_)
| ast::Expr::TryExpr(_)
| ast::Expr::YieldExpr(_)
@@ -180,6 +186,33 @@ fn foo() {
}
#[test]
+ fn usage_in_field_shorthand() {
+ check_assist(
+ promote_local_to_const,
+ r"
+struct Foo {
+ bar: usize,
+}
+
+fn main() {
+ let $0bar = 0;
+ let foo = Foo { bar };
+}
+",
+ r"
+struct Foo {
+ bar: usize,
+}
+
+fn main() {
+ const $0BAR: usize = 0;
+ let foo = Foo { bar: BAR };
+}
+",
+ )
+ }
+
+ #[test]
fn not_applicable_non_const_meth_call() {
cov_mark::check!(promote_local_non_const);
check_assist_not_applicable(
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index a403d5bc6..cffa3f55c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -39,14 +39,11 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let replacements =
macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
- if replacements.is_empty() {
- return None;
- }
acc.add(
AssistId("remove_dbg", AssistKind::Refactor),
"Remove dbg!()",
- replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range)).unwrap(),
+ replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
|builder| {
for (range, expr) in replacements {
if let Some(expr) = expr {
@@ -116,10 +113,7 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
Some(parent) => match (expr, parent) {
(ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
(
- ast::Expr::BoxExpr(_)
- | ast::Expr::PrefixExpr(_)
- | ast::Expr::RefExpr(_)
- | ast::Expr::MacroExpr(_),
+ ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_) | ast::Expr::MacroExpr(_),
ast::Expr::AwaitExpr(_)
| ast::Expr::CallExpr(_)
| ast::Expr::CastExpr(_)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
index dd4839351..5fcab8c02 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -67,7 +67,7 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>)
// This case maps to the situation where the * token is braced.
// In this case, the parent use tree's path is the one we should use to resolve the glob.
match u.syntax().ancestors().skip(1).find_map(ast::UseTree::cast) {
- Some(parent_u) if parent_u.path().is_some() => parent_u.path().unwrap(),
+ Some(parent_u) if parent_u.path().is_some() => parent_u.path()?,
_ => return None,
}
} else {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
index 24c338745..61e9bcdcc 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs
@@ -48,6 +48,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<
return None;
}
+ let new_result_ty =
+ make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update();
+ let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?;
+ let last_genarg = generic_args.generic_args().last()?;
+
acc.add(
AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite),
"Wrap return type in Result",
@@ -75,19 +80,12 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext<
ted::replace(ret_expr_arg.syntax(), ok_wrapped.syntax());
}
- let new_result_ty =
- make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update();
let old_result_ty = edit.make_mut(type_ref.clone());
ted::replace(old_result_ty.syntax(), new_result_ty.syntax());
if let Some(cap) = ctx.config.snippet_cap {
- let generic_args = new_result_ty
- .syntax()
- .descendants()
- .find_map(ast::GenericArgList::cast)
- .unwrap();
- edit.add_placeholder_snippet(cap, generic_args.generic_args().last().unwrap());
+ edit.add_placeholder_snippet(cap, last_genarg);
}
},
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index 2ebb5ef9b..6f973ab53 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -114,6 +114,7 @@ mod handlers {
mod add_turbo_fish;
mod apply_demorgan;
mod auto_import;
+ mod bind_unused_param;
mod change_visibility;
mod convert_bool_then;
mod convert_comment_block;
@@ -211,6 +212,7 @@ mod handlers {
mod unwrap_result_return_type;
mod unqualify_method_call;
mod wrap_return_type_in_result;
+ mod into_to_qualified_from;
pub(crate) fn all() -> &'static [Handler] {
&[
@@ -224,6 +226,7 @@ mod handlers {
add_turbo_fish::add_turbo_fish,
apply_demorgan::apply_demorgan,
auto_import::auto_import,
+ bind_unused_param::bind_unused_param,
change_visibility::change_visibility,
convert_bool_then::convert_bool_then_to_if,
convert_bool_then::convert_if_to_bool_then,
@@ -274,6 +277,7 @@ mod handlers {
inline_local_variable::inline_local_variable,
inline_type_alias::inline_type_alias,
inline_type_alias::inline_type_alias_uses,
+ into_to_qualified_from::into_to_qualified_from,
introduce_named_generic::introduce_named_generic,
introduce_named_lifetime::introduce_named_lifetime,
invert_if::invert_if,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 6eadc3dbc..dfaa53449 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -266,6 +266,21 @@ pub mod std { pub mod collections { pub struct HashMap { } } }
}
#[test]
+fn doctest_bind_unused_param() {
+ check_doc_test(
+ "bind_unused_param",
+ r#####"
+fn some_function(x: i32$0) {}
+"#####,
+ r#####"
+fn some_function(x: i32) {
+ let _ = x;
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_change_visibility() {
check_doc_test(
"change_visibility",
@@ -694,25 +709,12 @@ fn doctest_extract_expressions_from_format_string() {
check_doc_test(
"extract_expressions_from_format_string",
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
+//- minicore: fmt
fn main() {
print!("{var} {x + 1}$0");
}
"#####,
r#####"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
-macro_rules! print {
- ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
-}
-
fn main() {
print!("{var} {}"$0, x + 1);
}
@@ -1755,6 +1757,40 @@ fn foo() {
}
#[test]
+fn doctest_into_to_qualified_from() {
+ check_doc_test(
+ "into_to_qualified_from",
+ r#####"
+//- minicore: from
+struct B;
+impl From<i32> for B {
+ fn from(a: i32) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a = 3;
+ let b: B = a.in$0to();
+}
+"#####,
+ r#####"
+struct B;
+impl From<i32> for B {
+ fn from(a: i32) -> Self {
+ B
+ }
+}
+
+fn main() -> () {
+ let a = 3;
+ let b: B = B::from(a);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_introduce_named_generic() {
check_doc_test(
"introduce_named_generic",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
index f74ebfae0..16704d598 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs
@@ -103,7 +103,6 @@ pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>)
match expr {
ast::Expr::RefExpr(inner) => next_expr = inner.expr(),
- ast::Expr::BoxExpr(inner) => next_expr = inner.expr(),
ast::Expr::AwaitExpr(inner) => next_expr = inner.expr(),
// ast::Expr::BlockExpr(block) => expr = block.tail_expr(),
ast::Expr::CastExpr(inner) => next_expr = inner.expr(),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
index 480cb77b4..f60ac1501 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -20,6 +20,7 @@ pub(crate) mod r#type;
pub(crate) mod use_;
pub(crate) mod vis;
pub(crate) mod env_vars;
+pub(crate) mod extern_crate;
use std::iter;
@@ -703,7 +704,9 @@ pub(super) fn complete_name_ref(
TypeLocation::TypeAscription(ascription) => {
r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription);
}
- TypeLocation::GenericArgList(_)
+ TypeLocation::GenericArg { .. }
+ | TypeLocation::AssocConstEq
+ | TypeLocation::AssocTypeEq
| TypeLocation::TypeBound
| TypeLocation::ImplTarget
| TypeLocation::ImplTrait
@@ -737,6 +740,7 @@ pub(super) fn complete_name_ref(
}
}
}
+ NameRefKind::ExternCrate => extern_crate::complete_extern_crate(acc, ctx),
NameRefKind::DotAccess(dot_access) => {
flyimport::import_on_the_fly_dot(acc, ctx, dot_access);
dot::complete_dot(acc, ctx, dot_access);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
index 62bdb6ee6..87a286778 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/cfg.rs
@@ -1,10 +1,8 @@
//! Completion for cfg
-use std::iter;
-
use ide_db::SymbolKind;
use itertools::Itertools;
-use syntax::SyntaxKind;
+use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind};
use crate::{completions::Completions, context::CompletionContext, CompletionItem};
@@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
acc.add(completion.build(ctx.db));
};
- let previous = iter::successors(ctx.original_token.prev_token(), |t| {
- (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia())
- .then(|| t.prev_token())
- .flatten()
- })
- .find(|t| matches!(t.kind(), SyntaxKind::IDENT));
-
- match previous.as_ref().map(|p| p.text()) {
- Some("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion),
- Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion),
- Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion),
- Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion),
- Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion),
- Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
- let insert_text = format!(r#""{s}""#);
- let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
- item.insert_text(insert_text);
+ // FIXME: Move this into context/analysis.rs
+ let previous = ctx
+ .original_token
+ .prev_token()
+ .and_then(|it| {
+ if matches!(it.kind(), SyntaxKind::EQ) {
+ Some(it.into())
+ } else {
+ algo::non_trivia_sibling(it.into(), Direction::Prev)
+ }
+ })
+ .filter(|t| matches!(t.kind(), SyntaxKind::EQ))
+ .and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev))
+ .map(|it| match it {
+ NodeOrToken::Node(_) => None,
+ NodeOrToken::Token(t) => Ident::cast(t),
+ });
+ match previous {
+ Some(None) => (),
+ Some(Some(p)) => match p.text() {
+ "target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion),
+ "target_env" => KNOWN_ENV.iter().copied().for_each(add_completion),
+ "target_os" => KNOWN_OS.iter().copied().for_each(add_completion),
+ "target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion),
+ "target_endian" => ["little", "big"].into_iter().for_each(add_completion),
+ name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
+ let insert_text = format!(r#""{s}""#);
+ let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
+ item.insert_text(insert_text);
- acc.add(item.build(ctx.db));
- }),
+ acc.add(item.build(ctx.db));
+ }),
+ },
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
acc.add(item.build(ctx.db));
}),
- };
+ }
}
const KNOWN_ARCH: [&str; 20] = [
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
index 9447bc7db..90dac1902 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/derive.rs
@@ -1,6 +1,6 @@
//! Completion for derives
-use hir::{HasAttrs, ScopeDef};
-use ide_db::SymbolKind;
+use hir::ScopeDef;
+use ide_db::{documentation::HasDocs, SymbolKind};
use itertools::Itertools;
use syntax::SmolStr;
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
index 6bc6f34ed..f9dec5380 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute/lint.rs
@@ -1,5 +1,5 @@
//! Completion for lints
-use ide_db::{generated::lints::Lint, SymbolKind};
+use ide_db::{documentation::Documentation, generated::lints::Lint, SymbolKind};
use syntax::ast;
use crate::{context::CompletionContext, item::CompletionItem, Completions};
@@ -55,7 +55,7 @@ pub(super) fn complete_lint(
_ => name.to_owned(),
};
let mut item = CompletionItem::new(SymbolKind::Attribute, ctx.source_range(), label);
- item.documentation(hir::Documentation::new(description.to_owned()));
+ item.documentation(Documentation::new(description.to_owned()));
item.add_to(acc, ctx.db)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
new file mode 100644
index 000000000..f9cde4466
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs
@@ -0,0 +1,71 @@
+//! Completion for extern crates
+
+use hir::Name;
+use ide_db::{documentation::HasDocs, SymbolKind};
+
+use crate::{context::CompletionContext, CompletionItem, CompletionItemKind};
+
+use super::Completions;
+
+pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionContext<'_>) {
+ let imported_extern_crates: Vec<Name> = ctx.scope.extern_crate_decls().collect();
+
+ for (name, module) in ctx.scope.extern_crates() {
+ if imported_extern_crates.contains(&name) {
+ continue;
+ }
+
+ let mut item = CompletionItem::new(
+ CompletionItemKind::SymbolKind(SymbolKind::Module),
+ ctx.source_range(),
+ name.to_smol_str(),
+ );
+ item.set_documentation(module.docs(ctx.db));
+
+ item.add_to(acc, ctx.db);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::tests::completion_list_no_kw;
+
+ #[test]
+ fn can_complete_extern_crate() {
+ let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /other_crate_b.rs crate:other_crate_b
+pub mod good_mod{}
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a
+extern crate oth$0
+mod other_mod {}
+"#;
+
+ let completion_list = completion_list_no_kw(case);
+
+ assert_eq!("md other_crate_a\n".to_string(), completion_list);
+ }
+
+ #[test]
+ fn will_not_complete_existing_import() {
+ let case = r#"
+//- /lib.rs crate:other_crate_a
+// nothing here
+//- /lib.rs crate:crate_c
+// nothing here
+//- /lib.rs crate:other_crate_b
+//
+//- /lib.rs crate:lib deps:other_crate_a,other_crate_b,crate_c extern-prelude:other_crate_a,other_crate_b
+extern crate other_crate_b;
+extern crate oth$0
+mod other_mod {}
+"#;
+
+ let completion_list = completion_list_no_kw(case);
+
+ assert_eq!("md other_crate_a\n".to_string(), completion_list);
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
index 8e904fd60..cecbe7539 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs
@@ -51,9 +51,7 @@ mod tests {
fn works_when_wrapped() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
macro_rules! print {
($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
}
@@ -70,9 +68,7 @@ fn main() {
fn no_completion_without_brace() {
check(
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("f$0");
@@ -87,18 +83,13 @@ fn main() {
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{f$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
@@ -108,18 +99,13 @@ fn main() {
check_edit(
"foobar",
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
+//- minicore: fmt
fn main() {
let foobar = 1;
format_args!("{$0");
}
"#,
r#"
-macro_rules! format_args {
- ($lit:literal $(tt:tt)*) => { 0 },
-}
fn main() {
let foobar = 1;
format_args!("{foobar");
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 269e40e6e..42dfbfc7d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -33,8 +33,8 @@
use hir::{self, HasAttrs};
use ide_db::{
- path_transform::PathTransform, syntax_helpers::insert_whitespace_into_node,
- traits::get_missing_assoc_items, SymbolKind,
+ documentation::HasDocs, path_transform::PathTransform,
+ syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind,
};
use syntax::{
ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
index 2ffe12337..fc21bba45 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs
@@ -2,8 +2,12 @@
mod format_like;
-use hir::{Documentation, HasAttrs};
-use ide_db::{imports::insert_use::ImportScope, ty_filter::TryEnum, SnippetCap};
+use ide_db::{
+ documentation::{Documentation, HasDocs},
+ imports::insert_use::ImportScope,
+ ty_filter::TryEnum,
+ SnippetCap,
+};
use syntax::{
ast::{self, make, AstNode, AstToken},
SyntaxKind::{BLOCK_EXPR, EXPR_STMT, FOR_EXPR, IF_EXPR, LOOP_EXPR, STMT_LIST, WHILE_EXPR},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
index e9831a5b2..3ff68b978 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/snippet.rs
@@ -1,7 +1,6 @@
//! This file provides snippet completions, like `pd` => `eprintln!(...)`.
-use hir::Documentation;
-use ide_db::{imports::insert_use::ImportScope, SnippetCap};
+use ide_db::{documentation::Documentation, imports::insert_use::ImportScope, SnippetCap};
use crate::{
context::{ExprCtx, ItemListKind, PathCompletionCtx, Qualified},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
index e47054756..a30fd13b1 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/type.rs
@@ -1,7 +1,7 @@
//! Completion of names from the current scope in type position.
use hir::{HirDisplay, ScopeDef};
-use syntax::{ast, AstNode, SyntaxKind};
+use syntax::{ast, AstNode};
use crate::{
context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@@ -20,16 +20,15 @@ pub(crate) fn complete_type_path(
let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
match def {
- ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
+ ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(),
+ ScopeDef::Label(_) => false,
// no values in type places
ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
// unless its a constant in a generic arg list position
ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => {
- matches!(location, TypeLocation::GenericArgList(_))
- }
- ScopeDef::ImplSelfType(_) => {
- !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+ location.complete_consts()
}
+ ScopeDef::ImplSelfType(_) => location.complete_self_type(),
// Don't suggest attribute macros and derives.
ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db),
// Type things are fine
@@ -38,12 +37,12 @@ pub(crate) fn complete_type_path(
)
| ScopeDef::AdtSelfType(_)
| ScopeDef::Unknown
- | ScopeDef::GenericParam(TypeParam(_)) => true,
+ | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(),
}
};
let add_assoc_item = |acc: &mut Completions, item| match item {
- hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => {
+ hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => {
acc.add_const(ctx, ct)
}
hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (),
@@ -157,56 +156,30 @@ pub(crate) fn complete_type_path(
});
return;
}
- TypeLocation::GenericArgList(Some(arg_list)) => {
- let in_assoc_type_arg = ctx
- .original_token
- .parent_ancestors()
- .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
-
- if !in_assoc_type_arg {
- if let Some(path_seg) =
- arg_list.syntax().parent().and_then(ast::PathSegment::cast)
- {
- if path_seg
- .syntax()
- .ancestors()
- .find_map(ast::TypeBound::cast)
- .is_some()
- {
- if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
- trait_,
- ))) = ctx.sema.resolve_path(&path_seg.parent_path())
- {
- 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
- }
- }
+ 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
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 3cb65b272..0da7ba6d0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -155,13 +155,63 @@ pub(crate) struct ExprCtx {
pub(crate) enum TypeLocation {
TupleField,
TypeAscription(TypeAscriptionTarget),
- GenericArgList(Option<ast::GenericArgList>),
+ /// Generic argument position e.g. `Foo<$0>`
+ GenericArg {
+ /// The generic argument list containing the generic arg
+ args: Option<ast::GenericArgList>,
+ /// `Some(trait_)` if `trait_` is being instantiated with `args`
+ of_trait: Option<hir::Trait>,
+ /// The generic parameter being filled in by the generic arg
+ corresponding_param: Option<ast::GenericParam>,
+ },
+ /// Associated type equality constraint e.g. `Foo<Bar = $0>`
+ AssocTypeEq,
+ /// Associated constant equality constraint e.g. `Foo<X = $0>`
+ AssocConstEq,
TypeBound,
ImplTarget,
ImplTrait,
Other,
}
+impl TypeLocation {
+ pub(crate) fn complete_lifetimes(&self) -> bool {
+ matches!(
+ self,
+ TypeLocation::GenericArg {
+ corresponding_param: Some(ast::GenericParam::LifetimeParam(_)),
+ ..
+ }
+ )
+ }
+
+ pub(crate) fn complete_consts(&self) -> bool {
+ match self {
+ TypeLocation::GenericArg {
+ corresponding_param: Some(ast::GenericParam::ConstParam(_)),
+ ..
+ } => true,
+ TypeLocation::AssocConstEq => true,
+ _ => false,
+ }
+ }
+
+ pub(crate) fn complete_types(&self) -> bool {
+ match self {
+ TypeLocation::GenericArg { corresponding_param: Some(param), .. } => {
+ matches!(param, ast::GenericParam::TypeParam(_))
+ }
+ TypeLocation::AssocConstEq => false,
+ TypeLocation::AssocTypeEq => true,
+ _ => true,
+ }
+ }
+
+ pub(crate) fn complete_self_type(&self) -> bool {
+ self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait)
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum TypeAscriptionTarget {
Let(Option<ast::Pat>),
@@ -301,6 +351,7 @@ pub(super) enum NameRefKind {
expr: ast::RecordExpr,
},
Pattern(PatternContext),
+ ExternCrate,
}
/// The identifier we are currently completing.
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 3ea506590..1e6b2f319 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1,11 +1,11 @@
//! Module responsible for analyzing the code surrounding the cursor for completion.
use std::iter;
-use hir::{Semantics, Type, TypeInfo, Variant};
+use hir::{HasSource, Semantics, Type, TypeInfo, Variant};
use ide_db::{active_parameter::ActiveParameter, RootDatabase};
use syntax::{
algo::{find_node_at_offset, non_trivia_sibling},
- ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef},
+ ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef},
match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode,
SyntaxToken, TextRange, TextSize, T,
};
@@ -624,6 +624,10 @@ fn classify_name_ref(
});
return Some(make_res(kind));
},
+ ast::ExternCrate(_) => {
+ let kind = NameRefKind::ExternCrate;
+ return Some(make_res(kind));
+ },
ast::MethodCallExpr(method) => {
let receiver = find_opt_node_in_file(original_file, method.receiver());
let kind = NameRefKind::DotAccess(DotAccess {
@@ -719,6 +723,136 @@ fn classify_name_ref(
None
};
+ let generic_arg_location = |arg: ast::GenericArg| {
+ let mut override_location = None;
+ let location = find_opt_node_in_file_compensated(
+ sema,
+ original_file,
+ arg.syntax().parent().and_then(ast::GenericArgList::cast),
+ )
+ .map(|args| {
+ let mut in_trait = None;
+ let param = (|| {
+ let parent = args.syntax().parent()?;
+ let params = match_ast! {
+ match parent {
+ ast::PathSegment(segment) => {
+ match sema.resolve_path(&segment.parent_path().top_path())? {
+ hir::PathResolution::Def(def) => match def {
+ hir::ModuleDef::Function(func) => {
+ func.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Adt(adt) => {
+ adt.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Variant(variant) => {
+ variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::Trait(trait_) => {
+ if let ast::GenericArg::AssocTypeArg(arg) = &arg {
+ let arg_name = arg.name_ref()?;
+ 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 {
+ override_location = Some(TypeLocation::AssocTypeEq);
+ return None;
+ }
+ },
+ hir::AssocItem::Const(const_) => {
+ if const_.name(sema.db)?.as_str()? == arg_name {
+ override_location = Some(TypeLocation::AssocConstEq);
+ return None;
+ }
+ },
+ _ => (),
+ }
+ }
+ return None;
+ } else {
+ in_trait = Some(trait_);
+ trait_.source(sema.db)?.value.generic_param_list()
+ }
+ }
+ hir::ModuleDef::TraitAlias(trait_) => {
+ trait_.source(sema.db)?.value.generic_param_list()
+ }
+ hir::ModuleDef::TypeAlias(ty_) => {
+ ty_.source(sema.db)?.value.generic_param_list()
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ },
+ ast::MethodCallExpr(call) => {
+ let func = sema.resolve_method_call(&call)?;
+ func.source(sema.db)?.value.generic_param_list()
+ },
+ ast::AssocTypeArg(arg) => {
+ let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?;
+ match sema.resolve_path(&trait_.parent_path().top_path())? {
+ hir::PathResolution::Def(def) => match def {
+ hir::ModuleDef::Trait(trait_) => {
+ let arg_name = arg.name_ref()?;
+ let arg_name = arg_name.text();
+ let trait_items = trait_.items_with_supertraits(sema.db);
+ let assoc_ty = trait_items.iter().find_map(|item| match item {
+ hir::AssocItem::TypeAlias(assoc_ty) => {
+ (assoc_ty.name(sema.db).as_str()? == arg_name)
+ .then_some(assoc_ty)
+ },
+ _ => None,
+ })?;
+ assoc_ty.source(sema.db)?.value.generic_param_list()
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+ }?;
+ // Determine the index of the argument in the `GenericArgList` and match it with
+ // the corresponding parameter in the `GenericParamList`. Since lifetime parameters
+ // are often omitted, ignore them for the purposes of matching the argument with
+ // its parameter unless a lifetime argument is provided explicitly. That is, for
+ // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`.
+ // FIXME: This operates on the syntax tree and will produce incorrect results when
+ // generic parameters are disabled by `#[cfg]` directives. It should operate on the
+ // HIR, but the functionality necessary to do so is not exposed at the moment.
+ let mut explicit_lifetime_arg = false;
+ let arg_idx = arg
+ .syntax()
+ .siblings(Direction::Prev)
+ // Skip the node itself
+ .skip(1)
+ .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true })
+ .count();
+ let param_idx = if explicit_lifetime_arg {
+ arg_idx
+ } else {
+ // Lifetimes parameters always precede type and generic parameters,
+ // so offset the argument index by the total number of lifetime params
+ arg_idx + params.lifetime_params().count()
+ };
+ params.generic_params().nth(param_idx)
+ })();
+ (args, in_trait, param)
+ });
+ let (arg_list, of_trait, corresponding_param) = match location {
+ Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param),
+ _ => (None, None, None),
+ };
+ override_location.unwrap_or(TypeLocation::GenericArg {
+ args: arg_list,
+ of_trait,
+ corresponding_param,
+ })
+ };
+
let type_location = |node: &SyntaxNode| {
let parent = node.parent()?;
let res = match_ast! {
@@ -774,9 +908,12 @@ fn classify_name_ref(
ast::TypeBound(_) => TypeLocation::TypeBound,
// is this case needed?
ast::TypeBoundList(_) => TypeLocation::TypeBound,
- ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))),
+ ast::GenericArg(it) => generic_arg_location(it),
// is this case needed?
- ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
+ ast::GenericArgList(it) => {
+ let args = find_opt_node_in_file_compensated(sema, original_file, Some(it));
+ TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None }
+ },
ast::TupleField(_) => TypeLocation::TupleField,
_ => return None,
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
index 0309952c2..c45cc8d7b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs
@@ -2,8 +2,11 @@
use std::fmt;
-use hir::{Documentation, Mutability};
-use ide_db::{imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind};
+use hir::Mutability;
+use ide_db::{
+ documentation::Documentation, imports::import_assets::LocatedImport, RootDatabase, SnippetCap,
+ SymbolKind,
+};
use itertools::Itertools;
use smallvec::SmallVec;
use stdx::{impl_from, never};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index 1953eb479..dfe8fe7e2 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -12,7 +12,10 @@ pub(crate) mod literal;
use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
use ide_db::{
- helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind,
+ documentation::{Documentation, HasDocs},
+ helpers::item_name,
+ imports::import_assets::LocatedImport,
+ RootDatabase, SnippetCap, SymbolKind,
};
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
@@ -114,7 +117,7 @@ impl<'a> RenderContext<'a> {
}
// FIXME: remove this
- fn docs(&self, def: impl HasAttrs) -> Option<hir::Documentation> {
+ fn docs(&self, def: impl HasDocs) -> Option<Documentation> {
def.docs(self.db())
}
}
@@ -409,7 +412,7 @@ fn res_to_kind(resolution: ScopeDef) -> CompletionItemKind {
}
}
-fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<hir::Documentation> {
+fn scope_def_docs(db: &RootDatabase, resolution: ScopeDef) -> Option<Documentation> {
use hir::ModuleDef::*;
match resolution {
ScopeDef::ModuleDef(Module(it)) => it.docs(db),
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
index 728d236df..b218502f7 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs
@@ -1,7 +1,10 @@
//! Renderer for `enum` variants.
-use hir::{db::HirDatabase, Documentation, HasAttrs, StructKind};
-use ide_db::SymbolKind;
+use hir::{db::HirDatabase, StructKind};
+use ide_db::{
+ documentation::{Documentation, HasDocs},
+ SymbolKind,
+};
use crate::{
context::{CompletionContext, PathCompletionCtx, PathKind},
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
index ce7af1d34..68d175c2b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs
@@ -1,7 +1,7 @@
//! Renderer for macro invocations.
-use hir::{Documentation, HirDisplay};
-use ide_db::SymbolKind;
+use hir::HirDisplay;
+use ide_db::{documentation::Documentation, SymbolKind};
use syntax::SmolStr;
use crate::{
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
index d06abc5e9..6f998119b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs
@@ -1,7 +1,7 @@
//! Renderer for patterns.
-use hir::{db::HirDatabase, HasAttrs, Name, StructKind};
-use ide_db::SnippetCap;
+use hir::{db::HirDatabase, Name, StructKind};
+use ide_db::{documentation::HasDocs, SnippetCap};
use itertools::Itertools;
use syntax::SmolStr;
@@ -103,7 +103,7 @@ fn build_completion(
label: SmolStr,
lookup: SmolStr,
pat: String,
- def: impl HasAttrs + Copy,
+ def: impl HasDocs + Copy,
adt_ty: hir::Type,
// Missing in context of match statement completions
is_variant_missing: bool,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
index 1aaf39587..d8c134c53 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
@@ -67,11 +67,6 @@ struct Foo;
}
#[test]
-fn inside_nested_attr() {
- check(r#"#[cfg($0)]"#, expect![[]])
-}
-
-#[test]
fn with_existing_attr() {
check(
r#"#[no_mangle] #[$0] mcall!();"#,
@@ -636,6 +631,32 @@ mod cfg {
use super::*;
#[test]
+ fn inside_cfg() {
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg($0)]
+"#,
+ expect![[r#"
+ ba dbg
+ ba opt_level
+ ba test
+ "#]],
+ );
+ check(
+ r#"
+//- /main.rs cfg:test,dbg=false,opt_level=2
+#[cfg(b$0)]
+"#,
+ expect![[r#"
+ ba dbg
+ ba opt_level
+ ba test
+ "#]],
+ );
+ }
+
+ #[test]
fn cfg_target_endian() {
check(
r#"#[cfg(target_endian = $0"#,
@@ -644,6 +665,13 @@ mod cfg {
ba little
"#]],
);
+ check(
+ r#"#[cfg(target_endian = b$0"#,
+ expect![[r#"
+ ba big
+ ba little
+ "#]],
+ );
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
index 8c038c0fb..4cdfd546f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs
@@ -1286,3 +1286,57 @@ macro_rules! println {
expect![""],
)
}
+
+#[test]
+fn no_completions_for_external_doc_hidden_in_path() {
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+ pub mod server {
+ pub trait Span
+ }
+}
+pub mod bridge2 {
+ #[doc(hidden)]
+ pub mod server2 {
+ pub trait Span
+ }
+}
+"#,
+ expect![""],
+ );
+ // unless re-exported
+ check(
+ r#"
+//- /main.rs crate:main deps:dep
+fn main() {
+ Span$0
+}
+//- /lib.rs crate:dep
+#[doc(hidden)]
+pub mod bridge {
+ pub mod server {
+ pub trait Span
+ }
+}
+pub use bridge::server::Span;
+pub mod bridge2 {
+ #[doc(hidden)]
+ pub mod server2 {
+ pub trait Span2
+ }
+}
+pub use bridge2::server2::Span2;
+"#,
+ expect![[r#"
+ tt Span (use dep::Span)
+ tt Span2 (use dep::Span2)
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index 8cb1ff4a1..d518dd764 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -384,10 +384,8 @@ trait Trait2<T>: Trait1 {
fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
- ct CONST
- cp CONST_PARAM
en Enum
- ma makro!(…) macro_rules! makro
+ ma makro!(…) macro_rules! makro
md module
st Record
st Tuple
@@ -404,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
);
check(
r#"
-trait Trait2 {
+trait Trait2<T> {
type Foo;
}
fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -437,7 +434,6 @@ trait Tr<T> {
impl Tr<$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -485,7 +481,6 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -511,7 +506,6 @@ trait MyTrait<T, U> {
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -555,7 +549,6 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -581,7 +574,6 @@ trait MyTrait<T, U = u8> {
fn f(t: impl MyTrait<u8, u$0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -627,7 +619,6 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = $0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -653,7 +644,6 @@ trait MyTrait {
fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#,
expect![[r#"
- ct CONST
en Enum
ma makro!(…) macro_rules! makro
md module
@@ -668,6 +658,22 @@ fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
kw self::
"#]],
);
+
+ check(
+ r#"
+trait MyTrait {
+ const C: usize;
+};
+
+fn f(t: impl MyTrait<C = $0
+"#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
}
#[test]
@@ -719,3 +725,267 @@ pub struct S;
"#]],
)
}
+
+#[test]
+fn completes_const_and_type_generics_separately() {
+ // Function generic params
+ check(
+ r#"
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<F$0, _>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ // FIXME: This should probably also suggest completions for types, at least those that have
+ // associated constants usable in this position. For example, a user could be typing
+ // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position.
+ check(
+ r#"
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<_, $0>();
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Method generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<_, $0>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<X$0, _>();
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Associated type generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Bar
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo<T: Bar<Baz<(), $0> = ()>>() {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Type generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ fn main() {
+ let _: Foo::<_, $0> = Foo(());
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Type alias generic params
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ type Bar<const X: usize, U> = Foo<U, X>;
+ fn main() {
+ let _: Bar::<X$0, _> = Bar(());
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Enum variant params
+ check(
+ r#"
+ const X: usize = 0;
+ enum Foo<T, const N: usize> { A(T), B }
+ fn main() {
+ Foo::B::<(), $0>;
+ }
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Trait params
+ check(
+ r#"
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ impl Foo<(), $0> for () {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Trait alias params
+ check(
+ r#"
+ #![feature(trait_alias)]
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ trait Bar<const M: usize, U> = Foo<U, M>;
+ fn foo<T: Bar<X$0, ()>>() {}
+ "#,
+ expect![[r#"
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+
+ // Omitted lifetime params
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<F$0, _>; }
+ "#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+ // Explicit lifetime params
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, 'static, F$0, _>; }
+ "#,
+ expect![[r#"
+ ct CONST
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, F$0, _, _>; }
+ "#,
+ expect![[r#"
+ lt 'a
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 5e4562d9c..4ce80532e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -7,7 +7,7 @@
use arrayvec::ArrayVec;
use hir::{
- Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper,
+ Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, DocLinkDef,
ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
TypeAlias, Variant, Visibility,
@@ -649,3 +649,13 @@ impl From<ModuleDef> for Definition {
}
}
}
+
+impl From<DocLinkDef> for Definition {
+ fn from(def: DocLinkDef) -> Self {
+ match def {
+ DocLinkDef::ModuleDef(it) => it.into(),
+ DocLinkDef::Field(it) => it.into(),
+ DocLinkDef::SelfType(it) => it.into(),
+ }
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
new file mode 100644
index 000000000..26f3cd28a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/documentation.rs
@@ -0,0 +1,281 @@
+//! Documentation attribute related utilties.
+use either::Either;
+use hir::{
+ db::{DefDatabase, HirDatabase},
+ resolve_doc_path_on, AttrId, AttrSourceMap, AttrsWithOwner, HasAttrs, InFile,
+};
+use itertools::Itertools;
+use syntax::{
+ ast::{self, IsString},
+ AstToken,
+};
+use text_edit::{TextRange, TextSize};
+
+/// Holds documentation
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Documentation(String);
+
+impl Documentation {
+ pub fn new(s: String) -> Self {
+ Documentation(s)
+ }
+
+ pub fn as_str(&self) -> &str {
+ &self.0
+ }
+}
+
+impl From<Documentation> for String {
+ fn from(Documentation(string): Documentation) -> Self {
+ string
+ }
+}
+
+pub trait HasDocs: HasAttrs {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation>;
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef>;
+}
+/// A struct to map text ranges from [`Documentation`] back to TextRanges in the syntax tree.
+#[derive(Debug)]
+pub struct DocsRangeMap {
+ source_map: AttrSourceMap,
+ // (docstring-line-range, attr_index, attr-string-range)
+ // a mapping from the text range of a line of the [`Documentation`] to the attribute index and
+ // the original (untrimmed) syntax doc line
+ mapping: Vec<(TextRange, AttrId, TextRange)>,
+}
+
+impl DocsRangeMap {
+ /// Maps a [`TextRange`] relative to the documentation string back to its AST range
+ pub fn map(&self, range: TextRange) -> Option<InFile<TextRange>> {
+ let found = self.mapping.binary_search_by(|(probe, ..)| probe.ordering(range)).ok()?;
+ let (line_docs_range, idx, original_line_src_range) = self.mapping[found];
+ if !line_docs_range.contains_range(range) {
+ return None;
+ }
+
+ let relative_range = range - line_docs_range.start();
+
+ let InFile { file_id, value: source } = self.source_map.source_of_id(idx);
+ match source {
+ Either::Left(attr) => {
+ let string = get_doc_string_in_attr(attr)?;
+ let text_range = string.open_quote_text_range()?;
+ let range = TextRange::at(
+ text_range.end() + original_line_src_range.start() + relative_range.start(),
+ string.syntax().text_range().len().min(range.len()),
+ );
+ Some(InFile { file_id, value: range })
+ }
+ Either::Right(comment) => {
+ let text_range = comment.syntax().text_range();
+ let range = TextRange::at(
+ text_range.start()
+ + TextSize::try_from(comment.prefix().len()).ok()?
+ + original_line_src_range.start()
+ + relative_range.start(),
+ text_range.len().min(range.len()),
+ );
+ Some(InFile { file_id, value: range })
+ }
+ }
+ }
+}
+
+pub fn docs_with_rangemap(
+ db: &dyn DefDatabase,
+ attrs: &AttrsWithOwner,
+) -> Option<(Documentation, DocsRangeMap)> {
+ let docs =
+ attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value().map(|s| (s, attr.id)));
+ let indent = doc_indent(attrs);
+ let mut buf = String::new();
+ let mut mapping = Vec::new();
+ for (doc, idx) in docs {
+ if !doc.is_empty() {
+ let mut base_offset = 0;
+ for raw_line in doc.split('\n') {
+ let line = raw_line.trim_end();
+ let line_len = line.len();
+ let (offset, line) = match line.char_indices().nth(indent) {
+ Some((offset, _)) => (offset, &line[offset..]),
+ None => (0, line),
+ };
+ let buf_offset = buf.len();
+ buf.push_str(line);
+ mapping.push((
+ TextRange::new(buf_offset.try_into().ok()?, buf.len().try_into().ok()?),
+ idx,
+ TextRange::at(
+ (base_offset + offset).try_into().ok()?,
+ line_len.try_into().ok()?,
+ ),
+ ));
+ buf.push('\n');
+ base_offset += raw_line.len() + 1;
+ }
+ } else {
+ buf.push('\n');
+ }
+ }
+ buf.pop();
+ if buf.is_empty() {
+ None
+ } else {
+ Some((Documentation(buf), DocsRangeMap { mapping, source_map: attrs.source_map(db) }))
+ }
+}
+
+pub fn docs_from_attrs(attrs: &hir::Attrs) -> Option<String> {
+ let docs = attrs.by_key("doc").attrs().filter_map(|attr| attr.string_value());
+ let indent = doc_indent(attrs);
+ let mut buf = String::new();
+ for doc in docs {
+ // str::lines doesn't yield anything for the empty string
+ if !doc.is_empty() {
+ buf.extend(Itertools::intersperse(
+ doc.lines().map(|line| {
+ line.char_indices()
+ .nth(indent)
+ .map_or(line, |(offset, _)| &line[offset..])
+ .trim_end()
+ }),
+ "\n",
+ ));
+ }
+ buf.push('\n');
+ }
+ buf.pop();
+ if buf.is_empty() {
+ None
+ } else {
+ Some(buf)
+ }
+}
+
+macro_rules! impl_has_docs {
+ ($($def:ident,)*) => {$(
+ impl HasDocs for hir::$def {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ docs_from_attrs(&self.attrs(db)).map(Documentation)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>
+ ) -> Option<hir::DocLinkDef> {
+ resolve_doc_path_on(db, self, link, ns)
+ }
+ }
+ )*};
+}
+
+impl_has_docs![
+ Variant, Field, Static, Const, Trait, TraitAlias, TypeAlias, Macro, Function, Adt, Module,
+ Impl,
+];
+
+macro_rules! impl_has_docs_enum {
+ ($($variant:ident),* for $enum:ident) => {$(
+ impl HasDocs for hir::$variant {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ hir::$enum::$variant(self).docs(db)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>
+ ) -> Option<hir::DocLinkDef> {
+ hir::$enum::$variant(self).resolve_doc_path(db, link, ns)
+ }
+ }
+ )*};
+}
+
+impl_has_docs_enum![Struct, Union, Enum for Adt];
+
+impl HasDocs for hir::AssocItem {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ match self {
+ hir::AssocItem::Function(it) => it.docs(db),
+ hir::AssocItem::Const(it) => it.docs(db),
+ hir::AssocItem::TypeAlias(it) => it.docs(db),
+ }
+ }
+
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef> {
+ match self {
+ hir::AssocItem::Function(it) => it.resolve_doc_path(db, link, ns),
+ hir::AssocItem::Const(it) => it.resolve_doc_path(db, link, ns),
+ hir::AssocItem::TypeAlias(it) => it.resolve_doc_path(db, link, ns),
+ }
+ }
+}
+
+impl HasDocs for hir::ExternCrateDecl {
+ fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
+ let crate_docs =
+ docs_from_attrs(&self.resolved_crate(db)?.root_module().attrs(db)).map(String::from);
+ let decl_docs = docs_from_attrs(&self.attrs(db)).map(String::from);
+ match (decl_docs, crate_docs) {
+ (None, None) => None,
+ (Some(decl_docs), None) => Some(decl_docs),
+ (None, Some(crate_docs)) => Some(crate_docs),
+ (Some(mut decl_docs), Some(crate_docs)) => {
+ decl_docs.push('\n');
+ decl_docs.push('\n');
+ decl_docs += &crate_docs;
+ Some(decl_docs)
+ }
+ }
+ .map(Documentation::new)
+ }
+ fn resolve_doc_path(
+ self,
+ db: &dyn HirDatabase,
+ link: &str,
+ ns: Option<hir::Namespace>,
+ ) -> Option<hir::DocLinkDef> {
+ resolve_doc_path_on(db, self, link, ns)
+ }
+}
+
+fn get_doc_string_in_attr(it: &ast::Attr) -> Option<ast::String> {
+ match it.expr() {
+ // #[doc = lit]
+ Some(ast::Expr::Literal(lit)) => match lit.kind() {
+ ast::LiteralKind::String(it) => Some(it),
+ _ => None,
+ },
+ // #[cfg_attr(..., doc = "", ...)]
+ None => {
+ // FIXME: See highlight injection for what to do here
+ None
+ }
+ _ => None,
+ }
+}
+
+fn doc_indent(attrs: &hir::Attrs) -> usize {
+ attrs
+ .by_key("doc")
+ .attrs()
+ .filter_map(|attr| attr.string_value())
+ .flat_map(|s| s.lines())
+ .filter(|line| !line.chars().all(|c| c.is_whitespace()))
+ .map(|line| line.chars().take_while(|c| c.is_whitespace()).count())
+ .min()
+ .unwrap_or(0)
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index 49b37024a..57563a174 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -3505,8 +3505,8 @@ This serves two purposes:
"##,
},
Lint {
- label: "no_coverage",
- description: r##"# `no_coverage`
+ label: "coverage",
+ description: r##"# `coverage`
The tracking issue for this feature is: [#84605]
@@ -3514,7 +3514,7 @@ The tracking issue for this feature is: [#84605]
---
-The `no_coverage` attribute can be used to selectively disable coverage
+The `coverage` attribute can be used to selectively disable coverage
instrumentation in an annotated function. This might be useful to:
- Avoid instrumentation overhead in a performance critical function
@@ -3524,14 +3524,14 @@ instrumentation in an annotated function. This might be useful to:
## Example
```rust
-#![feature(no_coverage)]
+#![feature(coverage)]
// `foo()` will get coverage instrumentation (by default)
fn foo() {
// ...
}
-#[no_coverage]
+#[coverage(off)]
fn bar() {
// ...
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index 1eb8f0002..330af442f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -117,7 +117,7 @@ pub fn get_definition(
sema: &Semantics<'_, RootDatabase>,
token: SyntaxToken,
) -> Option<Definition> {
- for token in sema.descend_into_macros(token) {
+ for token in sema.descend_into_macros(token, 0.into()) {
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
if let Some(&[x]) = def.as_deref() {
return Some(x);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index e52dc3567..e475c5cd6 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -6,7 +6,7 @@ use hir::{
use itertools::Itertools;
use rustc_hash::FxHashSet;
use syntax::{
- ast::{self, HasName},
+ ast::{self, make, HasName},
utils::path_to_string_stripping_turbo_fish,
AstNode, SyntaxNode,
};
@@ -607,7 +607,7 @@ impl ImportCandidate {
fn for_name(sema: &Semantics<'_, RootDatabase>, name: &ast::Name) -> Option<Self> {
if sema
.scope(name.syntax())?
- .speculative_resolve(&ast::make::ext::ident_path(&name.text()))
+ .speculative_resolve(&make::ext::ident_path(&name.text()))
.is_some()
{
return None;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index f27ed485d..226def4d5 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -22,6 +22,7 @@ pub mod symbol_index;
pub mod traits;
pub mod ty_filter;
pub mod use_trivial_constructor;
+pub mod documentation;
pub mod imports {
pub mod import_assets;
@@ -94,18 +95,21 @@ impl fmt::Debug for RootDatabase {
}
impl Upcast<dyn ExpandDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn ExpandDatabase + 'static) {
&*self
}
}
impl Upcast<dyn DefDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn DefDatabase + 'static) {
&*self
}
}
impl Upcast<dyn HirDatabase> for RootDatabase {
+ #[inline]
fn upcast(&self) -> &(dyn HirDatabase + 'static) {
&*self
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 1d0cb426a..fb75b5b45 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -5,7 +5,7 @@ use either::Either;
use hir::{AsAssocItem, HirDisplay, SemanticsScope};
use rustc_hash::FxHashMap;
use syntax::{
- ast::{self, AstNode},
+ ast::{self, make, AstNode},
ted, SyntaxNode,
};
@@ -21,6 +21,7 @@ enum TypeOrConst {
}
type LifetimeName = String;
+type DefaultedParam = Either<hir::TypeParam, hir::ConstParam>;
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
///
@@ -115,7 +116,7 @@ impl<'a> PathTransform<'a> {
};
let mut type_substs: FxHashMap<hir::TypeParam, ast::Type> = Default::default();
let mut const_substs: FxHashMap<hir::ConstParam, SyntaxNode> = Default::default();
- let mut default_types: Vec<hir::TypeParam> = Default::default();
+ let mut defaulted_params: Vec<DefaultedParam> = Default::default();
self.generic_def
.into_iter()
.flat_map(|it| it.type_params(db))
@@ -138,8 +139,8 @@ impl<'a> PathTransform<'a> {
if let Some(default) =
&default.display_source_code(db, source_module.into(), false).ok()
{
- type_substs.insert(k, ast::make::ty(default).clone_for_update());
- default_types.push(k);
+ type_substs.insert(k, make::ty(default).clone_for_update());
+ defaulted_params.push(Either::Left(k));
}
}
}
@@ -155,11 +156,19 @@ impl<'a> PathTransform<'a> {
// is a standalone statement or a part of another expresson)
// and sometimes require slight modifications; see
// https://doc.rust-lang.org/reference/statements.html#expression-statements
+ // (default values in curly brackets can cause the same problem)
const_substs.insert(k, expr.syntax().clone());
}
}
- (Either::Left(_), None) => (), // FIXME: get default const value
- _ => (), // ignore mismatching params
+ (Either::Left(k), None) => {
+ if let Some(default) = k.default(db) {
+ if let Some(default) = default.expr() {
+ const_substs.insert(k, default.syntax().clone_for_update());
+ defaulted_params.push(Either::Right(k));
+ }
+ }
+ }
+ _ => (), // ignore mismatching params
});
let lifetime_substs: FxHashMap<_, _> = self
.generic_def
@@ -175,7 +184,7 @@ impl<'a> PathTransform<'a> {
target_module,
source_scope: self.source_scope,
};
- ctx.transform_default_type_substs(default_types);
+ ctx.transform_default_values(defaulted_params);
ctx
}
}
@@ -212,13 +221,19 @@ impl Ctx<'_> {
});
}
- fn transform_default_type_substs(&self, default_types: Vec<hir::TypeParam>) {
- for k in default_types {
- let v = self.type_substs.get(&k).unwrap();
+ fn transform_default_values(&self, defaulted_params: Vec<DefaultedParam>) {
+ // By now the default values are simply copied from where they are declared
+ // and should be transformed. As any value is allowed to refer to previous
+ // generic (both type and const) parameters, they should be all iterated left-to-right.
+ for param in defaulted_params {
+ let value = match param {
+ Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(),
+ Either::Right(k) => self.const_substs.get(&k).unwrap(),
+ };
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
// so that such operation is safe.
- let paths = postorder(&v.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
+ let paths = postorder(value).filter_map(ast::Path::cast).collect::<Vec<_>>();
for path in paths {
self.transform_path(path);
}
@@ -263,15 +278,14 @@ impl Ctx<'_> {
hir::ModuleDef::Trait(trait_ref),
false,
)?;
- match ast::make::ty_path(mod_path_to_ast(&found_path)) {
+ match make::ty_path(mod_path_to_ast(&found_path)) {
ast::Type::PathType(path_ty) => Some(path_ty),
_ => None,
}
});
- let segment = ast::make::path_segment_ty(subst.clone(), trait_ref);
- let qualified =
- ast::make::path_from_segments(std::iter::once(segment), false);
+ let segment = make::path_segment_ty(subst.clone(), trait_ref);
+ let qualified = make::path_from_segments(std::iter::once(segment), false);
ted::replace(path.syntax(), qualified.clone_for_update().syntax());
} else if let Some(path_ty) = ast::PathType::cast(parent) {
ted::replace(
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index aa0bb7cce..353a9749a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -71,12 +71,29 @@ impl Definition {
sema: &Semantics<'_, RootDatabase>,
new_name: &str,
) -> Result<SourceChange> {
+ // self.krate() returns None if
+ // self is a built-in attr, built-in type or tool module.
+ // it is not allowed for these defs to be renamed.
+ // cases where self.krate() is None is handled below.
+ if let Some(krate) = self.krate(sema.db) {
+ if !krate.origin(sema.db).is_local() {
+ bail!("Cannot rename a non-local definition.")
+ }
+ }
+
match *self {
Definition::Module(module) => rename_mod(sema, module, new_name),
+ Definition::ToolModule(_) => {
+ bail!("Cannot rename a tool module")
+ }
Definition::BuiltinType(_) => {
bail!("Cannot rename builtin type")
}
+ Definition::BuiltinAttr(_) => {
+ bail!("Cannot rename a builtin attr.")
+ }
Definition::SelfType(_) => bail!("Cannot rename `Self`"),
+ Definition::Macro(mac) => rename_reference(sema, Definition::Macro(mac), new_name),
def => rename_reference(sema, def, new_name),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
index e27e23867..ab2a25028 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rust_doc.rs
@@ -1,5 +1,7 @@
//! Rustdoc specific doc comment handling
+use crate::documentation::Documentation;
+
// stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933
pub fn is_rust_fence(s: &str) -> bool {
let mut seen_rust_tags = false;
@@ -32,3 +34,170 @@ pub fn is_rust_fence(s: &str) -> bool {
!seen_other_tags || seen_rust_tags
}
+
+const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
+
+pub fn format_docs(src: &Documentation) -> String {
+ format_docs_(src.as_str())
+}
+
+fn format_docs_(src: &str) -> String {
+ let mut processed_lines = Vec::new();
+ let mut in_code_block = false;
+ let mut is_rust = false;
+
+ for mut line in src.lines() {
+ if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
+ continue;
+ }
+
+ if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
+ {
+ in_code_block ^= true;
+
+ if in_code_block {
+ is_rust = is_rust_fence(header);
+
+ if is_rust {
+ line = "```rust";
+ }
+ }
+ }
+
+ if in_code_block {
+ let trimmed = line.trim_start();
+ if is_rust && trimmed.starts_with("##") {
+ line = &trimmed[1..];
+ }
+ }
+
+ processed_lines.push(line);
+ }
+ processed_lines.join("\n")
+}
+
+fn code_line_ignored_by_rustdoc(line: &str) -> bool {
+ let trimmed = line.trim();
+ trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_format_docs_adds_rust() {
+ let comment = "```\nfn some_rust() {}\n```";
+ assert_eq!(format_docs_(comment), "```rust\nfn some_rust() {}\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_plain_text() {
+ let comment = "```text\nthis is plain text\n```";
+ assert_eq!(format_docs_(comment), "```text\nthis is plain text\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_non_rust() {
+ let comment = "```sh\nsupposedly shell code\n```";
+ assert_eq!(format_docs_(comment), "```sh\nsupposedly shell code\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_rust_alias() {
+ let comment = "```ignore\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_complex_code_block_attrs() {
+ let comment = "```rust,no_run\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_error_codes() {
+ let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet b = 0 as *const _;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_skips_comments_in_rust_block() {
+ let comment =
+ "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
+ assert_eq!(format_docs_(comment), "```rust\n#stay1\nstay2\n```");
+ }
+
+ #[test]
+ fn test_format_docs_does_not_skip_lines_if_plain_text() {
+ let comment =
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
+ assert_eq!(
+ format_docs_(comment),
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
+ );
+ }
+
+ #[test]
+ fn test_format_docs_keeps_comments_outside_of_rust_block() {
+ let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_format_docs_preserves_newlines() {
+ let comment = "this\nis\nmultiline";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_rust() {
+ let comment = r#"```rust
+fn main(){}
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_text() {
+ let comment = r#"```text
+filler
+text
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_format_docs_handles_escape_double_hashes() {
+ let comment = r#"```rust
+let s = "foo
+## bar # baz";
+```"#;
+
+ assert_eq!(format_docs_(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_double_hashes_non_rust() {
+ let comment = r#"```markdown
+## A second-level heading
+```"#;
+ assert_eq!(format_docs_(comment), "```markdown\n## A second-level heading\n```");
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index d5abd0991..9c4f0ac8c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -6,7 +6,7 @@
use std::mem;
-use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
+use base_db::{salsa::Database, FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{
AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility,
};
@@ -221,7 +221,6 @@ impl Definition {
}
// def is crate root
- // FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
if module.is_crate_root() {
return SearchScope::reverse_dependencies(db, module.krate());
@@ -393,7 +392,10 @@ impl<'a> FindUsages<'a> {
let name = match self.def {
// special case crate modules as these do not have a proper name
Definition::Module(module) if module.is_crate_root() => {
- // FIXME: This assumes the crate name is always equal to its display name when it really isn't
+ // FIXME: This assumes the crate name is always equal to its display name when it
+ // really isn't
+ // we should instead look at the dependency edge name and recursively search our way
+ // up the ancestors
module
.krate()
.display_name(self.sema.db)
@@ -456,18 +458,19 @@ impl<'a> FindUsages<'a> {
it.text().trim_start_matches("r#") == name
})
.into_iter()
- .flat_map(|token| {
+ .flat_map(move |token| {
// FIXME: There should be optimization potential here
// Currently we try to descend everything we find which
// means we call `Semantics::descend_into_macros` on
// every textual hit. That function is notoriously
// expensive even for things that do not get down mapped
// into macros.
- sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent())
+ sema.descend_into_macros(token, offset).into_iter().filter_map(|it| it.parent())
})
};
for (text, file_id, search_range) in scope_files(sema, &search_scope) {
+ self.sema.db.unwind_if_cancelled();
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
@@ -504,6 +507,7 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new("super");
for (text, file_id, search_range) in scope_files(sema, &scope) {
+ self.sema.db.unwind_if_cancelled();
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
for offset in match_indices(&text, finder, search_range) {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index b54c43b29..f699f999b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -323,6 +323,8 @@ impl Query {
hir::ModuleDef::Adt(..)
| hir::ModuleDef::TypeAlias(..)
| hir::ModuleDef::BuiltinType(..)
+ | hir::ModuleDef::TraitAlias(..)
+ | hir::ModuleDef::Trait(..)
)
{
continue;
@@ -417,9 +419,16 @@ const CONST_WITH_INNER: () = {
mod b_mod;
+
+use define_struct as really_define_struct;
+use Macro as ItemLikeMacro;
+use Macro as Trait; // overlay namespaces
//- /b_mod.rs
struct StructInModB;
- "#,
+use super::Macro as SuperItemLikeMacro;
+use crate::b_mod::StructInModB as ThisStruct;
+use crate::Trait as IsThisJustATrait;
+"#,
);
let symbols: Vec<_> = Crate::from(db.test_crate())
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
index acf0a67de..8302b015d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs
@@ -1,10 +1,10 @@
//! Tools to work with format string literals for the `format_args!` family of macros.
-use crate::syntax_helpers::node_ext::macro_call_for_string_token;
use syntax::{
ast::{self, IsString},
- TextRange, TextSize,
+ AstNode, AstToken, TextRange, TextSize,
};
+// FIXME: This can probably be re-implemented via the HIR?
pub fn is_format_string(string: &ast::String) -> bool {
// Check if `string` is a format string argument of a macro invocation.
// `string` is a string literal, mapped down into the innermost macro expansion.
@@ -15,19 +15,9 @@ pub fn is_format_string(string: &ast::String) -> bool {
// This setup lets us correctly highlight the components of `concat!("{}", "bla")` format
// strings. It still fails for `concat!("{", "}")`, but that is rare.
(|| {
- let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?;
-
- if !matches!(
- name.text().as_str(),
- "format_args" | "format_args_nl" | "const_format_args" | "panic_2015" | "panic_2021"
- ) {
- return None;
- }
-
- // NB: we match against `panic_2015`/`panic_2021` here because they have a special-cased arm for
- // `"{}"`, which otherwise wouldn't get highlighted.
-
- Some(())
+ let lit = string.syntax().parent().and_then(ast::Literal::cast)?;
+ let fa = lit.syntax().parent().and_then(ast::FormatArgsExpr::cast)?;
+ (fa.template()? == ast::Expr::Literal(lit)).then_some(|| ())
})()
.is_some()
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
index 22ced69d8..e4e735cec 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -312,7 +312,6 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
ast::Expr::ArrayExpr(_)
| ast::Expr::AwaitExpr(_)
| ast::Expr::BinExpr(_)
- | ast::Expr::BoxExpr(_)
| ast::Expr::BreakExpr(_)
| ast::Expr::CallExpr(_)
| ast::Expr::CastExpr(_)
@@ -335,7 +334,10 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
| ast::Expr::LetExpr(_)
| ast::Expr::UnderscoreExpr(_)
| ast::Expr::YieldExpr(_)
- | ast::Expr::YeetExpr(_) => cb(expr),
+ | ast::Expr::YeetExpr(_)
+ | ast::Expr::OffsetOfExpr(_)
+ | ast::Expr::FormatArgsExpr(_)
+ | ast::Expr::AsmExpr(_) => cb(expr),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 1a00e2938..87ad5844c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -119,6 +119,35 @@
is_alias: false,
},
FileSymbol {
+ name: "ItemLikeMacro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 654..676,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 663..676,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "Macro",
def: Macro(
Macro {
@@ -353,6 +382,35 @@
is_alias: false,
},
FileSymbol {
+ name: "Trait",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 682..696,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 691..696,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "Union",
def: Adt(
Union(
@@ -552,6 +610,35 @@
is_alias: false,
},
FileSymbol {
+ name: "really_define_struct",
+ def: Macro(
+ Macro {
+ id: MacroRulesId(
+ MacroRulesId(
+ 1,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 0,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 611..648,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 628..648,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "trait_fn",
def: Function(
Function {
@@ -632,6 +719,35 @@
},
[
FileSymbol {
+ name: "IsThisJustATrait",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 111..143,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 127..143,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
name: "StructInModB",
def: Adt(
Struct(
@@ -660,6 +776,93 @@
container_name: None,
is_alias: false,
},
+ FileSymbol {
+ name: "SuperItemLikeMacro",
+ def: Macro(
+ Macro {
+ id: Macro2Id(
+ Macro2Id(
+ 0,
+ ),
+ ),
+ },
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 25..59,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 41..59,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "ThisStruct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 65..105,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 95..105,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
+ FileSymbol {
+ name: "ThisStruct",
+ def: Adt(
+ Struct(
+ Struct {
+ id: StructId(
+ 3,
+ ),
+ },
+ ),
+ ),
+ loc: DeclarationLocation {
+ hir_file_id: FileId(
+ FileId(
+ 1,
+ ),
+ ),
+ ptr: SyntaxNodePtr {
+ kind: USE_TREE,
+ range: 65..105,
+ },
+ name_ptr: SyntaxNodePtr {
+ kind: NAME,
+ range: 95..105,
+ },
+ },
+ container_name: None,
+ is_alias: false,
+ },
],
),
]
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
index f96ea29ae..a915391ad 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/use_trivial_constructor.rs
@@ -1,31 +1,29 @@
//! Functionality for generating trivial constructors
use hir::StructKind;
-use syntax::ast;
+use syntax::ast::{make, Expr, Path};
/// given a type return the trivial constructor (if one exists)
pub fn use_trivial_constructor(
db: &crate::RootDatabase,
- path: ast::Path,
+ path: Path,
ty: &hir::Type,
-) -> Option<ast::Expr> {
+) -> Option<Expr> {
match ty.as_adt() {
Some(hir::Adt::Enum(x)) => {
if let &[variant] = &*x.variants(db) {
if variant.kind(db) == hir::StructKind::Unit {
- let path = ast::make::path_qualified(
+ let path = make::path_qualified(
path,
- syntax::ast::make::path_segment(ast::make::name_ref(
- &variant.name(db).to_smol_str(),
- )),
+ make::path_segment(make::name_ref(&variant.name(db).to_smol_str())),
);
- return Some(syntax::ast::make::expr_path(path));
+ return Some(make::expr_path(path));
}
}
}
Some(hir::Adt::Struct(x)) if x.kind(db) == StructKind::Unit => {
- return Some(syntax::ast::make::expr_path(path));
+ return Some(make::expr_path(path));
}
_ => {}
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index f54cdd63b..7ca0a0eab 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -157,6 +157,7 @@ struct S;
fn macro_diag_builtin() {
check_diagnostics(
r#"
+//- minicore: fmt
#[rustc_builtin_macro]
macro_rules! env {}
@@ -166,9 +167,6 @@ macro_rules! include {}
#[rustc_builtin_macro]
macro_rules! compile_error {}
-#[rustc_builtin_macro]
-macro_rules! format_args { () => {} }
-
fn main() {
// Test a handful of built-in (eager) macros:
@@ -189,7 +187,7 @@ fn main() {
// Lazy:
format_args!();
- //^^^^^^^^^^^ error: no rule matches input tokens
+ //^^^^^^^^^^^ error: Syntax Error in Expansion: expected expression
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 6238c7e09..8265e0b1c 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -1,10 +1,37 @@
+use either::Either;
+use hir::InFile;
use syntax::{
ast::{self, HasArgList},
- AstNode, TextRange,
+ AstNode, SyntaxNodePtr, TextRange,
};
use crate::{adjusted_display_range, Diagnostic, DiagnosticCode, DiagnosticsContext};
+// Diagnostic: mismatched-tuple-struct-pat-arg-count
+//
+// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
+pub(crate) fn mismatched_tuple_struct_pat_arg_count(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::MismatchedTupleStructPatArgCount,
+) -> Diagnostic {
+ let s = if d.found == 1 { "" } else { "s" };
+ let s2 = if d.expected == 1 { "" } else { "s" };
+ let message = format!(
+ "this pattern has {} field{s}, but the corresponding tuple struct has {} field{s2}",
+ d.found, d.expected
+ );
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0023"),
+ message,
+ invalid_args_range(
+ ctx,
+ d.expr_or_pat.clone().map(|it| it.either(Into::into, Into::into)),
+ d.expected,
+ d.found,
+ ),
+ )
+}
+
// Diagnostic: mismatched-arg-count
//
// This diagnostic is triggered if a function is invoked with an incorrect amount of arguments.
@@ -14,31 +41,63 @@ pub(crate) fn mismatched_arg_count(
) -> Diagnostic {
let s = if d.expected == 1 { "" } else { "s" };
let message = format!("expected {} argument{s}, found {}", d.expected, d.found);
- Diagnostic::new(DiagnosticCode::RustcHardError("E0107"), message, invalid_args_range(ctx, d))
+ Diagnostic::new(
+ DiagnosticCode::RustcHardError("E0107"),
+ message,
+ invalid_args_range(ctx, d.call_expr.clone().map(Into::into), d.expected, d.found),
+ )
}
-fn invalid_args_range(ctx: &DiagnosticsContext<'_>, d: &hir::MismatchedArgCount) -> TextRange {
- adjusted_display_range::<ast::Expr>(ctx, d.call_expr.clone().map(|it| it.into()), &|expr| {
- let arg_list = match expr {
- ast::Expr::CallExpr(call) => call.arg_list()?,
- ast::Expr::MethodCallExpr(call) => call.arg_list()?,
+fn invalid_args_range(
+ ctx: &DiagnosticsContext<'_>,
+ source: InFile<SyntaxNodePtr>,
+ expected: usize,
+ found: usize,
+) -> TextRange {
+ adjusted_display_range::<Either<ast::Expr, ast::TupleStructPat>>(ctx, source, &|expr| {
+ let (text_range, r_paren_token, expected_arg) = match expr {
+ Either::Left(ast::Expr::CallExpr(call)) => {
+ let arg_list = call.arg_list()?;
+ (
+ arg_list.syntax().text_range(),
+ arg_list.r_paren_token(),
+ arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
+ Either::Left(ast::Expr::MethodCallExpr(call)) => {
+ let arg_list = call.arg_list()?;
+ (
+ arg_list.syntax().text_range(),
+ arg_list.r_paren_token(),
+ arg_list.args().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
+ Either::Right(pat) => {
+ let r_paren = pat.r_paren_token()?;
+ let l_paren = pat.l_paren_token()?;
+ (
+ l_paren.text_range().cover(r_paren.text_range()),
+ Some(r_paren),
+ pat.fields().nth(expected).map(|it| it.syntax().text_range()),
+ )
+ }
_ => return None,
};
- if d.found < d.expected {
- if d.found == 0 {
- return Some(arg_list.syntax().text_range());
+ if found < expected {
+ if found == 0 {
+ return Some(text_range);
}
- if let Some(r_paren) = arg_list.r_paren_token() {
+ if let Some(r_paren) = r_paren_token {
return Some(r_paren.text_range());
}
}
- if d.expected < d.found {
- if d.expected == 0 {
- return Some(arg_list.syntax().text_range());
+ if expected < found {
+ if expected == 0 {
+ return Some(text_range);
}
- let zip = arg_list.args().nth(d.expected).zip(arg_list.r_paren_token());
+ let zip = expected_arg.zip(r_paren_token);
if let Some((arg, r_paren)) = zip {
- return Some(arg.syntax().text_range().cover(r_paren.text_range()));
+ return Some(arg.cover(r_paren.text_range()));
}
}
@@ -331,4 +390,21 @@ fn g() {
"#,
)
}
+
+ #[test]
+ fn tuple_struct_pat() {
+ check_diagnostics(
+ r#"
+struct S(u32, u32);
+fn f(
+ S(a, b, c): S,
+ // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+ S(): S,
+ // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 2 fields
+ S(e, f, .., g, d): S
+ // ^^^^^^^^^ error: this pattern has 4 fields, but the corresponding tuple struct has 2 fields
+) {}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
index 82a9a3bd5..06b03d3d1 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_match_arms.rs
@@ -319,6 +319,7 @@ fn main() {
match Either::A {
Either::A => (),
Either::B() => (),
+ // ^^ error: this pattern has 0 fields, but the corresponding tuple struct has 1 field
}
}
"#,
@@ -334,9 +335,11 @@ enum A { B(isize, isize), C }
fn main() {
match A::B(1, 2) {
A::B(_, _, _) => (),
+ // ^^ error: this pattern has 3 fields, but the corresponding tuple struct has 2 fields
}
match A::B(1, 2) {
A::C(_) => (),
+ // ^^^ error: this pattern has 1 field, but the corresponding tuple struct has 0 fields
}
}
"#,
@@ -846,6 +849,7 @@ fn main() {
struct Foo { }
fn main(f: Foo) {
match f { Foo { bar } => () }
+ // ^^^ error: no such field
}
"#,
);
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
index e0c3bedce..d056e5c85 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs
@@ -76,7 +76,7 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Di
"variable does not need to be mutable",
ast,
)
- .experimental() // Not supporting `#[allow(unused_mut)]` leads to false positive.
+ .experimental() // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive.
.with_fixes(fixes)
}
@@ -1173,4 +1173,27 @@ fn f() {
"#,
);
}
+
+ #[test]
+ fn regression_15623() {
+ check_diagnostics(
+ r#"
+//- minicore: fn
+
+struct Foo;
+
+impl Foo {
+ fn needs_mut(&mut self) {}
+}
+
+fn foo(mut foo: Foo) {
+ let mut call_me = || {
+ let 0 = 1 else { return };
+ foo.needs_mut();
+ };
+ call_me();
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
index a34a5824f..290c16c9d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/no_such_field.rs
@@ -1,3 +1,4 @@
+use either::Either;
use hir::{db::ExpandDatabase, HasSource, HirDisplay, Semantics};
use ide_db::{base_db::FileId, source_change::SourceChange, RootDatabase};
use syntax::{
@@ -12,22 +13,39 @@ use crate::{fix, Assist, Diagnostic, DiagnosticCode, DiagnosticsContext};
//
// This diagnostic is triggered if created structure does not have field provided in record.
pub(crate) fn no_such_field(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Diagnostic {
- Diagnostic::new_with_syntax_node_ptr(
- ctx,
- DiagnosticCode::RustcHardError("E0559"),
- "no such field",
- d.field.clone().map(|it| it.into()),
- )
- .with_fixes(fixes(ctx, d))
+ let node = d.field.clone().map(|it| it.either(Into::into, Into::into));
+ if d.private {
+ // FIXME: quickfix to add required visibility
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0451"),
+ "field is private",
+ node,
+ )
+ } else {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0559"),
+ "no such field",
+ node,
+ )
+ .with_fixes(fixes(ctx, d))
+ }
}
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::NoSuchField) -> Option<Vec<Assist>> {
- let root = ctx.sema.db.parse_or_expand(d.field.file_id);
- missing_record_expr_field_fixes(
- &ctx.sema,
- d.field.file_id.original_file(ctx.sema.db),
- &d.field.value.to_node(&root),
- )
+ // FIXME: quickfix for pattern
+ match &d.field.value {
+ Either::Left(ptr) => {
+ let root = ctx.sema.db.parse_or_expand(d.field.file_id);
+ missing_record_expr_field_fixes(
+ &ctx.sema,
+ d.field.file_id.original_file(ctx.sema.db),
+ &ptr.to_node(&root),
+ )
+ }
+ _ => None,
+ }
}
fn missing_record_expr_field_fixes(
@@ -118,13 +136,34 @@ mod tests {
r#"
struct S { foo: i32, bar: () }
impl S {
- fn new() -> S {
+ fn new(
+ s@S {
+ //^ 💡 error: missing structure fields:
+ //| - bar
+ foo,
+ baz: baz2,
+ //^^^^^^^^^ error: no such field
+ qux
+ //^^^ error: no such field
+ }: S
+ ) -> S {
+ S {
+ //^ 💡 error: missing structure fields:
+ //| - bar
+ foo,
+ baz: baz2,
+ //^^^^^^^^^ error: no such field
+ qux
+ //^^^ error: no such field
+ } = s;
S {
//^ 💡 error: missing structure fields:
//| - bar
foo: 92,
baz: 62,
//^^^^^^^ 💡 error: no such field
+ qux
+ //^^^ error: no such field
}
}
}
@@ -295,4 +334,38 @@ fn main() {
"#,
)
}
+
+ #[test]
+ fn test_struct_field_private() {
+ check_diagnostics(
+ r#"
+mod m {
+ pub struct Struct {
+ field: u32,
+ field2: u32,
+ }
+}
+fn f(s@m::Struct {
+ field: f,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+}: m::Struct) {
+ // assignee expression
+ m::Struct {
+ field: 0,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+ } = s;
+ m::Struct {
+ field: 0,
+ //^^^^^^^^ error: field is private
+ field2
+ //^^^^^^ error: field is private
+ };
+}
+"#,
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
index 7de9a9a32..495ea7487 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/undeclared_label.rs
@@ -35,6 +35,25 @@ fn foo() {
}
#[test]
+ fn while_let_loop_with_label_in_condition() {
+ check_diagnostics(
+ r#"
+fn foo() {
+ let mut optional = Some(0);
+
+ 'my_label: while let Some(a) = match optional {
+ None => break 'my_label,
+ Some(val) => Some(val),
+ } {
+ optional = None;
+ continue 'my_label;
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn for_loop() {
check_diagnostics(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
index 0aa439f79..c4ac59ec2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/useless_braces.rs
@@ -1,3 +1,4 @@
+use hir::InFile;
use ide_db::{base_db::FileId, source_change::SourceChange};
use itertools::Itertools;
use syntax::{ast, AstNode, SyntaxNode};
@@ -39,6 +40,7 @@ pub(crate) fn useless_braces(
"Unnecessary braces in use statement".to_string(),
use_range,
)
+ .with_main_node(InFile::new(file_id.into(), node.clone()))
.with_fixes(Some(vec![fix(
"remove_braces",
"Remove unnecessary braces",
@@ -156,4 +158,23 @@ use a::{c, d::e};
"#,
);
}
+
+ #[test]
+ fn respect_lint_attributes_for_unused_braces() {
+ check_diagnostics(
+ r#"
+mod b {}
+#[allow(unused_braces)]
+use {b};
+"#,
+ );
+ check_diagnostics(
+ r#"
+mod b {}
+#[deny(unused_braces)]
+use {b};
+ //^^^ 💡 error: Unnecessary braces in use statement
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index b1b9b4b8e..ebe197a67 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -369,6 +369,7 @@ pub fn diagnostics(
AnyDiagnostic::UnresolvedProcMacro(d) => handlers::unresolved_proc_macro::unresolved_proc_macro(&ctx, &d, config.proc_macros_enabled, config.proc_attr_macros_enabled),
AnyDiagnostic::UnusedMut(d) => handlers::mutability_errors::unused_mut(&ctx, &d),
AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d),
+ AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d),
};
res.push(d)
}
@@ -432,7 +433,8 @@ fn handle_lint_attributes(
diagnostics_of_range: &mut FxHashMap<InFile<SyntaxNode>, &mut Diagnostic>,
) {
let file_id = sema.hir_file_for(root);
- for ev in root.preorder() {
+ let mut preorder = root.preorder();
+ while let Some(ev) = preorder.next() {
match ev {
syntax::WalkEvent::Enter(node) => {
for attr in node.children().filter_map(ast::Attr::cast) {
@@ -515,7 +517,7 @@ fn parse_lint_attribute(
let Some((tag, args_tt)) = attr.as_simple_call() else {
return;
};
- let serevity = match tag.as_str() {
+ let severity = match tag.as_str() {
"allow" => Severity::Allow,
"warn" => Severity::Warning,
"forbid" | "deny" => Severity::Error,
@@ -523,12 +525,12 @@ fn parse_lint_attribute(
};
for lint in parse_tt_as_comma_sep_paths(args_tt).into_iter().flatten() {
if let Some(lint) = lint.as_single_name_ref() {
- job(rustc_stack.entry(lint.to_string()).or_default(), serevity);
+ job(rustc_stack.entry(lint.to_string()).or_default(), severity);
}
if let Some(tool) = lint.qualifier().and_then(|x| x.as_single_name_ref()) {
if let Some(name_ref) = &lint.segment().and_then(|x| x.name_ref()) {
if tool.to_string() == "clippy" {
- job(clippy_stack.entry(name_ref.to_string()).or_default(), serevity);
+ job(clippy_stack.entry(name_ref.to_string()).or_default(), severity);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
index a8e883690..60fcbbbd3 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -560,8 +560,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
placeholder_value.autoref_kind = self
.sema
.resolve_method_call_as_callable(code)
- .and_then(|callable| callable.receiver_param(self.sema.db))
- .map(|(self_param, _)| self_param.kind())
+ .and_then(|callable| {
+ let (self_param, _) = callable.receiver_param(self.sema.db)?;
+ Some(self_param.source(self.sema.db)?.value.kind())
+ })
.unwrap_or(ast::SelfParamKind::Owned);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
index f994c284c..fb79b5dc2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
@@ -94,10 +94,9 @@ pub(crate) fn annotations(
enum_
.variants(db)
.into_iter()
- .map(|variant| {
+ .filter_map(|variant| {
variant.source(db).and_then(|node| name_range(db, node, file_id))
})
- .flatten()
.for_each(|range| {
let (annotation_range, target_position) = mk_ranges(range);
annotations.push(Annotation {
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index dd1d0d75c..f834f2ce5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -74,18 +74,20 @@ pub(crate) fn incoming_calls(
Some(calls.into_items())
}
-pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Option<Vec<CallItem>> {
+pub(crate) fn outgoing_calls(
+ db: &RootDatabase,
+ FilePosition { file_id, offset }: FilePosition,
+) -> Option<Vec<CallItem>> {
let sema = Semantics::new(db);
- let file_id = position.file_id;
let file = sema.parse(file_id);
let file = file.syntax();
- let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+ let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
IDENT => 1,
_ => 0,
})?;
let mut calls = CallLocations::default();
- sema.descend_into_macros(token)
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|it| it.parent_ancestors().nth(1).and_then(ast::Item::cast))
.filter_map(|item| match item {
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index d240127f3..37a177622 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -16,6 +16,7 @@ use hir::{db::HirDatabase, Adt, AsAssocItem, AssocItem, AssocItemContainer, HasA
use ide_db::{
base_db::{CrateOrigin, LangCrateOrigin, ReleaseChannel, SourceDatabase},
defs::{Definition, NameClass, NameRefClass},
+ documentation::{docs_with_rangemap, Documentation, HasDocs},
helpers::pick_best_token,
RootDatabase,
};
@@ -131,19 +132,19 @@ pub(crate) fn remove_links(markdown: &str) -> String {
// |===
pub(crate) fn external_docs(
db: &RootDatabase,
- position: &FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
target_dir: Option<&OsStr>,
sysroot: Option<&OsStr>,
) -> Option<DocumentationLinks> {
let sema = &Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
- let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
+ let file = sema.parse(file_id).syntax().clone();
+ let token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
IDENT | INT_NUMBER | T![self] => 3,
T!['('] | T![')'] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
})?;
- let token = sema.descend_into_macros_single(token);
+ let token = sema.descend_into_macros_single(token, offset);
let node = token.parent()?;
let definition = match_ast! {
@@ -171,7 +172,7 @@ pub(crate) fn external_docs(
/// Extracts all links from a given markdown text returning the definition text range, link-text
/// and the namespace if known.
pub(crate) fn extract_definitions_from_docs(
- docs: &hir::Documentation,
+ docs: &Documentation,
) -> Vec<(TextRange, String, Option<hir::Namespace>)> {
Parser::new_with_broken_link_callback(
docs.as_str(),
@@ -285,7 +286,7 @@ impl DocCommentToken {
let original_start = doc_token.text_range().start();
let relative_comment_offset = offset - original_start - prefix_len;
- sema.descend_into_macros(doc_token).into_iter().find_map(|t| {
+ sema.descend_into_macros(doc_token, offset).into_iter().find_map(|t| {
let (node, descended_prefix_len) = match_ast! {
match t {
ast::Comment(comment) => (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?),
@@ -297,7 +298,7 @@ impl DocCommentToken {
let abs_in_expansion_offset = token_start + relative_comment_offset + descended_prefix_len;
let (attributes, def) = doc_attributes(sema, &node)?;
- let (docs, doc_mapping) = attributes.docs_with_rangemap(sema.db)?;
+ let (docs, doc_mapping) = docs_with_rangemap(sema.db, &attributes)?;
let (in_expansion_range, link, ns) =
extract_definitions_from_docs(&docs).into_iter().find_map(|(range, link, ns)| {
let mapped = doc_mapping.map(range)?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
index 05a64b33b..9ae70ae66 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs
@@ -1,10 +1,11 @@
use std::ffi::OsStr;
use expect_test::{expect, Expect};
-use hir::{HasAttrs, Semantics};
+use hir::Semantics;
use ide_db::{
base_db::{FilePosition, FileRange},
defs::Definition,
+ documentation::{Documentation, HasDocs},
RootDatabase,
};
use itertools::Itertools;
@@ -78,7 +79,7 @@ fn check_doc_links(ra_fixture: &str) {
fn def_under_cursor(
sema: &Semantics<'_, RootDatabase>,
position: &FilePosition,
-) -> (Definition, hir::Documentation) {
+) -> (Definition, Documentation) {
let (docs, def) = sema
.parse(position.file_id)
.syntax()
@@ -96,7 +97,7 @@ fn def_under_cursor(
fn node_to_def(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
-) -> Option<Option<(Option<hir::Documentation>, Definition)>> {
+) -> Option<Option<(Option<Documentation>, Definition)>> {
Some(match_ast! {
match node {
ast::SourceFile(it) => sema.to_def(&it).map(|def| (def.docs(sema.db), Definition::Module(def))),
@@ -518,6 +519,62 @@ fn function();
}
#[test]
+fn doc_links_field() {
+ check_doc_links(
+ r#"
+/// [`S::f`]
+/// [`S2::f`]
+/// [`T::0`]
+/// [`U::a`]
+/// [`E::A::f`]
+/// [`E::B::0`]
+struct S$0 {
+ f: i32,
+ //^ S::f
+ //^ S2::f
+}
+type S2 = S;
+struct T(i32);
+ //^^^ T::0
+union U {
+ a: i32,
+ //^ U::a
+}
+enum E {
+ A { f: i32 },
+ //^ E::A::f
+ B(i32),
+ //^^^ E::B::0
+}
+"#,
+ );
+}
+
+#[test]
+fn doc_links_field_via_self() {
+ check_doc_links(
+ r#"
+/// [`Self::f`]
+struct S$0 {
+ f: i32,
+ //^ Self::f
+}
+"#,
+ );
+}
+
+#[test]
+fn doc_links_tuple_field_via_self() {
+ check_doc_links(
+ r#"
+/// [`Self::0`]
+struct S$0(i32);
+ //^^^ Self::0
+"#,
+ );
+}
+
+#[test]
fn rewrite_html_root_url() {
check_rewrite(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index d6bbf2bf7..119a9c7c3 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -40,28 +40,33 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
// struct Bar;
// ```
- let derive = sema.descend_into_macros(tok.clone()).into_iter().find_map(|descended| {
- let hir_file = sema.hir_file_for(&descended.parent()?);
- if !hir_file.is_derive_attr_pseudo_expansion(db) {
- return None;
- }
+ let derive =
+ sema.descend_into_macros(tok.clone(), 0.into()).into_iter().find_map(|descended| {
+ let hir_file = sema.hir_file_for(&descended.parent()?);
+ if !hir_file.is_derive_attr_pseudo_expansion(db) {
+ return None;
+ }
- let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
- // up map out of the #[derive] expansion
- let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
- let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
- let expansions = sema.expand_derive_macro(&attr)?;
- let idx = attr
- .token_tree()?
- .token_trees_and_tokens()
- .filter_map(NodeOrToken::into_token)
- .take_while(|it| it != &token)
- .filter(|it| it.kind() == T![,])
- .count();
- let expansion =
- format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?);
- Some(ExpandedMacro { name, expansion })
- });
+ let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
+ // up map out of the #[derive] expansion
+ let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
+ let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
+ let expansions = sema.expand_derive_macro(&attr)?;
+ let idx = attr
+ .token_tree()?
+ .token_trees_and_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .take_while(|it| it != &token)
+ .filter(|it| it.kind() == T![,])
+ .count();
+ let expansion = format(
+ db,
+ SyntaxKind::MACRO_ITEMS,
+ position.file_id,
+ expansions.get(idx).cloned()?,
+ );
+ Some(ExpandedMacro { name, expansion })
+ });
if derive.is_some() {
return derive;
diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
index f90618222..3d89599c5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs
@@ -17,8 +17,6 @@ use crate::FileRange;
// Extends or shrinks the current selection to the encompassing syntactic construct
// (expression, statement, item, module, etc). It works with multiple cursors.
//
-// This is a standard LSP feature and not a protocol extension.
-//
// |===
// | Editor | Shortcut
//
@@ -142,8 +140,10 @@ fn extend_tokens_from_range(
// compute original mapped token range
let extended = {
- let fst_expanded = sema.descend_into_macros_single(first_token.clone());
- let lst_expanded = sema.descend_into_macros_single(last_token.clone());
+ let fst_expanded =
+ sema.descend_into_macros_single(first_token.clone(), original_range.start());
+ let lst_expanded =
+ sema.descend_into_macros_single(last_token.clone(), original_range.end());
let mut lca =
algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
lca = shallowest_node(&lca);
@@ -154,13 +154,16 @@ fn extend_tokens_from_range(
};
// Compute parent node range
- let validate = |token: &SyntaxToken| -> bool {
- let expanded = sema.descend_into_macros_single(token.clone());
- let parent = match expanded.parent() {
- Some(it) => it,
- None => return false,
- };
- algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended)
+ let validate = |offset: TextSize| {
+ let extended = &extended;
+ move |token: &SyntaxToken| -> bool {
+ let expanded = sema.descend_into_macros_single(token.clone(), offset);
+ let parent = match expanded.parent() {
+ Some(it) => it,
+ None => return false,
+ };
+ algo::least_common_ancestor(extended, &parent).as_ref() == Some(extended)
+ }
};
// Find the first and last text range under expanded parent
@@ -168,14 +171,14 @@ fn extend_tokens_from_range(
let token = token.prev_token()?;
skip_trivia_token(token, Direction::Prev)
})
- .take_while(validate)
+ .take_while(validate(original_range.start()))
.last()?;
let last = successors(Some(last_token), |token| {
let token = token.next_token()?;
skip_trivia_token(token, Direction::Next)
})
- .take_while(validate)
+ .take_while(validate(original_range.end()))
.last()?;
let range = first.text_range().cover(last.text_range());
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
index c39c696cf..7e0fab426 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
@@ -20,16 +20,16 @@ use crate::{
// - fields in patterns will navigate to the field declaration of the struct, union or variant
pub(crate) fn goto_declaration(
db: &RootDatabase,
- position: FilePosition,
+ position @ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
+ let file = sema.parse(file_id).syntax().clone();
let original_token = file
- .token_at_offset(position.offset)
+ .token_at_offset(offset)
.find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
let range = original_token.text_range();
let info: Vec<NavigationTarget> = sema
- .descend_into_macros(original_token)
+ .descend_into_macros(original_token, offset)
.iter()
.filter_map(|token| {
let parent = token.parent()?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 21471ab2a..e09b9f391 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -29,45 +29,39 @@ use syntax::{ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextRange, T};
// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[]
pub(crate) fn goto_definition(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = &Semantics::new(db);
- let file = sema.parse(position.file_id).syntax().clone();
- let original_token =
- pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
- IDENT
- | INT_NUMBER
- | LIFETIME_IDENT
- | T![self]
- | T![super]
- | T![crate]
- | T![Self]
- | COMMENT => 4,
- // index and prefix ops
- T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
- kind if kind.is_keyword() => 2,
- T!['('] | T![')'] => 2,
- kind if kind.is_trivia() => 0,
- _ => 1,
- })?;
+ let file = sema.parse(file_id).syntax().clone();
+ let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
+ IDENT
+ | INT_NUMBER
+ | LIFETIME_IDENT
+ | T![self]
+ | T![super]
+ | T![crate]
+ | T![Self]
+ | COMMENT => 4,
+ // index and prefix ops
+ T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
+ kind if kind.is_keyword() => 2,
+ T!['('] | T![')'] => 2,
+ kind if kind.is_trivia() => 0,
+ _ => 1,
+ })?;
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
- return doc_comment.get_definition_with_descend_at(
- sema,
- position.offset,
- |def, _, link_range| {
- let nav = def.try_to_nav(db)?;
- Some(RangeInfo::new(link_range, vec![nav]))
- },
- );
+ return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| {
+ let nav = def.try_to_nav(db)?;
+ Some(RangeInfo::new(link_range, vec![nav]))
+ });
}
let navs = sema
- .descend_into_macros(original_token.clone())
+ .descend_into_macros(original_token.clone(), offset)
.into_iter()
.filter_map(|token| {
let parent = token.parent()?;
if let Some(tt) = ast::TokenTree::cast(parent) {
- if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), position.file_id)
- {
+ if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
return Some(vec![x]);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
index 37166bdbd..544c6b423 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs
@@ -22,20 +22,19 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
pub(crate) fn goto_implementation(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = Semantics::new(db);
- let source_file = sema.parse(position.file_id);
+ let source_file = sema.parse(file_id);
let syntax = source_file.syntax().clone();
- let original_token =
- pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
- IDENT | T![self] | INT_NUMBER => 1,
- _ => 0,
- })?;
+ let original_token = pick_best_token(syntax.token_at_offset(offset), |kind| match kind {
+ IDENT | T![self] | INT_NUMBER => 1,
+ _ => 0,
+ })?;
let range = original_token.text_range();
let navs =
- sema.descend_into_macros(original_token)
+ sema.descend_into_macros(original_token, offset)
.into_iter()
.filter_map(|token| token.parent().and_then(ast::NameLike::cast))
.filter_map(|node| match &node {
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index 6048990f7..955923d76 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -16,13 +16,13 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav};
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
db: &RootDatabase,
- position: FilePosition,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
let sema = hir::Semantics::new(db);
- let file: ast::SourceFile = sema.parse(position.file_id);
+ let file: ast::SourceFile = sema.parse(file_id);
let token: SyntaxToken =
- pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
+ pick_best_token(file.syntax().token_at_offset(offset), |kind| match kind {
IDENT | INT_NUMBER | T![self] => 2,
kind if kind.is_trivia() => 0,
_ => 1,
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
}
};
let range = token.text_range();
- sema.descend_into_macros(token)
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|token| {
let ty = sema
diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
index 43e89a334..46a0464e9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -15,6 +15,7 @@ use syntax::{
SyntaxKind::{self, IDENT, INT_NUMBER},
SyntaxNode, SyntaxToken, TextRange, T,
};
+use text_edit::TextSize;
use crate::{navigation_target::ToNav, references, NavigationTarget, TryToNav};
@@ -51,7 +52,7 @@ pub struct HighlightRelatedConfig {
pub(crate) fn highlight_related(
sema: &Semantics<'_, RootDatabase>,
config: HighlightRelatedConfig,
- FilePosition { offset, file_id }: FilePosition,
+ pos @ FilePosition { offset, file_id }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
let _p = profile::span("highlight_related");
let syntax = sema.parse(file_id).syntax().clone();
@@ -79,7 +80,7 @@ pub(crate) fn highlight_related(
}
T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id),
- _ if config.references => highlight_references(sema, &syntax, token, file_id),
+ _ if config.references => highlight_references(sema, &syntax, token, pos),
_ => None,
}
}
@@ -129,9 +130,9 @@ fn highlight_references(
sema: &Semantics<'_, RootDatabase>,
node: &SyntaxNode,
token: SyntaxToken,
- file_id: FileId,
+ FilePosition { file_id, offset }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
- let defs = find_defs(sema, token.clone());
+ let defs = find_defs(sema, token.clone(), offset);
let usages = defs
.iter()
.filter_map(|&d| {
@@ -455,8 +456,12 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
}
}
-fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
- sema.descend_into_macros(token)
+fn find_defs(
+ sema: &Semantics<'_, RootDatabase>,
+ token: SyntaxToken,
+ offset: TextSize,
+) -> FxHashSet<Definition> {
+ sema.descend_into_macros(token, offset)
.into_iter()
.filter_map(|token| IdentClass::classify_token(sema, &token))
.map(IdentClass::definitions_no_ops)
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs
index 40659e6c2..21934b948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs
@@ -162,9 +162,9 @@ fn hover_simple(
// prefer descending the same token kind in attribute expansions, in normal macros text
// equivalency is more important
let descended = if in_attr {
- [sema.descend_into_macros_with_kind_preference(original_token.clone())].into()
+ [sema.descend_into_macros_with_kind_preference(original_token.clone(), offset)].into()
} else {
- sema.descend_into_macros_with_same_text(original_token.clone())
+ sema.descend_into_macros_with_same_text(original_token.clone(), offset)
};
let descended = || descended.iter();
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index a33a6ee18..f72ce37d1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,12 +3,13 @@ use std::fmt::Display;
use either::Either;
use hir::{
- Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasAttrs, HasSource, HirDisplay, Layout,
- LayoutError, Semantics, TypeInfo,
+ Adt, AsAssocItem, AttributeTemplate, CaptureKind, HasSource, HirDisplay, Layout, LayoutError,
+ Semantics, TypeInfo,
};
use ide_db::{
base_db::SourceDatabase,
defs::Definition,
+ documentation::{Documentation, HasDocs},
famous_defs::FamousDefs,
generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES},
syntax_helpers::insert_whitespace_into_node,
@@ -470,7 +471,7 @@ pub(super) fn definition(
Definition::SelfType(impl_def) => {
impl_def.self_ty(db).as_adt().map(|adt| label_and_docs(db, adt))?
}
- Definition::GenericParam(it) => label_and_docs(db, it),
+ Definition::GenericParam(it) => (it.display(db).to_string(), None),
Definition::Label(it) => return Some(Markup::fenced_block(&it.name(db).display(db))),
Definition::ExternCrateDecl(it) => label_and_docs(db, it),
// FIXME: We should be able to show more info about these
@@ -616,9 +617,9 @@ fn render_builtin_attr(db: &RootDatabase, attr: hir::BuiltinAttr) -> Option<Mark
markup(Some(docs.replace('*', "\\*")), desc, None)
}
-fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<hir::Documentation>)
+fn label_and_docs<D>(db: &RootDatabase, def: D) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
{
let label = def.display(db).to_string();
let docs = def.docs(db);
@@ -631,9 +632,9 @@ fn label_and_layout_info_and_docs<D, E, E2>(
config: &HoverConfig,
layout_extractor: E,
layout_offset_extractor: E2,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Result<Layout, LayoutError>,
E2: Fn(&Layout) -> Option<u64>,
{
@@ -657,9 +658,9 @@ fn label_value_and_layout_info_and_docs<D, E, E2, E3, V>(
value_extractor: E,
layout_extractor: E2,
layout_tag_extractor: E3,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Option<V>,
E2: Fn(&D) -> Result<Layout, LayoutError>,
E3: Fn(&Layout) -> Option<usize>,
@@ -686,9 +687,9 @@ fn label_value_and_docs<D, E, V>(
db: &RootDatabase,
def: D,
value_extractor: E,
-) -> (String, Option<hir::Documentation>)
+) -> (String, Option<Documentation>)
where
- D: HasAttrs + HirDisplay,
+ D: HasDocs + HirDisplay,
E: Fn(&D) -> Option<V>,
V: Display,
{
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index ddc71dffa..81d6db564 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -1557,6 +1557,49 @@ fn test_hover_function_show_types() {
}
#[test]
+fn test_hover_function_associated_type_params() {
+ check(
+ r#"
+trait Foo { type Bar; }
+impl Foo for i32 { type Bar = i64; }
+fn foo(arg: <i32 as Foo>::Bar) {}
+fn main() { foo$0; }
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn foo(arg: <i32 as Foo>::Bar)
+ ```
+ "#]],
+ );
+
+ check(
+ r#"
+trait Foo<T> { type Bar<U>; }
+impl Foo<i64> for i32 { type Bar<U> = i32; }
+fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>) {}
+fn main() { foo$0; }
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ fn foo(arg: <<i32 as Foo<i64>>::Bar<i8> as Foo<i64>>::Bar<i8>)
+ ```
+ "#]],
+ );
+}
+
+#[test]
fn test_hover_function_pointer_show_identifiers() {
check(
r#"type foo$0 = fn(a: i32, b: i32) -> i32;"#,
@@ -3292,7 +3335,50 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
```
```rust
- struct ST<const C: usize, T = Foo>
+ struct ST<const C: usize = 1, T = Foo>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_default_value() {
+ check(
+ r#"
+struct Foo;
+struct S$0T<const C: usize = {40 + 2}, T = Foo>(T);
+"#,
+ expect![[r#"
+ *ST*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ struct ST<const C: usize = {const}, T = Foo>
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn const_generic_default_value_2() {
+ check(
+ r#"
+struct Foo;
+const VAL = 1;
+struct S$0T<const C: usize = VAL, T = Foo>(T);
+"#,
+ expect![[r#"
+ *ST*
+
+ ```rust
+ test
+ ```
+
+ ```rust
+ struct ST<const C: usize = VAL, T = Foo>
```
"#]],
);
@@ -6469,3 +6555,42 @@ fn test() {
"#]],
);
}
+
+#[test]
+fn generic_params_disabled_by_cfg() {
+ check(
+ r#"
+struct S<#[cfg(never)] T>;
+fn test() {
+ let s$0: S = S;
+}
+"#,
+ expect![[r#"
+ *s*
+
+ ```rust
+ let s: S // size = 0, align = 1
+ ```
+ "#]],
+ );
+}
+
+#[test]
+fn format_args_arg() {
+ check(
+ r#"
+//- minicore: fmt
+fn test() {
+ let foo = 0;
+ format_args!("{}", foo$0);
+}
+"#,
+ expect![[r#"
+ *foo*
+
+ ```rust
+ let foo: i32 // size = 4, align = 4
+ ```
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 292591674..a5d070fe7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -52,6 +52,28 @@ pub struct InlayHintsConfig {
pub closure_style: ClosureStyle,
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
+ pub fields_to_resolve: InlayFieldsToResolve,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct InlayFieldsToResolve {
+ pub resolve_text_edits: bool,
+ pub resolve_hint_tooltip: bool,
+ pub resolve_label_tooltip: bool,
+ pub resolve_label_location: bool,
+ pub resolve_label_command: bool,
+}
+
+impl InlayFieldsToResolve {
+ pub const fn empty() -> Self {
+ Self {
+ resolve_text_edits: false,
+ resolve_hint_tooltip: false,
+ resolve_label_tooltip: false,
+ resolve_label_location: false,
+ resolve_label_command: false,
+ }
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -123,11 +145,13 @@ pub struct InlayHint {
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<TextEdit>,
+ pub needs_resolve: bool,
}
impl InlayHint {
fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
+ needs_resolve: false,
range,
kind,
label: InlayHintLabel::from(")"),
@@ -139,6 +163,7 @@ impl InlayHint {
}
fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
+ needs_resolve: false,
range,
kind,
label: InlayHintLabel::from("("),
@@ -196,6 +221,10 @@ impl InlayHintLabel {
}),
}
}
+
+ pub fn needs_resolve(&self) -> bool {
+ self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some())
+ }
}
impl From<String> for InlayHintLabel {
@@ -529,6 +558,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
#[cfg(test)]
mod tests {
+
use expect_test::Expect;
use hir::ClosureStyle;
use itertools::Itertools;
@@ -538,7 +568,7 @@ mod tests {
use crate::DiscriminantHints;
use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints};
- use super::ClosureReturnTypeHints;
+ use super::{ClosureReturnTypeHints, InlayFieldsToResolve};
pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig {
discriminant_hints: DiscriminantHints::Never,
@@ -559,6 +589,7 @@ mod tests {
param_names_for_lifetime_elision_hints: false,
max_length: None,
closing_brace_hints_min_lines: None,
+ fields_to_resolve: InlayFieldsToResolve::empty(),
};
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
type_hints: true,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index 6d6bd315e..631807d99 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -137,21 +137,23 @@ pub(super) fn hints(
}
_ => continue,
};
+ let label = InlayHintLabel::simple(
+ if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
+ Some(InlayTooltip::Markdown(format!(
+ "`{}` → `{}` ({coercion} coercion)",
+ source.display(sema.db),
+ target.display(sema.db),
+ ))),
+ None,
+ );
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(),
pad_left: false,
pad_right: false,
position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before },
kind: InlayKind::Adjustment,
- label: InlayHintLabel::simple(
- if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() },
- Some(InlayTooltip::Markdown(format!(
- "`{}` → `{}` ({coercion} coercion)",
- source.display(sema.db),
- target.display(sema.db),
- ))),
- None,
- ),
+ label,
text_edit: None,
});
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
index 07b9f9cc1..680035c72 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs
@@ -99,6 +99,7 @@ pub(super) fn hints(
None => pat.syntax().text_range(),
};
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve() || text_edit.is_some(),
range: match type_ascriptable {
Some(Some(t)) => text_range.cover(t.text_range()),
_ => text_range,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
index 343cf17e5..35504ffa7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs
@@ -50,9 +50,10 @@ pub(super) fn hints(
_ => return,
};
acc.push(InlayHint {
+ needs_resolve: false,
range,
kind: InlayKind::BindingMode,
- label: r.to_string().into(),
+ label: r.into(),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
@@ -68,9 +69,10 @@ pub(super) fn hints(
hir::BindingMode::Ref(Mutability::Shared) => "ref",
};
acc.push(InlayHint {
+ needs_resolve: false,
range: pat.syntax().text_range(),
kind: InlayKind::BindingMode,
- label: bm.to_string().into(),
+ label: bm.into(),
text_edit: None,
position: InlayHintPosition::Before,
pad_left: false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index b621a8dda..12e46c0f8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -57,10 +57,12 @@ pub(super) fn hints(
}
}
}
+ let label = label_of_ty(famous_defs, config, &ty)?;
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(),
kind: InlayKind::Chaining,
- label: label_of_ty(famous_defs, config, &ty)?,
+ label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: true,
@@ -128,6 +130,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 147..154,
@@ -152,6 +155,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -221,6 +225,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 143..179,
@@ -245,6 +250,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -298,6 +304,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 143..179,
@@ -322,6 +329,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -389,6 +397,7 @@ fn main() {
"<i32, bool>>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 246..265,
@@ -426,6 +435,7 @@ fn main() {
"<i32, bool>>",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -474,7 +484,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -487,7 +497,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -495,6 +505,7 @@ fn main() {
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..224,
@@ -511,7 +522,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -524,7 +535,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -532,6 +543,7 @@ fn main() {
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..206,
@@ -548,7 +560,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9289..9297,
+ range: 10739..10747,
},
),
tooltip: "",
@@ -561,7 +573,7 @@ fn main() {
file_id: FileId(
1,
),
- range: 9321..9325,
+ range: 10771..10775,
},
),
tooltip: "",
@@ -569,6 +581,7 @@ fn main() {
" = ()>",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 174..189,
@@ -593,6 +606,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
@@ -655,6 +669,7 @@ fn main() {
],
},
),
+ needs_resolve: true,
},
InlayHint {
range: 145..185,
@@ -679,6 +694,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 145..168,
@@ -703,6 +719,7 @@ fn main() {
"",
],
text_edit: None,
+ needs_resolve: true,
},
InlayHint {
range: 222..228,
@@ -725,6 +742,7 @@ fn main() {
},
],
text_edit: None,
+ needs_resolve: true,
},
]
"#]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index 2cefd5acd..2b68538c1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -109,6 +109,7 @@ pub(super) fn hints(
let linked_location = name_range.map(|range| FileRange { file_id, range });
acc.push(InlayHint {
+ needs_resolve: linked_location.is_some(),
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
label: InlayHintLabel::simple(label, None, linked_location),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
index 9d5defcbb..d691303c1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs
@@ -31,9 +31,10 @@ pub(super) fn hints(
let range = closure.syntax().first_token()?.prev_token()?.text_range();
let range = TextRange::new(range.end() - TextSize::from(1), range.end());
acc.push(InlayHint {
+ needs_resolve: false,
range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple("move", None, None),
+ label: InlayHintLabel::from("move"),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -43,6 +44,7 @@ pub(super) fn hints(
}
};
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("("),
@@ -59,23 +61,25 @@ pub(super) fn hints(
// force cache the source file, otherwise sema lookup will potentially panic
_ = sema.parse_or_expand(source.file());
+ let label = InlayHintLabel::simple(
+ format!(
+ "{}{}",
+ match capture.kind() {
+ hir::CaptureKind::SharedRef => "&",
+ hir::CaptureKind::UniqueSharedRef => "&unique ",
+ hir::CaptureKind::MutableRef => "&mut ",
+ hir::CaptureKind::Move => "",
+ },
+ capture.display_place(sema.db)
+ ),
+ None,
+ source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
+ );
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: move_kw_range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple(
- format!(
- "{}{}",
- match capture.kind() {
- hir::CaptureKind::SharedRef => "&",
- hir::CaptureKind::UniqueSharedRef => "&unique ",
- hir::CaptureKind::MutableRef => "&mut ",
- hir::CaptureKind::Move => "",
- },
- capture.display_place(sema.db)
- ),
- None,
- source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
- ),
+ label,
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -84,9 +88,10 @@ pub(super) fn hints(
if idx != last {
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
- label: InlayHintLabel::simple(", ", None, None),
+ label: InlayHintLabel::from(", "),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
@@ -95,6 +100,7 @@ pub(super) fn hints(
}
}
acc.push(InlayHint {
+ needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(")"),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
index 3b41db0f1..204967cd7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs
@@ -64,6 +64,7 @@ pub(super) fn hints(
};
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve() || text_edit.is_some(),
range: param_list.syntax().text_range(),
kind: InlayKind::Type,
label,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
index c4d2ac75c..26dc6fa8b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs
@@ -79,6 +79,7 @@ fn variant_hints(
None,
);
acc.push(InlayHint {
+ needs_resolve: label.needs_resolve(),
range: match eq_token {
Some(t) => range.cover(t.text_range()),
_ => range,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index 5fce11b78..7b05e32ad 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -22,6 +22,7 @@ pub(super) fn hints(
}
let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
+ needs_resolve: false,
range: t.text_range(),
kind: InlayKind::Lifetime,
label: label.into(),
@@ -185,6 +186,7 @@ pub(super) fn hints(
let angle_tok = gpl.l_angle_token()?;
let is_empty = gpl.generic_params().next().is_none();
acc.push(InlayHint {
+ needs_resolve: false,
range: angle_tok.text_range(),
kind: InlayKind::Lifetime,
label: format!(
@@ -200,6 +202,7 @@ pub(super) fn hints(
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
+ needs_resolve: false,
range: func.name()?.syntax().text_range(),
kind: InlayKind::GenericParamList,
label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
index fc297a8d8..f18e6421c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs
@@ -31,9 +31,10 @@ pub(super) fn hints(
if ty.lifetime().is_none() {
let t = ty.amp_token()?;
acc.push(InlayHint {
+ needs_resolve: false,
range: t.text_range(),
kind: InlayKind::Lifetime,
- label: "'static".to_owned().into(),
+ label: "'static".into(),
text_edit: None,
position: InlayHintPosition::After,
pad_left: false,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
index c4f43f411..b4260d825 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs
@@ -57,6 +57,7 @@ pub(super) fn hints(
let label =
InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location);
InlayHint {
+ needs_resolve: label.needs_resolve(),
range,
kind: InlayKind::Parameter,
label,
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index bf77d55d5..aee03d218 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -91,9 +91,9 @@ pub use crate::{
MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
},
inlay_hints::{
- AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
- InlayTooltip, LifetimeElisionHints,
+ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
+ InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition,
+ InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
@@ -111,7 +111,7 @@ pub use crate::{
HighlightConfig, HlRange,
},
};
-pub use hir::{Documentation, Semantics};
+pub use hir::Semantics;
pub use ide_assists::{
Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
};
@@ -124,6 +124,7 @@ pub use ide_db::{
Cancelled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange,
SourceRoot, SourceRootId,
},
+ documentation::Documentation,
label::Label,
line_index::{LineCol, LineIndex},
search::{ReferenceCategory, SearchScope},
@@ -484,7 +485,7 @@ impl Analysis {
sysroot: Option<&OsStr>,
) -> Cancellable<doc_links::DocumentationLinks> {
self.with_db(|db| {
- doc_links::external_docs(db, &position, target_dir, sysroot).unwrap_or_default()
+ doc_links::external_docs(db, position, target_dir, sysroot).unwrap_or_default()
})
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 17f3771b1..2ca2b5b1d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -99,7 +99,7 @@ pub(crate) fn moniker(
});
}
let navs = sema
- .descend_into_macros(original_token.clone())
+ .descend_into_macros(original_token.clone(), offset)
.into_iter()
.filter_map(|token| {
IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index d1479dd1e..32f211c6b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -4,14 +4,15 @@ use std::fmt;
use either::Either;
use hir::{
- symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasContainer, HasSource,
- HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
+ symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, HirDisplay, HirFileId,
+ InFile, LocalSource, ModuleSource,
};
use ide_db::{
base_db::{FileId, FileRange},
- SymbolKind,
+ defs::Definition,
+ documentation::{Documentation, HasDocs},
+ RootDatabase, SymbolKind,
};
-use ide_db::{defs::Definition, RootDatabase};
use stdx::never;
use syntax::{
ast::{self, HasName},
@@ -175,8 +176,12 @@ impl TryToNav for FileSymbol {
Some(NavigationTarget {
file_id: full_range.file_id,
- name: if self.is_alias { self.def.name(db)?.to_smol_str() } else { self.name.clone() },
- alias: if self.is_alias { Some(self.name.clone()) } else { None },
+ name: self
+ .is_alias
+ .then(|| self.def.name(db))
+ .flatten()
+ .map_or_else(|| self.name.clone(), |it| it.to_smol_str()),
+ alias: self.is_alias.then(|| self.name.clone()),
kind: Some(hir::ModuleDefId::from(self.def).into()),
full_range: full_range.range,
focus_range,
@@ -323,7 +328,7 @@ impl ToNavFromAst for hir::TraitAlias {
impl<D> TryToNav for D
where
- D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay,
+ D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay,
D::Ast: ast::HasName,
{
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 813f9ed94..2d0295692 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -126,7 +126,7 @@ pub(crate) fn find_defs<'a>(
)
});
token.map(|token| {
- sema.descend_into_macros_with_same_text(token)
+ sema.descend_into_macros_with_same_text(token, offset)
.into_iter()
.filter_map(|it| ast::NameLike::cast(it.parent()?))
.filter_map(move |name_like| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs
index dae8e71e8..ac9df5ed6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs
@@ -2634,4 +2634,33 @@ use qux as frob;
// ",
// );
}
+
+ #[test]
+ fn disallow_renaming_for_non_local_definition() {
+ check(
+ "Baz",
+ r#"
+//- /lib.rs crate:lib new_source_root:library
+pub struct S;
+//- /main.rs crate:main deps:lib new_source_root:local
+use lib::S$0;
+"#,
+ "error: Cannot rename a non-local definition.",
+ );
+ }
+
+ #[test]
+ fn disallow_renaming_for_builtin_macros() {
+ check(
+ "Baz",
+ r#"
+//- minicore: derive, hash
+//- /main.rs crate:main
+use core::hash::Hash;
+#[derive(H$0ash)]
+struct A;
+ "#,
+ "error: Cannot rename a non-local definition.",
+ )
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index 5f87a7855..2d528c642 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -7,6 +7,7 @@ use ide_assists::utils::test_related_attribute;
use ide_db::{
base_db::{FilePosition, FileRange},
defs::Definition,
+ documentation::docs_from_attrs,
helpers::visit_file_defs,
search::SearchScope,
FxHashMap, FxHashSet, RootDatabase, SymbolKind,
@@ -496,7 +497,7 @@ const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
&["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
- attrs.docs().map_or(false, |doc| {
+ docs_from_attrs(attrs).map_or(false, |doc| {
let mut in_code_block = false;
for line in String::from(doc).lines() {
diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
index 7795be54e..e020b52e1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs
@@ -4,12 +4,11 @@
use std::collections::BTreeSet;
use either::Either;
-use hir::{
- AssocItem, GenericParam, HasAttrs, HirDisplay, ModuleDef, PathResolution, Semantics, Trait,
-};
+use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait};
use ide_db::{
active_parameter::{callable_for_node, generic_def_for_node},
base_db::FilePosition,
+ documentation::{Documentation, HasDocs},
FxIndexMap,
};
use stdx::format_to;
@@ -28,7 +27,7 @@ use crate::RootDatabase;
/// edited.
#[derive(Debug)]
pub struct SignatureHelp {
- pub doc: Option<String>,
+ pub doc: Option<Documentation>,
pub signature: String,
pub active_parameter: Option<usize>,
parameters: Vec<TextRange>,
@@ -67,17 +66,20 @@ impl SignatureHelp {
}
/// Computes parameter information for the given position.
-pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Option<SignatureHelp> {
+pub(crate) fn signature_help(
+ db: &RootDatabase,
+ FilePosition { file_id, offset }: FilePosition,
+) -> Option<SignatureHelp> {
let sema = Semantics::new(db);
- let file = sema.parse(position.file_id);
+ let file = sema.parse(file_id);
let file = file.syntax();
let token = file
- .token_at_offset(position.offset)
+ .token_at_offset(offset)
.left_biased()
// if the cursor is sandwiched between two space tokens and the call is unclosed
// this prevents us from leaving the CallExpression
.and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?;
- let token = sema.descend_into_macros_single(token);
+ let token = sema.descend_into_macros_single(token, offset);
for node in token.parent_ancestors() {
match_ast! {
@@ -176,7 +178,7 @@ fn signature_help_for_call(
let mut fn_params = None;
match callable.kind() {
hir::CallableKind::Function(func) => {
- res.doc = func.docs(db).map(|it| it.into());
+ res.doc = func.docs(db);
format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
@@ -184,11 +186,11 @@ fn signature_help_for_call(
});
}
hir::CallableKind::TupleStruct(strukt) => {
- res.doc = strukt.docs(db).map(|it| it.into());
+ res.doc = strukt.docs(db);
format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
- res.doc = variant.docs(db).map(|it| it.into());
+ res.doc = variant.docs(db);
format_to!(
res.signature,
"enum {}::{}",
@@ -202,7 +204,7 @@ fn signature_help_for_call(
res.signature.push('(');
{
if let Some((self_param, _)) = callable.receiver_param(db) {
- format_to!(res.signature, "{}", self_param)
+ format_to!(res.signature, "{}", self_param.display(db))
}
let mut buf = String::new();
for (idx, (pat, ty)) in callable.params(db).into_iter().enumerate() {
@@ -262,38 +264,38 @@ fn signature_help_for_generics(
let db = sema.db;
match generics_def {
hir::GenericDef::Function(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
// eg. `None::<u8>`
// We'll use the signature of the enum, but include the docs of the variant.
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
let enum_ = it.parent_enum(db);
format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
@@ -1315,6 +1317,25 @@ id! {
}
#[test]
+ fn fn_signature_for_method_call_defined_in_macro() {
+ check(
+ r#"
+macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
+struct S;
+id! {
+ impl S {
+ fn foo<'a>(&'a mut self) {}
+ }
+}
+fn test() { S.foo($0); }
+"#,
+ expect![[r#"
+ fn foo(&'a mut self)
+ "#]],
+ );
+ }
+
+ #[test]
fn call_info_for_lambdas() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index d8696198d..aabd26da2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -12,6 +12,7 @@ use ide_db::{
};
use syntax::{AstNode, SyntaxKind::*, TextRange, T};
+use crate::inlay_hints::InlayFieldsToResolve;
use crate::{
hover::hover_for_definition,
inlay_hints::AdjustmentHintsMode,
@@ -125,6 +126,7 @@ impl StaticIndex<'_> {
max_length: Some(25),
closure_capture_hints: false,
closing_brace_hints_min_lines: Some(25),
+ fields_to_resolve: InlayFieldsToResolve::empty(),
},
file_id,
None,
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index d2c77e2dc..c9ee460a1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -66,6 +66,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
None => format!("{}", krate.into_raw()),
};
format_to!(buf, "Crate: {}\n", display_crate(krate));
+ format_to!(buf, "Enabled cfgs: {:?}\n", crate_graph[krate].cfg_options);
let deps = crate_graph[krate]
.dependencies
.iter()
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index ae9723640..bb01c81d6 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -395,10 +395,10 @@ fn traverse(
NodeOrToken::Token(token) if token.kind() != COMMENT => {
let token = match attr_or_derive_item {
Some(AttrOrDerive::Attr(_)) => {
- sema.descend_into_macros_with_kind_preference(token)
+ sema.descend_into_macros_with_kind_preference(token, 0.into())
}
Some(AttrOrDerive::Derive(_)) | None => {
- sema.descend_into_macros_single(token)
+ sema.descend_into_macros_single(token, 0.into())
}
};
match token.parent().and_then(ast::NameLike::cast) {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
index 2ed57e201..2ef131594 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs
@@ -17,6 +17,7 @@ pub(super) fn highlight_format_string(
return;
}
+ // FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 8e96bfa01..7d00282fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -617,6 +617,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
CONST => SymbolKind::Const,
STATIC => SymbolKind::Static,
IDENT_PAT => SymbolKind::Local,
+ FORMAT_ARGS_ARG => SymbolKind::Local,
_ => return default.into(),
};
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
index 2657a6414..71f4d0724 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs
@@ -5,8 +5,8 @@ use std::mem;
use either::Either;
use hir::{InFile, Semantics};
use ide_db::{
- active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence,
- SymbolKind,
+ active_parameter::ActiveParameter, base_db::FileId, defs::Definition,
+ documentation::docs_with_rangemap, rust_doc::is_rust_fence, SymbolKind,
};
use syntax::{
ast::{self, AstNode, IsString, QuoteOffsets},
@@ -118,7 +118,7 @@ pub(super) fn doc_comment(
let src_file_id = src_file_id.into();
// Extract intra-doc links and emit highlights for them.
- if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) {
+ if let Some((docs, doc_mapping)) = docs_with_rangemap(sema.db, &attributes) {
extract_definitions_from_docs(&docs)
.into_iter()
.filter_map(|(range, link, ns)| {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 3ac8aa9cc..64e614cec 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -45,17 +45,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
</style>
<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="brace">{</span>
- <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span><span class="parenthesis">)</span>
<span class="brace">}</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
-<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">const_format_args</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">rustc_builtin_macro</span><span class="attribute_bracket attribute">]</span>
-<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute library">macro_export</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">format_args_nl</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">mod</span> <span class="module declaration">panic</span> <span class="brace">{</span>
@@ -75,7 +69,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_display<span class="parenthesis">(</span><span class="punctuation">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="colon">:</span>expr<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
- <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+ <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panicking<span class="colon">:</span><span class="colon">:</span>panic_fmt<span class="parenthesis">(</span>const_format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>fmt<span class="comma">,</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -92,7 +86,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
@@ -114,18 +108,18 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "Hello, world!"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "The number is 1"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "(3, 4)"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "4"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "1 2"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "0042" with leading zerosV</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1 1 2"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "test"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "2 1"</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "a 3 b"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span> <span class="comment">// =&gt; "{2}"</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier">&lt;</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
@@ -140,10 +134,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">&gt;</span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span>
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span>
@@ -167,16 +161,24 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span>
<span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"mov eax, </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
- <span class="macro">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="none macro">format_args</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="none macro">toho</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more {}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{} asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
+ <span class="macro unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
+ <span class="string_literal macro">"mov {0}, {1}"</span><span class="comma macro">,</span>
+ <span class="string_literal macro">"add {0}, 5"</span><span class="comma macro">,</span>
+ <span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span>
+ <span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span>
+ <span class="parenthesis macro">)</span><span class="semicolon">;</span>
+
+ <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+ <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 8749d355c..542d89925 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -401,19 +401,14 @@ fn test_string_highlighting() {
// thus, we have to copy the macro definition from `std`
check_highlighting(
r#"
+//- minicore: fmt
macro_rules! println {
($($arg:tt)*) => ({
- $crate::io::_print($crate::format_args_nl!($($arg)*));
+ $crate::io::_print(format_args_nl!($($arg)*));
})
}
#[rustc_builtin_macro]
#[macro_export]
-macro_rules! format_args {}
-#[rustc_builtin_macro]
-#[macro_export]
-macro_rules! const_format_args {}
-#[rustc_builtin_macro]
-#[macro_export]
macro_rules! format_args_nl {}
mod panic {
@@ -433,7 +428,7 @@ mod panic {
$crate::panicking::panic_display(&$arg)
),
($fmt:expr, $($arg:tt)+) => (
- $crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
+ $crate::panicking::panic_fmt(const_format_args!($fmt, $($arg)+))
),
}
}
@@ -450,7 +445,7 @@ macro_rules! concat {}
macro_rules! toho {
() => ($crate::panic!("not yet implemented"));
- ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", $crate::format_args!($($arg)+)));
+ ($($arg:tt)+) => ($crate::panic!("not yet implemented: {}", format_args!($($arg)+)));
}
fn main() {
@@ -534,7 +529,15 @@ fn main() {
assert!(true, "{}", 1);
assert!(true, "{} asdasd", 1);
toho!("{}fmt", 0);
- asm!("mov eax, {0}");
+ let i: u64 = 3;
+ let o: u64;
+ asm!(
+ "mov {0}, {1}",
+ "add {0}, 5",
+ out(reg) o,
+ in(reg) i,
+ );
+
format_args!(concat!("{}"), "{}");
format_args!("{} {} {} {} {} {}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash);
}"#,
diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs
index c7e403f6b..b40509715 100644
--- a/src/tools/rust-analyzer/crates/ide/src/typing.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs
@@ -32,7 +32,7 @@ use crate::SourceChange;
pub(crate) use on_enter::on_enter;
// Don't forget to add new trigger characters to `server_capabilities` in `caps.rs`.
-pub(crate) const TRIGGER_CHARS: &str = ".=<>{";
+pub(crate) const TRIGGER_CHARS: &str = ".=<>{(";
struct ExtendedTextEdit {
edit: TextEdit,
@@ -86,45 +86,57 @@ fn on_char_typed_inner(
if !stdx::always!(TRIGGER_CHARS.contains(char_typed)) {
return None;
}
- return match char_typed {
+ let conv = |text_edit: Option<TextEdit>| {
+ Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
+ };
+ match char_typed {
'.' => conv(on_dot_typed(&file.tree(), offset)),
'=' => conv(on_eq_typed(&file.tree(), offset)),
'<' => on_left_angle_typed(&file.tree(), offset),
'>' => conv(on_right_angle_typed(&file.tree(), offset)),
- '{' => conv(on_opening_brace_typed(file, offset)),
- _ => return None,
- };
-
- fn conv(text_edit: Option<TextEdit>) -> Option<ExtendedTextEdit> {
- Some(ExtendedTextEdit { edit: text_edit?, is_snippet: false })
+ '{' => conv(on_opening_bracket_typed(file, offset, '{')),
+ '(' => conv(on_opening_bracket_typed(file, offset, '(')),
+ _ => None,
}
}
-/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
-/// block, or a part of a `use` item.
-fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
- if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
+/// Inserts a closing bracket when the user types an opening bracket, wrapping an existing expression in a
+/// block, or a part of a `use` item (for `{`).
+fn on_opening_bracket_typed(
+ file: &Parse<SourceFile>,
+ offset: TextSize,
+ opening_bracket: char,
+) -> Option<TextEdit> {
+ let (closing_bracket, expected_ast_bracket) = match opening_bracket {
+ '{' => ('}', SyntaxKind::L_CURLY),
+ '(' => (')', SyntaxKind::L_PAREN),
+ _ => return None,
+ };
+
+ if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some(opening_bracket)) {
return None;
}
let brace_token = file.tree().syntax().token_at_offset(offset).right_biased()?;
- if brace_token.kind() != SyntaxKind::L_CURLY {
+ if brace_token.kind() != expected_ast_bracket {
return None;
}
- // Remove the `{` to get a better parse tree, and reparse.
+ // Remove the opening bracket to get a better parse tree, and reparse.
let range = brace_token.text_range();
- if !stdx::always!(range.len() == TextSize::of('{')) {
+ if !stdx::always!(range.len() == TextSize::of(opening_bracket)) {
return None;
}
let file = file.reparse(&Indel::delete(range));
- if let Some(edit) = brace_expr(&file.tree(), offset) {
+ if let Some(edit) = bracket_expr(&file.tree(), offset, opening_bracket, closing_bracket) {
return Some(edit);
}
- if let Some(edit) = brace_use_path(&file.tree(), offset) {
- return Some(edit);
+ if closing_bracket == '}' {
+ if let Some(edit) = brace_use_path(&file.tree(), offset) {
+ return Some(edit);
+ }
}
return None;
@@ -143,7 +155,12 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
))
}
- fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
+ fn bracket_expr(
+ file: &SourceFile,
+ offset: TextSize,
+ opening_bracket: char,
+ closing_bracket: char,
+ ) -> Option<TextEdit> {
let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
if expr.syntax().text_range().start() != offset {
return None;
@@ -166,10 +183,10 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
return None;
}
- // Insert `}` right after the expression.
+ // Insert the closing bracket right after the expression.
Some(TextEdit::insert(
- expr.syntax().text_range().end() + TextSize::of("{"),
- "}".to_string(),
+ expr.syntax().text_range().end() + TextSize::of(opening_bracket),
+ closing_bracket.to_string(),
))
}
}
@@ -938,6 +955,193 @@ use some::pa$0th::to::Item;
}
#[test]
+ fn adds_closing_parenthesis_for_expr() {
+ type_char(
+ '(',
+ r#"
+fn f() { match () { _ => $0() } }
+ "#,
+ r#"
+fn f() { match () { _ => (()) } }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { $0() }
+ "#,
+ r#"
+fn f() { (()) }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { let x = $0(); }
+ "#,
+ r#"
+fn f() { let x = (()); }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() { let x = $0a.b(); }
+ "#,
+ r#"
+fn f() { let x = (a.b()); }
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+const S: () = $0();
+fn f() {}
+ "#,
+ r#"
+const S: () = (());
+fn f() {}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+const S: () = $0a.b();
+fn f() {}
+ "#,
+ r#"
+const S: () = (a.b());
+fn f() {}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+fn f() {
+ match x {
+ 0 => $0(),
+ 1 => (),
+ }
+}
+ "#,
+ r#"
+fn f() {
+ match x {
+ 0 => (()),
+ 1 => (),
+ }
+}
+ "#,
+ );
+ type_char(
+ '(',
+ r#"
+ fn f() {
+ let z = Some($03);
+ }
+ "#,
+ r#"
+ fn f() {
+ let z = Some((3));
+ }
+ "#,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_string_literal() {
+ // Regression test for #9351
+ type_char_noop(
+ '(',
+ r##"
+fn check_with(ra_fixture: &str, expect: Expect) {
+ let base = r#"
+enum E { T(), R$0, C }
+use self::E::X;
+const Z: E = E::C;
+mod m {}
+asdasdasdasdasdasda
+sdasdasdasdasdasda
+sdasdasdasdasd
+"#;
+ let actual = completion_list(&format!("{}\n{}", base, ra_fixture));
+ expect.assert_eq(&actual)
+}
+ "##,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_item_position_with_macro() {
+ type_char_noop('(', r#"$0println!();"#);
+ type_char_noop(
+ '(',
+ r#"
+fn main() $0println!("hello");
+}"#,
+ );
+ }
+
+ #[test]
+ fn parenthesis_noop_in_use_tree() {
+ type_char_noop(
+ '(',
+ r#"
+use some::$0Path;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::{Path, $0Other};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::{$0Path, Other};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::path::$0to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::$0path::to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use $0some::path::to::Item;
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use some::path::$0to::{Item};
+ "#,
+ );
+ type_char_noop(
+ '(',
+ r#"
+use $0Thing as _;
+ "#,
+ );
+
+ type_char_noop(
+ '(',
+ r#"
+use some::pa$0th::to::Item;
+ "#,
+ );
+ }
+
+ #[test]
fn adds_closing_angle_bracket_for_generic_args() {
type_char(
'<',
diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml
index 4d56c7719..89b302c79 100644
--- a/src/tools/rust-analyzer/crates/intern/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml
@@ -16,6 +16,5 @@ doctest = false
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
dashmap = { version = "=5.4.0", features = ["raw-api"] }
hashbrown.workspace = true
-once_cell = "1.17.0"
rustc-hash = "1.1.0"
triomphe.workspace = true
diff --git a/src/tools/rust-analyzer/crates/intern/src/lib.rs b/src/tools/rust-analyzer/crates/intern/src/lib.rs
index dabbf3a38..2934d2667 100644
--- a/src/tools/rust-analyzer/crates/intern/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/lib.rs
@@ -6,11 +6,11 @@ use std::{
fmt::{self, Debug, Display},
hash::{BuildHasherDefault, Hash, Hasher},
ops::Deref,
+ sync::OnceLock,
};
use dashmap::{DashMap, SharedValue};
use hashbrown::{hash_map::RawEntryMut, HashMap};
-use once_cell::sync::OnceCell;
use rustc_hash::FxHasher;
use triomphe::Arc;
@@ -177,12 +177,12 @@ impl<T: Display + Internable + ?Sized> Display for Interned<T> {
}
pub struct InternStorage<T: ?Sized> {
- map: OnceCell<InternMap<T>>,
+ map: OnceLock<InternMap<T>>,
}
impl<T: ?Sized> InternStorage<T> {
pub const fn new() -> Self {
- Self { map: OnceCell::new() }
+ Self { map: OnceLock::new() }
}
}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
index 62b2accf5..7b9bb61e6 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/syntax_bridge.rs
@@ -961,6 +961,7 @@ impl TtTreeSink<'_> {
if has_pseudo_dot {
assert!(right.is_empty(), "{left}.{right}");
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
self.inner.start_node(SyntaxKind::NAME_REF);
self.inner.token(SyntaxKind::INT_NUMBER, right);
self.inner.finish_node();
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
index 333318f53..6a2a9adce 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs
@@ -184,7 +184,9 @@ pub(crate) mod entry {
};
p.bump_any();
while !p.at(EOF) && !p.at(closing_paren_kind) {
- expressions::expr(p);
+ if expressions::expr(p).is_none() {
+ break;
+ }
if !p.at(EOF) && !p.at(closing_paren_kind) {
p.expect(T![,]);
}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index d8553d3f9..4197f248e 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -1,3 +1,5 @@
+use crate::grammar::types::type_;
+
use super::*;
// test expr_literals
@@ -73,6 +75,9 @@ pub(super) fn atom_expr(
if let Some(m) = literal(p) {
return Some((m, BlockLike::NotBlock));
}
+ if p.at_contextual_kw(T![builtin]) && p.nth_at(1, T![#]) {
+ return Some((builtin_expr(p)?, BlockLike::NotBlock));
+ }
if paths::is_path_start(p) {
return Some(path_expr(p, r));
}
@@ -93,7 +98,6 @@ pub(super) fn atom_expr(
m.complete(p, UNDERSCORE_EXPR)
}
T![loop] => loop_expr(p, None),
- T![box] => box_expr(p, None),
T![while] => while_expr(p, None),
T![try] => try_block_expr(p, None),
T![match] => match_expr(p),
@@ -212,6 +216,72 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
m.complete(p, if saw_expr && !saw_comma { PAREN_EXPR } else { TUPLE_EXPR })
}
+// test builtin_expr
+// fn foo() {
+// builtin#asm(0);
+// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
+// builtin#offset_of(Foo, bar.baz.0);
+// }
+fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
+ let m = p.start();
+ p.bump_remap(T![builtin]);
+ p.bump(T![#]);
+ if p.at_contextual_kw(T![offset_of]) {
+ p.bump_remap(T![offset_of]);
+ p.expect(T!['(']);
+ type_(p);
+ p.expect(T![,]);
+ while !p.at(EOF) && !p.at(T![')']) {
+ if p.at(IDENT) || p.at(INT_NUMBER) {
+ name_ref_or_index(p);
+ // } else if p.at(FLOAT_NUMBER) {
+ // FIXME: needs float hack
+ } else {
+ p.err_and_bump("expected field name or number");
+ }
+ if !p.at(T![')']) {
+ p.expect(T![.]);
+ }
+ }
+ p.expect(T![')']);
+ Some(m.complete(p, OFFSET_OF_EXPR))
+ } else if p.at_contextual_kw(T![format_args]) {
+ p.bump_remap(T![format_args]);
+ p.expect(T!['(']);
+ expr(p);
+ if p.eat(T![,]) {
+ while !p.at(EOF) && !p.at(T![')']) {
+ let m = p.start();
+ if p.at(IDENT) && p.nth_at(1, T![=]) {
+ name(p);
+ p.bump(T![=]);
+ }
+ if expr(p).is_none() {
+ m.abandon(p);
+ break;
+ }
+ m.complete(p, FORMAT_ARGS_ARG);
+
+ if !p.at(T![')']) {
+ p.expect(T![,]);
+ }
+ }
+ }
+ p.expect(T![')']);
+ Some(m.complete(p, FORMAT_ARGS_EXPR))
+ } else if p.at_contextual_kw(T![asm]) {
+ p.bump_remap(T![asm]);
+ p.expect(T!['(']);
+ // FIXME: We just put expression here so highlighting kind of keeps working
+ expr(p);
+ p.expect(T![')']);
+ Some(m.complete(p, ASM_EXPR))
+ } else {
+ m.abandon(p);
+ None
+ }
+}
+
// test array_expr
// fn foo() {
// [];
@@ -662,19 +732,3 @@ fn try_block_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker {
}
m.complete(p, BLOCK_EXPR)
}
-
-// test box_expr
-// fn foo() {
-// let x = box 1i32;
-// let y = (box 1i32, box 2i32);
-// let z = Foo(box 1i32, box 2i32);
-// }
-fn box_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker {
- assert!(p.at(T![box]));
- let m = m.unwrap_or_else(|| p.start());
- p.bump(T![box]);
- if p.at_ts(EXPR_FIRST) {
- expr(p);
- }
- m.complete(p, BOX_EXPR)
-}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
index 8ed1c84c4..29d9b05d3 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs
@@ -88,7 +88,7 @@ fn const_param(p: &mut Parser<'_>, m: Marker) {
// test const_param_default_path
// struct A<const N: i32 = i32::MAX>;
- generic_args::const_arg_expr(p);
+ generic_args::const_arg(p);
}
m.complete(p, CONST_PARAM);
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index e4dce21f3..36c52953a 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -221,6 +221,7 @@ impl<'a> Converter<'a> {
rustc_lexer::TokenKind::Caret => T![^],
rustc_lexer::TokenKind::Percent => T![%],
rustc_lexer::TokenKind::Unknown => ERROR,
+ rustc_lexer::TokenKind::UnknownPrefix if token_text == "builtin" => IDENT,
rustc_lexer::TokenKind::UnknownPrefix => {
err = "unknown literal prefix";
IDENT
diff --git a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
index 53cdad649..2c47e3d08 100644
--- a/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/shortcuts.rs
@@ -46,12 +46,16 @@ impl LexedStr<'_> {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER && !self.text(i).ends_with('.') {
- res.was_joint();
+ if kind == SyntaxKind::FLOAT_NUMBER {
+ if !self.text(i).ends_with('.') {
+ res.was_joint();
+ } else {
+ was_joint = false;
+ }
+ } else {
+ was_joint = true;
}
}
-
- was_joint = true;
}
}
res
@@ -204,6 +208,7 @@ impl Builder<'_, '_> {
assert!(right.is_empty(), "{left}.{right}");
self.state = State::Normal;
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
(self.sink)(StrStep::Enter { kind: SyntaxKind::NAME_REF });
(self.sink)(StrStep::Token { kind: SyntaxKind::INT_NUMBER, text: right });
(self.sink)(StrStep::Exit);
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index 48f407623..db5278f89 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -105,12 +105,16 @@ pub enum SyntaxKind {
WHILE_KW,
YIELD_KW,
AUTO_KW,
+ BUILTIN_KW,
DEFAULT_KW,
EXISTENTIAL_KW,
UNION_KW,
RAW_KW,
MACRO_RULES_KW,
YEET_KW,
+ OFFSET_OF_KW,
+ ASM_KW,
+ FORMAT_ARGS_KW,
INT_NUMBER,
FLOAT_NUMBER,
CHAR,
@@ -203,7 +207,10 @@ pub enum SyntaxKind {
RECORD_EXPR,
RECORD_EXPR_FIELD_LIST,
RECORD_EXPR_FIELD,
- BOX_EXPR,
+ OFFSET_OF_EXPR,
+ ASM_EXPR,
+ FORMAT_ARGS_EXPR,
+ FORMAT_ARGS_ARG,
CALL_EXPR,
INDEX_EXPR,
METHOD_CALL_EXPR,
@@ -315,12 +322,16 @@ impl SyntaxKind {
| WHILE_KW
| YIELD_KW
| AUTO_KW
+ | BUILTIN_KW
| DEFAULT_KW
| EXISTENTIAL_KW
| UNION_KW
| RAW_KW
| MACRO_RULES_KW
| YEET_KW
+ | OFFSET_OF_KW
+ | ASM_KW
+ | FORMAT_ARGS_KW
)
}
pub fn is_punct(self) -> bool {
@@ -435,12 +446,16 @@ impl SyntaxKind {
pub fn from_contextual_keyword(ident: &str) -> Option<SyntaxKind> {
let kw = match ident {
"auto" => AUTO_KW,
+ "builtin" => BUILTIN_KW,
"default" => DEFAULT_KW,
"existential" => EXISTENTIAL_KW,
"union" => UNION_KW,
"raw" => RAW_KW,
"macro_rules" => MACRO_RULES_KW,
"yeet" => YEET_KW,
+ "offset_of" => OFFSET_OF_KW,
+ "asm" => ASM_KW,
+ "format_args" => FORMAT_ARGS_KW,
_ => return None,
};
Some(kw)
@@ -481,5 +496,5 @@ impl SyntaxKind {
}
}
#[macro_export]
-macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
+macro_rules ! T { [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [existential] => { $ crate :: SyntaxKind :: EXISTENTIAL_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; }
pub use T;
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
index 809ad1b8d..49f163b16 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/0022_recover_from_missing_const_default.rast
@@ -20,7 +20,8 @@ SOURCE_FILE
IDENT "i32"
WHITESPACE " "
EQ "="
- WHITESPACE " "
+ WHITESPACE " "
+ CONST_ARG
COMMA ","
WHITESPACE " "
CONST_PARAM
@@ -37,8 +38,9 @@ SOURCE_FILE
IDENT "i32"
WHITESPACE " "
EQ "="
+ CONST_ARG
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
-error 23: expected a generic const argument
+error 24: expected a generic const argument
error 40: expected a generic const argument
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast
deleted file mode 100644
index b21f37cd8..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rast
+++ /dev/null
@@ -1,90 +0,0 @@
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "x"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "y"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- TUPLE_EXPR
- L_PAREN "("
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- COMMA ","
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "2i32"
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "z"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Foo"
- ARG_LIST
- L_PAREN "("
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1i32"
- COMMA ","
- WHITESPACE " "
- BOX_EXPR
- BOX_KW "box"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "2i32"
- R_PAREN ")"
- SEMICOLON ";"
- WHITESPACE "\n"
- R_CURLY "}"
- WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs
deleted file mode 100644
index fc9923b71..000000000
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0132_box_expr.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-fn foo() {
- let x = box 1i32;
- let y = (box 1i32, box 2i32);
- let z = Foo(box 1i32, box 2i32);
-}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
index 11002bf98..3f5fb47d2 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0188_const_param_default_path.rast
@@ -21,16 +21,17 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- PATH_EXPR
- PATH
+ CONST_ARG
+ PATH_EXPR
PATH
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i32"
+ COLON2 "::"
PATH_SEGMENT
NAME_REF
- IDENT "i32"
- COLON2 "::"
- PATH_SEGMENT
- NAME_REF
- IDENT "MAX"
+ IDENT "MAX"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
index 0607ff54f..d65011374 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0199_const_param_default_expression.rast
@@ -21,14 +21,15 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
- WHITESPACE " "
- R_CURLY "}"
+ CONST_ARG
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ R_CURLY "}"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
index 8e5231365..6de10353b 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0200_const_param_default_literal.rast
@@ -21,10 +21,11 @@ SOURCE_FILE
WHITESPACE " "
EQ "="
WHITESPACE " "
- PREFIX_EXPR
- MINUS "-"
- LITERAL
- INT_NUMBER "1"
+ CONST_ARG
+ PREFIX_EXPR
+ MINUS "-"
+ LITERAL
+ INT_NUMBER "1"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
new file mode 100644
index 000000000..361900b6d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rast
@@ -0,0 +1,105 @@
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ ASM_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ ASM_KW "asm"
+ L_PAREN "("
+ LITERAL
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ FORMAT_ARGS_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ FORMAT_ARGS_KW "format_args"
+ L_PAREN "("
+ LITERAL
+ STRING "\"\""
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ LITERAL
+ INT_NUMBER "0"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ LITERAL
+ INT_NUMBER "1"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ NAME
+ IDENT "a"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BIN_EXPR
+ LITERAL
+ INT_NUMBER "2"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "3"
+ COMMA ","
+ WHITESPACE " "
+ FORMAT_ARGS_ARG
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "a"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "b"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ EXPR_STMT
+ OFFSET_OF_EXPR
+ BUILTIN_KW "builtin"
+ POUND "#"
+ OFFSET_OF_KW "offset_of"
+ L_PAREN "("
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Foo"
+ COMMA ","
+ WHITESPACE " "
+ NAME_REF
+ IDENT "bar"
+ DOT "."
+ NAME_REF
+ IDENT "baz"
+ DOT "."
+ NAME_REF
+ INT_NUMBER "0"
+ R_PAREN ")"
+ SEMICOLON ";"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
new file mode 100644
index 000000000..14431b021
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/0207_builtin_expr.rs
@@ -0,0 +1,5 @@
+fn foo() {
+ builtin#asm(0);
+ builtin#format_args("", 0, 1, a = 2 + 3, a + b);
+ builtin#offset_of(Foo, bar.baz.0);
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
index 19a5caa4c..782715786 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-test/build.rs
@@ -71,6 +71,10 @@ fn main() {
.arg("--target-dir")
.arg(&target_dir);
+ if let Ok(target) = std::env::var("TARGET") {
+ cmd.args(["--target", &target]);
+ }
+
println!("Running {cmd:?}");
let output = cmd.output().unwrap();
diff --git a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs
index 71303d5a6..814a02574 100644
--- a/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs
+++ b/src/tools/rust-analyzer/crates/profile/src/stop_watch.rs
@@ -10,13 +10,13 @@ pub struct StopWatch {
time: Instant,
#[cfg(target_os = "linux")]
counter: Option<perf_event::Counter>,
- memory: Option<MemoryUsage>,
+ memory: MemoryUsage,
}
pub struct StopWatchSpan {
pub time: Duration,
pub instructions: Option<u64>,
- pub memory: Option<MemoryUsage>,
+ pub memory: MemoryUsage,
}
impl StopWatch {
@@ -45,20 +45,16 @@ impl StopWatch {
None
}
};
+ let memory = MemoryUsage::now();
let time = Instant::now();
StopWatch {
time,
#[cfg(target_os = "linux")]
counter,
- memory: None,
+ memory,
}
}
- pub fn memory(mut self, yes: bool) -> StopWatch {
- if yes {
- self.memory = Some(MemoryUsage::now());
- }
- self
- }
+
pub fn elapsed(&mut self) -> StopWatchSpan {
let time = self.time.elapsed();
@@ -69,7 +65,7 @@ impl StopWatch {
#[cfg(not(target_os = "linux"))]
let instructions = None;
- let memory = self.memory.map(|it| MemoryUsage::now() - it);
+ let memory = MemoryUsage::now() - self.memory;
StopWatchSpan { time, instructions, memory }
}
}
@@ -93,9 +89,7 @@ impl fmt::Display for StopWatchSpan {
}
write!(f, ", {instructions}{prefix}instr")?;
}
- if let Some(memory) = self.memory {
- write!(f, ", {memory}")?;
- }
+ write!(f, ", {}", self.memory)?;
Ok(())
}
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
index 8392718b2..c5d55f7d2 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs
@@ -2,14 +2,29 @@
use std::process::Command;
+use anyhow::Context;
use rustc_hash::FxHashMap;
-use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath};
+use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath, Sysroot};
+
+/// Determines how `rustc --print cfg` is discovered and invoked.
+///
+/// There options are supported:
+/// - [`RustcCfgConfig::Cargo`], which relies on `cargo rustc --print cfg`
+/// and `RUSTC_BOOTSTRAP`.
+/// - [`RustcCfgConfig::Explicit`], which uses an explicit path to the `rustc`
+/// binary in the sysroot.
+/// - [`RustcCfgConfig::Discover`], which uses [`toolchain::rustc`].
+pub(crate) enum RustcCfgConfig<'a> {
+ Cargo(&'a ManifestPath),
+ Explicit(&'a Sysroot),
+ Discover,
+}
pub(crate) fn get(
- cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ config: RustcCfgConfig<'_>,
) -> Vec<CfgFlag> {
let _p = profile::span("rustc_cfg::get");
let mut res = Vec::with_capacity(6 * 2 + 1);
@@ -25,49 +40,67 @@ pub(crate) fn get(
// Add miri cfg, which is useful for mir eval in stdlib
res.push(CfgFlag::Atom("miri".into()));
- match get_rust_cfgs(cargo_toml, target, extra_env) {
+ let rustc_cfgs = get_rust_cfgs(target, extra_env, config);
+
+ let rustc_cfgs = match rustc_cfgs {
+ Ok(cfgs) => cfgs,
+ Err(e) => {
+ tracing::error!(?e, "failed to get rustc cfgs");
+ return res;
+ }
+ };
+
+ let rustc_cfgs =
+ rustc_cfgs.lines().map(|it| it.parse::<CfgFlag>()).collect::<Result<Vec<_>, _>>();
+
+ match rustc_cfgs {
Ok(rustc_cfgs) => {
- tracing::debug!(
- "rustc cfgs found: {:?}",
- rustc_cfgs
- .lines()
- .map(|it| it.parse::<CfgFlag>().map(|it| it.to_string()))
- .collect::<Vec<_>>()
- );
- res.extend(rustc_cfgs.lines().filter_map(|it| it.parse().ok()));
+ tracing::debug!(?rustc_cfgs, "rustc cfgs found");
+ res.extend(rustc_cfgs);
+ }
+ Err(e) => {
+ tracing::error!(?e, "failed to get rustc cfgs")
}
- Err(e) => tracing::error!("failed to get rustc cfgs: {e:?}"),
}
res
}
fn get_rust_cfgs(
- cargo_toml: Option<&ManifestPath>,
target: Option<&str>,
extra_env: &FxHashMap<String, String>,
+ config: RustcCfgConfig<'_>,
) -> anyhow::Result<String> {
- if let Some(cargo_toml) = cargo_toml {
- let mut cargo_config = Command::new(toolchain::cargo());
- cargo_config.envs(extra_env);
- cargo_config
- .current_dir(cargo_toml.parent())
- .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
- .env("RUSTC_BOOTSTRAP", "1");
- if let Some(target) = target {
- cargo_config.args(["--target", target]);
+ let mut cmd = match config {
+ RustcCfgConfig::Cargo(cargo_toml) => {
+ let mut cmd = Command::new(toolchain::cargo());
+ cmd.envs(extra_env);
+ cmd.current_dir(cargo_toml.parent())
+ .args(["rustc", "-Z", "unstable-options", "--print", "cfg"])
+ .env("RUSTC_BOOTSTRAP", "1");
+ if let Some(target) = target {
+ cmd.args(["--target", target]);
+ }
+
+ return utf8_stdout(cmd).context("Unable to run `cargo rustc`");
}
- match utf8_stdout(cargo_config) {
- Ok(it) => return Ok(it),
- Err(e) => tracing::debug!("{e:?}: falling back to querying rustc for cfgs"),
+ RustcCfgConfig::Explicit(sysroot) => {
+ let rustc: std::path::PathBuf = sysroot.discover_rustc()?.into();
+ tracing::debug!(?rustc, "using explicit rustc from sysroot");
+ Command::new(rustc)
}
- }
- // using unstable cargo features failed, fall back to using plain rustc
- let mut cmd = Command::new(toolchain::rustc());
+ RustcCfgConfig::Discover => {
+ let rustc = toolchain::rustc();
+ tracing::debug!(?rustc, "using rustc from env");
+ Command::new(rustc)
+ }
+ };
+
cmd.envs(extra_env);
cmd.args(["--print", "cfg", "-O"]);
if let Some(target) = target {
cmd.args(["--target", target]);
}
- utf8_stdout(cmd)
+
+ utf8_stdout(cmd).context("Unable to run `rustc`")
}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index da862c9e8..fe046dd14 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -115,10 +115,19 @@ impl Sysroot {
Ok(Sysroot::load(sysroot_dir, src))
}
- pub fn discover_rustc(&self) -> Option<ManifestPath> {
+ pub fn discover_rustc_src(&self) -> Option<ManifestPath> {
get_rustc_src(&self.root)
}
+ pub fn discover_rustc(&self) -> Result<AbsPathBuf, std::io::Error> {
+ let rustc = self.root.join("bin/rustc");
+ tracing::debug!(?rustc, "checking for rustc binary at location");
+ match fs::metadata(&rustc) {
+ Ok(_) => Ok(rustc),
+ Err(e) => Err(e),
+ }
+ }
+
pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
format_err!("can't load standard library from sysroot path {sysroot_dir}")
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index f51ea7eeb..e0209ca15 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -2,7 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.
-use std::{collections::VecDeque, fmt, fs, process::Command, sync};
+use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync};
use anyhow::{format_err, Context};
use base_db::{
@@ -21,7 +21,7 @@ use crate::{
cargo_workspace::{DepKind, PackageData, RustLibSource},
cfg_flag::CfgFlag,
project_json::Crate,
- rustc_cfg,
+ rustc_cfg::{self, RustcCfgConfig},
sysroot::SysrootCrate,
target_data_layout, utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath,
Package, ProjectJson, ProjectManifest, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts,
@@ -167,7 +167,8 @@ impl ProjectWorkspace {
cmd.envs(&config.extra_env);
cmd.arg("--version").current_dir(current_dir);
cmd
- })?;
+ })
+ .with_context(|| format!("Failed to query rust toolchain version at {current_dir}, is your toolchain setup correctly?"))?;
anyhow::Ok(
cargo_version
.get(prefix.len()..)
@@ -239,9 +240,9 @@ impl ProjectWorkspace {
Some(RustLibSource::Path(path)) => ManifestPath::try_from(path.clone())
.map_err(|p| Some(format!("rustc source path is not absolute: {p}"))),
Some(RustLibSource::Discover) => {
- sysroot.as_ref().ok().and_then(Sysroot::discover_rustc).ok_or_else(|| {
- Some(format!("Failed to discover rustc source for sysroot."))
- })
+ sysroot.as_ref().ok().and_then(Sysroot::discover_rustc_src).ok_or_else(
+ || Some(format!("Failed to discover rustc source for sysroot.")),
+ )
}
None => Err(None),
};
@@ -278,8 +279,11 @@ impl ProjectWorkspace {
}
});
- let rustc_cfg =
- rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
+ let rustc_cfg = rustc_cfg::get(
+ config.target.as_deref(),
+ &config.extra_env,
+ RustcCfgConfig::Cargo(cargo_toml),
+ );
let cfg_overrides = config.cfg_overrides.clone();
let data_layout = target_data_layout::get(
@@ -330,11 +334,18 @@ impl ProjectWorkspace {
}
(None, None) => Err(None),
};
- if let Ok(sysroot) = &sysroot {
- tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
- }
+ let config = match &sysroot {
+ Ok(sysroot) => {
+ tracing::debug!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
+ RustcCfgConfig::Explicit(sysroot)
+ }
+ Err(_) => {
+ tracing::debug!("discovering sysroot");
+ RustcCfgConfig::Discover
+ }
+ };
- let rustc_cfg = rustc_cfg::get(None, target, extra_env);
+ let rustc_cfg = rustc_cfg::get(target, extra_env, config);
ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg, toolchain }
}
@@ -356,10 +367,18 @@ impl ProjectWorkspace {
}
None => Err(None),
};
- if let Ok(sysroot) = &sysroot {
- tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
- }
- let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
+ let rustc_config = match &sysroot {
+ Ok(sysroot) => {
+ tracing::info!(src_root = %sysroot.src_root(), root = %sysroot.root(), "Using sysroot");
+ RustcCfgConfig::Explicit(sysroot)
+ }
+ Err(_) => {
+ tracing::info!("discovering sysroot");
+ RustcCfgConfig::Discover
+ }
+ };
+
+ let rustc_cfg = rustc_cfg::get(None, &FxHashMap::default(), rustc_config);
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
}
@@ -729,6 +748,7 @@ fn project_json_to_crate_graph(
)
});
+ let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned());
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
let crates: FxHashMap<CrateId, CrateId> = project
.crates()
@@ -753,9 +773,14 @@ fn project_json_to_crate_graph(
let env = env.clone().into_iter().collect();
let target_cfgs = match target.as_deref() {
- Some(target) => cfg_cache
- .entry(target)
- .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
+ Some(target) => cfg_cache.entry(target).or_insert_with(|| {
+ let rustc_cfg = match sysroot {
+ Some(sysroot) => RustcCfgConfig::Explicit(sysroot),
+ None => RustcCfgConfig::Discover,
+ };
+
+ rustc_cfg::get(Some(target), extra_env, rustc_cfg)
+ }),
None => &rustc_cfg,
};
@@ -764,7 +789,12 @@ fn project_json_to_crate_graph(
*edition,
display_name.clone(),
version.clone(),
- target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
+ target_cfgs
+ .iter()
+ .chain(cfg.iter())
+ .chain(iter::once(&r_a_cfg_flag))
+ .cloned()
+ .collect(),
None,
env,
*is_proc_macro,
@@ -819,7 +849,7 @@ fn cargo_to_crate_graph(
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
- // Don't compute cfg and use this if present
+ // Don't compute cfg and use this if present, only used for the sysroot experiment hack
forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
@@ -841,12 +871,7 @@ fn cargo_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};
- let cfg_options = {
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
- cfg_options.insert_atom("debug_assertions".into());
- cfg_options
- };
+ let cfg_options = create_cfg_options(rustc_cfg);
// Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
@@ -865,6 +890,9 @@ fn cargo_to_crate_graph(
if cargo[pkg].is_local {
cfg_options.insert_atom("test".into());
}
+ if cargo[pkg].is_member {
+ cfg_options.insert_atom("rust_analyzer".into());
+ }
if !override_cfg.global.is_empty() {
cfg_options.apply_diff(override_cfg.global.clone());
@@ -1028,8 +1056,8 @@ fn detached_files_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg);
+ let mut cfg_options = create_cfg_options(rustc_cfg);
+ cfg_options.insert_atom("rust_analyzer".into());
for detached_file in detached_files {
let file_id = match load(detached_file) {
@@ -1227,6 +1255,10 @@ fn add_target_crate_root(
let mut env = Env::default();
inject_cargo_env(pkg, &mut env);
+ if let Ok(cname) = String::from_str(cargo_name) {
+ // CARGO_CRATE_NAME is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark.
+ env.set("CARGO_CRATE_NAME", cname.replace("-", "_"));
+ }
if let Some(envs) = build_data.map(|it| &it.envs) {
for (k, v) in envs {
@@ -1290,8 +1322,7 @@ fn sysroot_to_crate_graph(
channel: Option<ReleaseChannel>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
- let mut cfg_options = CfgOptions::default();
- cfg_options.extend(rustc_cfg.clone());
+ let cfg_options = create_cfg_options(rustc_cfg.clone());
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
Some(cargo) => handle_hack_cargo_workspace(
load,
@@ -1470,3 +1501,10 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) {
env.set("CARGO_PKG_LICENSE_FILE", String::new());
}
+
+fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions {
+ let mut cfg_options = CfgOptions::default();
+ cfg_options.extend(rustc_cfg);
+ cfg_options.insert_atom("debug_assertions".into());
+ cfg_options
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index e595cd827..727d39a30 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -162,7 +165,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -232,7 +236,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index e595cd827..727d39a30 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -162,7 +165,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
"test",
],
),
@@ -232,7 +236,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index f10c55d04..89728babd 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -80,6 +81,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -149,6 +151,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -159,7 +162,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "an_example",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
@@ -218,6 +221,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
+ "rust_analyzer",
],
),
potential_cfg_options: None,
@@ -228,7 +232,7 @@
"CARGO_MANIFEST_DIR": "$ROOT$hello-world",
"CARGO_PKG_VERSION": "0.1.0",
"CARGO_PKG_AUTHORS": "",
- "CARGO_CRATE_NAME": "hello_world",
+ "CARGO_CRATE_NAME": "it",
"CARGO_PKG_LICENSE_FILE": "",
"CARGO_PKG_HOMEPAGE": "",
"CARGO_PKG_DESCRIPTION": "",
diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index fb3f5933b..b7bf6cb27 100644
--- a/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/src/tools/rust-analyzer/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -14,7 +14,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -53,7 +55,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -84,7 +88,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -115,7 +121,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -146,7 +154,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -192,7 +202,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -223,7 +235,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -311,7 +325,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -342,7 +358,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -373,7 +391,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "debug_assertions",
+ ],
),
potential_cfg_options: None,
env: Env {
@@ -404,7 +424,9 @@
},
),
cfg_options: CfgOptions(
- [],
+ [
+ "rust_analyzer",
+ ],
),
potential_cfg_options: None,
env: Env {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 5bfac7ee4..7410f0a3a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -24,7 +24,7 @@ crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
itertools = "0.10.5"
scip = "0.1.1"
-lsp-types = { version = "=0.94", features = ["proposed"] }
+lsp-types = { version = "=0.94.0", features = ["proposed"] }
parking_lot = "0.12.1"
xflags = "0.3.0"
oorandom = "11.1.3"
@@ -50,7 +50,6 @@ always-assert = "0.1.2"
# These 3 deps are not used by r-a directly, but we list them here to lock in their versions
# in our transitive deps to prevent them from pulling in windows-sys 0.45.0
mio = "=0.8.5"
-filetime = "=0.2.19"
parking_lot_core = "=0.9.6"
cfg.workspace = true
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
index ab06b9681..8c9261ab0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/caps.rs
@@ -16,10 +16,12 @@ use lsp_types::{
};
use serde_json::json;
-use crate::config::{Config, RustfmtConfig};
-use crate::line_index::PositionEncoding;
-use crate::lsp_ext::negotiated_encoding;
-use crate::semantic_tokens;
+use crate::{
+ config::{Config, RustfmtConfig},
+ line_index::PositionEncoding,
+ lsp::semantic_tokens,
+ lsp_ext::negotiated_encoding,
+};
pub fn server_capabilities(config: &Config) -> ServerCapabilities {
ServerCapabilities {
@@ -218,7 +220,7 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi
}
fn more_trigger_character(config: &Config) -> Vec<String> {
- let mut res = vec![".".to_string(), ">".to_string(), "{".to_string()];
+ let mut res = vec![".".to_string(), ">".to_string(), "{".to_string(), "(".to_string()];
if config.snippet_cap() {
res.push("<".to_string());
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index f446a7c05..dcb3ca658 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -15,7 +15,10 @@ use hir_def::{
hir::{ExprId, PatId},
};
use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
-use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase};
+use ide::{
+ Analysis, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve, InlayHintsConfig, LineCol,
+ RootDatabase,
+};
use ide_db::{
base_db::{
salsa::{self, debug::DebugQueryTable, ParallelDatabase},
@@ -235,9 +238,7 @@ impl flags::AnalysisStats {
if let Some(instructions) = total_span.instructions {
report_metric("total instructions", instructions, "#instr");
}
- if let Some(memory) = total_span.memory {
- report_metric("total memory", memory.allocated.megabytes() as u64, "MB");
- }
+ report_metric("total memory", total_span.memory.allocated.megabytes() as u64, "MB");
if env::var("RA_COUNT").is_ok() {
eprintln!("{}", profile::countme::get_all());
@@ -257,7 +258,7 @@ impl flags::AnalysisStats {
eprintln!("source files: {total_file_size}, macro files: {total_macro_file_size}");
}
- if self.memory_usage && verbosity.is_verbose() {
+ if verbosity.is_verbose() {
print_memory_usage(host, vfs);
}
@@ -319,9 +320,13 @@ impl flags::AnalysisStats {
fn run_mir_lowering(&self, db: &RootDatabase, bodies: &[DefWithBody], verbosity: Verbosity) {
let mut sw = self.stop_watch();
- let all = bodies.len() as u64;
+ let mut all = 0;
let mut fail = 0;
for &body in bodies {
+ if matches!(body, DefWithBody::Variant(_)) {
+ continue;
+ }
+ all += 1;
let Err(e) = db.mir_body(body.into()) else {
continue;
};
@@ -784,6 +789,7 @@ impl flags::AnalysisStats {
closure_style: hir::ClosureStyle::ImplFn,
max_length: Some(25),
closing_brace_hints_min_lines: Some(20),
+ fields_to_resolve: InlayFieldsToResolve::empty(),
},
file_id,
None,
@@ -814,7 +820,7 @@ impl flags::AnalysisStats {
}
fn stop_watch(&self) -> StopWatch {
- StopWatch::start().memory(self.memory_usage)
+ StopWatch::start()
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 13b7f039b..419440b6d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -62,8 +62,6 @@ xflags::xflags! {
optional --randomize
/// Run type inference in parallel.
optional --parallel
- /// Collect memory usage statistics.
- optional --memory-usage
/// Print the total length of all source and macro files (whitespace is not counted).
optional --source-stats
@@ -191,7 +189,6 @@ pub struct AnalysisStats {
pub output: Option<OutputFormat>,
pub randomize: bool,
pub parallel: bool,
- pub memory_usage: bool,
pub source_stats: bool,
pub only: Option<String>,
pub with_deps: bool,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index 42d180114..d6a45ce06 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -21,7 +21,7 @@ use vfs::{AbsPathBuf, Vfs};
use crate::{
cli::flags,
line_index::{LineEndings, LineIndex, PositionEncoding},
- to_proto,
+ lsp::to_proto,
version::version,
};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 44337f955..8c056fff0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -51,7 +51,7 @@ impl flags::Scip {
version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(),
tool_info: Some(scip_types::ToolInfo {
name: "rust-analyzer".to_owned(),
- version: "0.1".to_owned(),
+ version: format!("{}", crate::version::version()),
arguments: vec![],
special_fields: Default::default(),
})
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index fa20c796e..ea3a21241 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -13,8 +13,9 @@ use cfg::{CfgAtom, CfgDiff};
use flycheck::FlycheckConfig;
use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
- HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig,
- JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope,
+ HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve,
+ InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
+ Snippet, SnippetScope,
};
use ide_db::{
imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -150,18 +151,22 @@ config_data! {
///
/// Set to `"all"` to pass `--all-features` to Cargo.
check_features | checkOnSave_features: Option<CargoFeaturesDef> = "null",
+ /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore.
+ ///
+ /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,...
+ check_ignore: FxHashSet<String> = "[]",
/// Specifies the working directory for running checks.
/// - "workspace": run checks for workspaces in the corresponding workspaces' root directories.
// FIXME: Ideally we would support this in some way
- /// This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.invocationStrategy#` is set to `once`.
+ /// This falls back to "root" if `#rust-analyzer.cargo.check.invocationStrategy#` is set to `once`.
/// - "root": run checks in the project's root directory.
- /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
/// is set.
check_invocationLocation | checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
- /// Specifies the invocation strategy to use when running the checkOnSave command.
+ /// Specifies the invocation strategy to use when running the check command.
/// If `per_workspace` is set, the command will be executed for each workspace.
/// If `once` is set, the command will be executed once.
- /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// This config only has an effect when `#rust-analyzer.cargo.check.overrideCommand#`
/// is set.
check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Whether to pass `--no-default-features` to Cargo. Defaults to
@@ -1098,6 +1103,7 @@ impl Config {
remap_prefix: self.data.diagnostics_remapPrefix.clone(),
warnings_as_info: self.data.diagnostics_warningsAsInfo.clone(),
warnings_as_hint: self.data.diagnostics_warningsAsHint.clone(),
+ check_ignore: self.data.check_ignore.clone(),
}
}
@@ -1330,6 +1336,18 @@ impl Config {
}
pub fn inlay_hints(&self) -> InlayHintsConfig {
+ let client_capability_fields = self
+ .caps
+ .text_document
+ .as_ref()
+ .and_then(|text| text.inlay_hint.as_ref())
+ .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref())
+ .map(|inlay_resolve| inlay_resolve.properties.iter())
+ .into_iter()
+ .flatten()
+ .cloned()
+ .collect::<FxHashSet<_>>();
+
InlayHintsConfig {
render_colons: self.data.inlayHints_renderColons,
type_hints: self.data.inlayHints_typeHints_enable,
@@ -1390,6 +1408,13 @@ impl Config {
} else {
None
},
+ fields_to_resolve: InlayFieldsToResolve {
+ resolve_text_edits: client_capability_fields.contains("textEdits"),
+ resolve_hint_tooltip: client_capability_fields.contains("tooltip"),
+ resolve_label_tooltip: client_capability_fields.contains("label.tooltip"),
+ resolve_label_location: client_capability_fields.contains("label.location"),
+ resolve_label_command: client_capability_fields.contains("label.command"),
+ },
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
index 33422fd05..71701ef16 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs
@@ -6,9 +6,10 @@ use std::mem;
use ide::FileId;
use ide_db::FxHashMap;
use nohash_hasher::{IntMap, IntSet};
+use rustc_hash::FxHashSet;
use triomphe::Arc;
-use crate::lsp_ext;
+use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext};
pub(crate) type CheckFixes = Arc<IntMap<usize, IntMap<FileId, Vec<Fix>>>>;
@@ -17,6 +18,7 @@ pub struct DiagnosticsMapConfig {
pub remap_prefix: FxHashMap<String, String>,
pub warnings_as_info: Vec<String>,
pub warnings_as_hint: Vec<String>,
+ pub check_ignore: FxHashSet<String>,
}
#[derive(Debug, Default, Clone)]
@@ -120,3 +122,41 @@ fn are_diagnostics_equal(left: &lsp_types::Diagnostic, right: &lsp_types::Diagno
&& left.range == right.range
&& left.message == right.message
}
+
+pub(crate) fn fetch_native_diagnostics(
+ snapshot: GlobalStateSnapshot,
+ subscriptions: Vec<FileId>,
+) -> Vec<(FileId, Vec<lsp_types::Diagnostic>)> {
+ let _p = profile::span("fetch_native_diagnostics");
+ let _ctx = stdx::panic_context::enter("fetch_native_diagnostics".to_owned());
+ subscriptions
+ .into_iter()
+ .filter_map(|file_id| {
+ let line_index = snapshot.file_line_index(file_id).ok()?;
+ let diagnostics = snapshot
+ .analysis
+ .diagnostics(
+ &snapshot.config.diagnostics(),
+ ide::AssistResolveStrategy::None,
+ file_id,
+ )
+ .ok()?
+ .into_iter()
+ .map(move |d| lsp_types::Diagnostic {
+ range: lsp::to_proto::range(&line_index, d.range),
+ severity: Some(lsp::to_proto::diagnostic_severity(d.severity)),
+ code: Some(lsp_types::NumberOrString::String(d.code.as_str().to_string())),
+ code_description: Some(lsp_types::CodeDescription {
+ href: lsp_types::Url::parse(&d.code.url()).unwrap(),
+ }),
+ source: Some("rust-analyzer".to_string()),
+ message: d.message,
+ related_information: None,
+ tags: d.unused.then(|| vec![lsp_types::DiagnosticTag::UNNECESSARY]),
+ data: None,
+ })
+ .collect::<Vec<_>>();
+ Some((file_id, diagnostics))
+ })
+ .collect()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
index e1d1130ff..731580557 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -8,8 +8,8 @@ use stdx::format_to;
use vfs::{AbsPath, AbsPathBuf};
use crate::{
- global_state::GlobalStateSnapshot, line_index::PositionEncoding, lsp_ext,
- to_proto::url_from_abs_path,
+ global_state::GlobalStateSnapshot, line_index::PositionEncoding,
+ lsp::to_proto::url_from_abs_path, lsp_ext,
};
use super::{DiagnosticsMapConfig, Fix};
@@ -292,6 +292,13 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
let mut source = String::from("rustc");
let mut code = rd.code.as_ref().map(|c| c.code.clone());
+
+ if let Some(code_val) = &code {
+ if config.check_ignore.contains(code_val) {
+ return Vec::new();
+ }
+ }
+
if let Some(code_val) = &code {
// See if this is an RFC #2103 scoped lint (e.g. from Clippy)
let scoped_code: Vec<&str> = code_val.split("::").collect();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
index 5e5cd9a02..7da431188 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -8,9 +8,9 @@ use stdx::thread::ThreadIntent;
use crate::{
global_state::{GlobalState, GlobalStateSnapshot},
+ lsp::LspError,
main_loop::Task,
version::version,
- LspError,
};
/// A visitor for routing a raw JSON request to an appropriate handler function.
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index ea8a69751..c09f57252 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -12,25 +12,27 @@ use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase};
use load_cargo::SourceRootConfig;
use lsp_types::{SemanticTokens, Url};
use nohash_hasher::IntMap;
-use parking_lot::{Mutex, RwLock};
+use parking_lot::{
+ MappedRwLockReadGuard, Mutex, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
+ RwLockWriteGuard,
+};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
-use vfs::AnchoredPathBuf;
+use vfs::{AnchoredPathBuf, Vfs};
use crate::{
config::{Config, ConfigError},
diagnostics::{CheckFixes, DiagnosticCollection},
- from_proto,
line_index::{LineEndings, LineIndex},
+ lsp::{from_proto, to_proto::url_from_abs_path},
lsp_ext,
main_loop::Task,
mem_docs::MemDocs,
op_queue::OpQueue,
reload,
task_pool::TaskPool,
- to_proto::url_from_abs_path,
};
// Enforces drop order
@@ -40,7 +42,7 @@ pub(crate) struct Handle<H, C> {
}
pub(crate) type ReqHandler = fn(&mut GlobalState, lsp_server::Response);
-pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
+type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
/// `GlobalState` is the primary mutable state of the language server
///
@@ -49,6 +51,7 @@ pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
/// incremental salsa database.
///
/// Note that this struct has more than one impl in various modules!
+#[doc(alias = "GlobalMess")]
pub(crate) struct GlobalState {
sender: Sender<lsp_server::Message>,
req_queue: ReqQueue,
@@ -66,6 +69,7 @@ pub(crate) struct GlobalState {
// status
pub(crate) shutdown_requested: bool,
+ pub(crate) send_hint_refresh_query: bool,
pub(crate) last_reported_status: Option<lsp_ext::ServerStatusParams>,
// proc macros
@@ -177,6 +181,7 @@ impl GlobalState {
mem_docs: MemDocs::default(),
semantic_tokens_cache: Arc::new(Default::default()),
shutdown_requested: false,
+ send_hint_refresh_query: false,
last_reported_status: None,
source_root_config: SourceRootConfig::default(),
config_errors: Default::default(),
@@ -216,12 +221,15 @@ impl GlobalState {
let mut file_changes = FxHashMap::default();
let (change, changed_files, workspace_structure_change) = {
let mut change = Change::new();
- let (vfs, line_endings_map) = &mut *self.vfs.write();
- let changed_files = vfs.take_changes();
+ let mut guard = self.vfs.write();
+ let changed_files = guard.0.take_changes();
if changed_files.is_empty() {
return false;
}
+ // downgrade to read lock to allow more readers while we are normalizing text
+ let guard = RwLockWriteGuard::downgrade_to_upgradable(guard);
+ let vfs: &Vfs = &guard.0;
// We need to fix up the changed events a bit. If we have a create or modify for a file
// id that is followed by a delete we actually skip observing the file text from the
// earlier event, to avoid problems later on.
@@ -272,6 +280,7 @@ impl GlobalState {
let mut workspace_structure_change = None;
// A file was added or deleted
let mut has_structure_changes = false;
+ let mut bytes = vec![];
for file in &changed_files {
let vfs_path = &vfs.file_path(file.file_id);
if let Some(path) = vfs_path.as_path() {
@@ -293,16 +302,28 @@ impl GlobalState {
let text = if file.exists() {
let bytes = vfs.file_contents(file.file_id).to_vec();
+
String::from_utf8(bytes).ok().and_then(|text| {
+ // FIXME: Consider doing normalization in the `vfs` instead? That allows
+ // getting rid of some locking
let (text, line_endings) = LineEndings::normalize(text);
- line_endings_map.insert(file.file_id, line_endings);
- Some(Arc::from(text))
+ Some((Arc::from(text), line_endings))
})
} else {
None
};
- change.change_file(file.file_id, text);
+ // delay `line_endings_map` changes until we are done normalizing the text
+ // this allows delaying the re-acquisition of the write lock
+ bytes.push((file.file_id, text));
}
+ let (vfs, line_endings_map) = &mut *RwLockUpgradableReadGuard::upgrade(guard);
+ bytes.into_iter().for_each(|(file_id, text)| match text {
+ None => change.change_file(file_id, None),
+ Some((text, line_endings)) => {
+ line_endings_map.insert(file_id, line_endings);
+ change.change_file(file_id, Some(text));
+ }
+ });
if has_structure_changes {
let roots = self.source_root_config.partition(vfs);
change.set_roots(roots);
@@ -422,12 +443,16 @@ impl Drop for GlobalState {
}
impl GlobalStateSnapshot {
+ fn vfs_read(&self) -> MappedRwLockReadGuard<'_, vfs::Vfs> {
+ RwLockReadGuard::map(self.vfs.read(), |(it, _)| it)
+ }
+
pub(crate) fn url_to_file_id(&self, url: &Url) -> anyhow::Result<FileId> {
- url_to_file_id(&self.vfs.read().0, url)
+ url_to_file_id(&self.vfs_read(), url)
}
pub(crate) fn file_id_to_url(&self, id: FileId) -> Url {
- file_id_to_url(&self.vfs.read().0, id)
+ file_id_to_url(&self.vfs_read(), id)
}
pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable<LineIndex> {
@@ -443,7 +468,7 @@ impl GlobalStateSnapshot {
}
pub(crate) fn anchored_path(&self, path: &AnchoredPathBuf) -> Url {
- let mut base = self.vfs.read().0.file_path(path.anchor);
+ let mut base = self.vfs_read().file_path(path.anchor);
base.pop();
let path = base.join(&path.path).unwrap();
let path = path.as_path().unwrap();
@@ -451,7 +476,7 @@ impl GlobalStateSnapshot {
}
pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
- self.vfs.read().0.file_path(file_id)
+ self.vfs_read().file_path(file_id)
}
pub(crate) fn cargo_target_for_crate_root(
@@ -459,7 +484,7 @@ impl GlobalStateSnapshot {
crate_id: CrateId,
) -> Option<(&CargoWorkspace, Target)> {
let file_id = self.analysis.crate_root(crate_id).ok()?;
- let path = self.vfs.read().0.file_path(file_id);
+ let path = self.vfs_read().file_path(file_id);
let path = path.as_path()?;
self.workspaces.iter().find_map(|ws| match ws {
ProjectWorkspace::Cargo { cargo, .. } => {
@@ -471,7 +496,11 @@ impl GlobalStateSnapshot {
}
pub(crate) fn vfs_memory_usage(&self) -> usize {
- self.vfs.read().0.memory_usage()
+ self.vfs_read().memory_usage()
+ }
+
+ pub(crate) fn file_exists(&self, file_id: FileId) -> bool {
+ self.vfs.read().0.exists(file_id)
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index e830e5e9a..f9070d273 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -13,8 +13,12 @@ use triomphe::Arc;
use vfs::{AbsPathBuf, ChangeKind, VfsPath};
use crate::{
- config::Config, from_proto, global_state::GlobalState, lsp_ext::RunFlycheckParams,
- lsp_utils::apply_document_changes, mem_docs::DocumentData, reload,
+ config::Config,
+ global_state::GlobalState,
+ lsp::{from_proto, utils::apply_document_changes},
+ lsp_ext::RunFlycheckParams,
+ mem_docs::DocumentData,
+ reload,
};
pub(crate) fn handle_cancel(state: &mut GlobalState, params: CancelParams) -> anyhow::Result<()> {
@@ -84,15 +88,16 @@ pub(crate) fn handle_did_change_text_document(
}
};
- let vfs = &mut state.vfs.write().0;
- let file_id = vfs.file_id(&path).unwrap();
let text = apply_document_changes(
state.config.position_encoding(),
- || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(),
+ || {
+ let vfs = &state.vfs.read().0;
+ let file_id = vfs.file_id(&path).unwrap();
+ std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into()
+ },
params.content_changes,
);
-
- vfs.set_file_contents(path, Some(text.into_bytes()));
+ state.vfs.write().0.set_file_contents(path, Some(text.into_bytes()));
}
Ok(())
}
@@ -108,6 +113,10 @@ pub(crate) fn handle_did_close_text_document(
tracing::error!("orphan DidCloseTextDocument: {}", path);
}
+ if let Some(file_id) = state.vfs.read().0.file_id(&path) {
+ state.diagnostics.clear_native_for(file_id);
+ }
+
state.semantic_tokens_cache.lock().remove(&params.text_document.uri);
if let Some(path) = path.as_path() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index 5f1f731cf..b8a1a39be 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -11,8 +11,8 @@ use anyhow::Context;
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
- HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
- SingleResolve, SourceChange, TextEdit,
+ HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory,
+ Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
@@ -30,21 +30,23 @@ use serde_json::json;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
use triomphe::Arc;
-use vfs::{AbsPath, AbsPathBuf, VfsPath};
+use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath};
use crate::{
cargo_target_spec::CargoTargetSpec,
config::{Config, RustfmtConfig, WorkspaceSymbolConfig},
diff::diff,
- from_proto,
global_state::{GlobalState, GlobalStateSnapshot},
line_index::LineEndings,
+ lsp::{
+ from_proto, to_proto,
+ utils::{all_edits_are_disjoint, invalid_params_error},
+ LspError,
+ },
lsp_ext::{
self, CrateInfoResult, ExternalDocsPair, ExternalDocsResponse, FetchDependencyListParams,
FetchDependencyListResult, PositionOrRange, ViewCrateGraphParams, WorkspaceSymbolParams,
},
- lsp_utils::{all_edits_are_disjoint, invalid_params_error},
- to_proto, LspError,
};
pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> anyhow::Result<()> {
@@ -354,7 +356,7 @@ pub(crate) fn handle_on_type_formatting(
// This should be a single-file edit
let (_, (text_edit, snippet_edit)) = edit.source_file_edits.into_iter().next().unwrap();
- stdx::never!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
+ stdx::always!(snippet_edit.is_none(), "on type formatting shouldn't use structured snippets");
let change = to_proto::snippet_text_edit_vec(&line_index, edit.is_snippet, text_edit);
Ok(Some(change))
@@ -972,7 +974,7 @@ pub(crate) fn handle_hover(
PositionOrRange::Range(range) => range,
};
- let file_range = from_proto::file_range(&snap, params.text_document, range)?;
+ let file_range = from_proto::file_range(&snap, &params.text_document, range)?;
let info = match snap.analysis.hover(&snap.config.hover(), file_range)? {
None => return Ok(None),
Some(info) => info,
@@ -1128,7 +1130,7 @@ pub(crate) fn handle_code_action(
let line_index =
snap.file_line_index(from_proto::file_id(&snap, &params.text_document.uri)?)?;
- let frange = from_proto::file_range(&snap, params.text_document.clone(), params.range)?;
+ let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
let mut assists_config = snap.config.assist();
assists_config.allowed = params
@@ -1381,7 +1383,7 @@ pub(crate) fn handle_ssr(
let selections = params
.selections
.iter()
- .map(|range| from_proto::file_range(&snap, params.position.text_document.clone(), *range))
+ .map(|range| from_proto::file_range(&snap, &params.position.text_document, *range))
.collect::<Result<Vec<_>, _>>()?;
let position = from_proto::file_position(&snap, params.position)?;
let source_change = snap.analysis.structural_search_replace(
@@ -1401,7 +1403,7 @@ pub(crate) fn handle_inlay_hints(
let document_uri = &params.text_document.uri;
let FileRange { file_id, range } = from_proto::file_range(
&snap,
- TextDocumentIdentifier::new(document_uri.to_owned()),
+ &TextDocumentIdentifier::new(document_uri.to_owned()),
params.range,
)?;
let line_index = snap.file_line_index(file_id)?;
@@ -1410,17 +1412,73 @@ pub(crate) fn handle_inlay_hints(
snap.analysis
.inlay_hints(&inlay_hints_config, file_id, Some(range))?
.into_iter()
- .map(|it| to_proto::inlay_hint(&snap, &line_index, it))
+ .map(|it| {
+ to_proto::inlay_hint(
+ &snap,
+ &inlay_hints_config.fields_to_resolve,
+ &line_index,
+ file_id,
+ it,
+ )
+ })
.collect::<Cancellable<Vec<_>>>()?,
))
}
pub(crate) fn handle_inlay_hints_resolve(
- _snap: GlobalStateSnapshot,
- hint: InlayHint,
+ snap: GlobalStateSnapshot,
+ mut original_hint: InlayHint,
) -> anyhow::Result<InlayHint> {
let _p = profile::span("handle_inlay_hints_resolve");
- Ok(hint)
+
+ let data = match original_hint.data.take() {
+ Some(it) => it,
+ None => return Ok(original_hint),
+ };
+
+ let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?;
+ let file_id = FileId(resolve_data.file_id);
+ anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
+
+ let line_index = snap.file_line_index(file_id)?;
+ let range = from_proto::text_range(
+ &line_index,
+ lsp_types::Range { start: original_hint.position, end: original_hint.position },
+ )?;
+ let range_start = range.start();
+ let range_end = range.end();
+ let large_range = TextRange::new(
+ range_start.checked_sub(1.into()).unwrap_or(range_start),
+ range_end.checked_add(1.into()).unwrap_or(range_end),
+ );
+ let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
+ forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
+ let resolve_hints = snap.analysis.inlay_hints(
+ &forced_resolve_inlay_hints_config,
+ file_id,
+ Some(large_range),
+ )?;
+
+ let mut resolved_hints = resolve_hints
+ .into_iter()
+ .filter_map(|it| {
+ to_proto::inlay_hint(
+ &snap,
+ &forced_resolve_inlay_hints_config.fields_to_resolve,
+ &line_index,
+ file_id,
+ it,
+ )
+ .ok()
+ })
+ .filter(|hint| hint.position == original_hint.position)
+ .filter(|hint| hint.kind == original_hint.kind);
+ if let Some(resolved_hint) = resolved_hints.next() {
+ if resolved_hints.next().is_none() {
+ return Ok(resolved_hint);
+ }
+ }
+ Ok(original_hint)
}
pub(crate) fn handle_call_hierarchy_prepare(
@@ -1453,7 +1511,7 @@ pub(crate) fn handle_call_hierarchy_incoming(
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
+ let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let call_items = match snap.analysis.incoming_calls(fpos)? {
@@ -1488,7 +1546,7 @@ pub(crate) fn handle_call_hierarchy_outgoing(
let item = params.item;
let doc = TextDocumentIdentifier::new(item.uri);
- let frange = from_proto::file_range(&snap, doc, item.selection_range)?;
+ let frange = from_proto::file_range(&snap, &doc, item.selection_range)?;
let fpos = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
let call_items = match snap.analysis.outgoing_calls(fpos)? {
@@ -1569,18 +1627,21 @@ pub(crate) fn handle_semantic_tokens_full_delta(
snap.config.highlighting_non_standard_tokens(),
);
- let mut cache = snap.semantic_tokens_cache.lock();
- let cached_tokens = cache.entry(params.text_document.uri).or_default();
+ let cached_tokens = snap.semantic_tokens_cache.lock().remove(&params.text_document.uri);
- if let Some(prev_id) = &cached_tokens.result_id {
+ if let Some(cached_tokens @ lsp_types::SemanticTokens { result_id: Some(prev_id), .. }) =
+ &cached_tokens
+ {
if *prev_id == params.previous_result_id {
let delta = to_proto::semantic_token_delta(cached_tokens, &semantic_tokens);
- *cached_tokens = semantic_tokens;
+ snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens);
return Ok(Some(delta.into()));
}
}
- *cached_tokens = semantic_tokens.clone();
+ // Clone first to keep the lock short
+ let semantic_tokens_clone = semantic_tokens.clone();
+ snap.semantic_tokens_cache.lock().insert(params.text_document.uri, semantic_tokens_clone);
Ok(Some(semantic_tokens.into()))
}
@@ -1591,7 +1652,7 @@ pub(crate) fn handle_semantic_tokens_range(
) -> anyhow::Result<Option<SemanticTokensRangeResult>> {
let _p = profile::span("handle_semantic_tokens_range");
- let frange = from_proto::file_range(&snap, params.text_document, params.range)?;
+ let frange = from_proto::file_range(&snap, &params.text_document, params.range)?;
let text = snap.analysis.file_text(frange.file_id)?;
let line_index = snap.file_line_index(frange.file_id)?;
@@ -1674,7 +1735,7 @@ pub(crate) fn handle_move_item(
) -> anyhow::Result<Vec<lsp_ext::SnippetTextEdit>> {
let _p = profile::span("handle_move_item");
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
- let range = from_proto::file_range(&snap, params.text_document, params.range)?;
+ let range = from_proto::file_range(&snap, &params.text_document, params.range)?;
let direction = match params.direction {
lsp_ext::MoveItemDirection::Up => ide::Direction::Up,
@@ -1879,12 +1940,15 @@ fn run_rustfmt(
// Determine the edition of the crate the file belongs to (if there's multiple, we pick the
// highest edition).
- let editions = snap
+ let Ok(editions) = snap
.analysis
.relevant_crates_for(file_id)?
.into_iter()
.map(|crate_id| snap.analysis.crate_edition(crate_id))
- .collect::<Result<Vec<_>, _>>()?;
+ .collect::<Result<Vec<_>, _>>()
+ else {
+ return Ok(None);
+ };
let edition = editions.iter().copied().max();
let line_index = snap.file_line_index(file_id)?;
@@ -1894,23 +1958,7 @@ fn run_rustfmt(
let mut cmd = process::Command::new(toolchain::rustfmt());
cmd.envs(snap.config.extra_env());
cmd.args(extra_args);
- // try to chdir to the file so we can respect `rustfmt.toml`
- // FIXME: use `rustfmt --config-path` once
- // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
- match text_document.uri.to_file_path() {
- Ok(mut path) => {
- // pop off file name
- if path.pop() && path.is_dir() {
- cmd.current_dir(path);
- }
- }
- Err(_) => {
- tracing::error!(
- "Unable to get file path for {}, rustfmt.toml might be ignored",
- text_document.uri
- );
- }
- }
+
if let Some(edition) = edition {
cmd.arg("--edition");
cmd.arg(edition.to_string());
@@ -1929,7 +1977,7 @@ fn run_rustfmt(
.into());
}
- let frange = from_proto::file_range(snap, text_document, range)?;
+ let frange = from_proto::file_range(snap, &text_document, range)?;
let start_line = line_index.index.line_col(frange.range.start()).line;
let end_line = line_index.index.line_col(frange.range.end()).line;
@@ -1948,12 +1996,31 @@ fn run_rustfmt(
}
RustfmtConfig::CustomCommand { command, args } => {
let mut cmd = process::Command::new(command);
+
cmd.envs(snap.config.extra_env());
cmd.args(args);
cmd
}
};
+ // try to chdir to the file so we can respect `rustfmt.toml`
+ // FIXME: use `rustfmt --config-path` once
+ // https://github.com/rust-lang/rustfmt/issues/4660 gets fixed
+ match text_document.uri.to_file_path() {
+ Ok(mut path) => {
+ // pop off file name
+ if path.pop() && path.is_dir() {
+ command.current_dir(path);
+ }
+ }
+ Err(_) => {
+ tracing::error!(
+ text_document = ?text_document.uri,
+ "Unable to get path, rustfmt.toml might be ignored"
+ );
+ }
+ }
+
let mut rustfmt = command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 57e26c241..6c62577f6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -23,18 +23,13 @@ mod cargo_target_spec;
mod diagnostics;
mod diff;
mod dispatch;
-mod from_proto;
mod global_state;
mod line_index;
-mod lsp_utils;
mod main_loop;
-mod markdown;
mod mem_docs;
mod op_queue;
mod reload;
-mod semantic_tokens;
mod task_pool;
-mod to_proto;
mod version;
mod handlers {
@@ -43,13 +38,12 @@ mod handlers {
}
pub mod config;
-pub mod lsp_ext;
+pub mod lsp;
+use self::lsp::ext as lsp_ext;
#[cfg(test)]
mod integrated_benchmarks;
-use std::fmt;
-
use serde::de::DeserializeOwned;
pub use crate::{caps::server_capabilities, main_loop::main_loop, version::version};
@@ -61,23 +55,3 @@ pub fn from_json<T: DeserializeOwned>(
serde_json::from_value(json.clone())
.map_err(|e| anyhow::format_err!("Failed to deserialize {what}: {e}; {json}"))
}
-
-#[derive(Debug)]
-struct LspError {
- code: i32,
- message: String,
-}
-
-impl LspError {
- fn new(code: i32, message: String) -> LspError {
- LspError { code, message }
- }
-}
-
-impl fmt::Display for LspError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
- }
-}
-
-impl std::error::Error for LspError {}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
new file mode 100644
index 000000000..ac7e1a95e
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp.rs
@@ -0,0 +1,29 @@
+//! Custom LSP definitions and protocol conversions.
+
+use core::fmt;
+
+pub(crate) mod utils;
+pub(crate) mod semantic_tokens;
+pub mod ext;
+pub(crate) mod from_proto;
+pub(crate) mod to_proto;
+
+#[derive(Debug)]
+pub(crate) struct LspError {
+ pub(crate) code: i32,
+ pub(crate) message: String,
+}
+
+impl LspError {
+ pub(crate) fn new(code: i32, message: String) -> LspError {
+ LspError { code, message }
+ }
+}
+
+impl fmt::Display for LspError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "Language Server request failed with {}. ({})", self.code, self.message)
+ }
+}
+
+impl std::error::Error for LspError {}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index d0989b323..ad5689916 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -682,7 +682,9 @@ pub struct CompletionResolveData {
}
#[derive(Debug, Serialize, Deserialize)]
-pub struct InlayHintResolveData {}
+pub struct InlayHintResolveData {
+ pub file_id: u32,
+}
#[derive(Debug, Serialize, Deserialize)]
pub struct CompletionImport {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
index c247e1bb2..69d6aba94 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/from_proto.rs
@@ -12,8 +12,8 @@ use crate::{
from_json,
global_state::GlobalStateSnapshot,
line_index::{LineIndex, PositionEncoding},
+ lsp::utils::invalid_params_error,
lsp_ext,
- lsp_utils::invalid_params_error,
};
pub(crate) fn abs_path(url: &lsp_types::Url) -> anyhow::Result<AbsPathBuf> {
@@ -72,7 +72,7 @@ pub(crate) fn file_position(
pub(crate) fn file_range(
snap: &GlobalStateSnapshot,
- text_document_identifier: lsp_types::TextDocumentIdentifier,
+ text_document_identifier: &lsp_types::TextDocumentIdentifier,
range: lsp_types::Range,
) -> anyhow::Result<FileRange> {
file_range_uri(snap, &text_document_identifier.uri, range)
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
index 1fe02fc7e..1fe02fc7e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 7b32180e3..23074493a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -8,11 +8,12 @@ use std::{
use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
- Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
- InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory,
- RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind,
- SymbolKind, TextEdit, TextRange, TextSize,
+ Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
+ InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
+ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
+ SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
};
+use ide_db::rust_doc::format_docs;
use itertools::Itertools;
use serde_json::to_value;
use vfs::AbsPath;
@@ -22,9 +23,12 @@ use crate::{
config::{CallInfoConfig, Config},
global_state::GlobalStateSnapshot,
line_index::{LineEndings, LineIndex, PositionEncoding},
+ lsp::{
+ semantic_tokens::{self, standard_fallback_type},
+ utils::invalid_params_error,
+ LspError,
+ },
lsp_ext::{self, SnippetTextEdit},
- lsp_utils::invalid_params_error,
- semantic_tokens::{self, standard_fallback_type},
};
pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
@@ -102,7 +106,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe
}
pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
- let value = crate::markdown::format_docs(documentation.as_str());
+ let value = format_docs(&documentation);
let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
lsp_types::Documentation::MarkupContent(markup_content)
}
@@ -413,7 +417,7 @@ pub(crate) fn signature_help(
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
- value: crate::markdown::format_docs(&doc),
+ value: format_docs(&doc),
})
});
@@ -434,10 +438,25 @@ pub(crate) fn signature_help(
pub(crate) fn inlay_hint(
snap: &GlobalStateSnapshot,
+ fields_to_resolve: &InlayFieldsToResolve,
line_index: &LineIndex,
+ file_id: FileId,
inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
+ let needs_resolve = inlay_hint.needs_resolve;
+ let (label, tooltip, mut something_to_resolve) =
+ inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?;
+ let text_edits = if needs_resolve && fields_to_resolve.resolve_text_edits {
+ something_to_resolve |= inlay_hint.text_edit.is_some();
+ None
+ } else {
+ inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
+ };
+ let data = if needs_resolve && something_to_resolve {
+ Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap())
+ } else {
+ None
+ };
Ok(lsp_types::InlayHint {
position: match inlay_hint.position {
@@ -451,8 +470,8 @@ pub(crate) fn inlay_hint(
InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE),
_ => None,
},
- text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)),
- data: None,
+ text_edits,
+ data,
tooltip,
label,
})
@@ -460,13 +479,18 @@ pub(crate) fn inlay_hint(
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
+ fields_to_resolve: &InlayFieldsToResolve,
+ needs_resolve: bool,
mut label: InlayHintLabel,
-) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
- let res = match &*label.parts {
+) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>, bool)> {
+ let mut something_to_resolve = false;
+ let (label, tooltip) = match &*label.parts {
[InlayHintLabelPart { linked_location: None, .. }] => {
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
- (
- lsp_types::InlayHintLabel::String(text),
+ let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip {
+ something_to_resolve |= tooltip.is_some();
+ None
+ } else {
match tooltip {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintTooltip::String(s))
@@ -478,41 +502,52 @@ fn inlay_hint_label(
}))
}
None => None,
- },
- )
+ }
+ };
+ (lsp_types::InlayHintLabel::String(text), hint_tooltip)
}
_ => {
let parts = label
.parts
.into_iter()
.map(|part| {
- part.linked_location.map(|range| location(snap, range)).transpose().map(
- |location| lsp_types::InlayHintLabelPart {
- value: part.text,
- tooltip: match part.tooltip {
- Some(ide::InlayTooltip::String(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::String(s))
- }
- Some(ide::InlayTooltip::Markdown(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
- lsp_types::MarkupContent {
- kind: lsp_types::MarkupKind::Markdown,
- value: s,
- },
- ))
- }
- None => None,
- },
- location,
- command: None,
- },
- )
+ let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip {
+ something_to_resolve |= part.tooltip.is_some();
+ None
+ } else {
+ match part.tooltip {
+ Some(ide::InlayTooltip::String(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::String(s))
+ }
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
+ lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ },
+ ))
+ }
+ None => None,
+ }
+ };
+ let location = if needs_resolve && fields_to_resolve.resolve_label_location {
+ something_to_resolve |= part.linked_location.is_some();
+ None
+ } else {
+ part.linked_location.map(|range| location(snap, range)).transpose()?
+ };
+ Ok(lsp_types::InlayHintLabelPart {
+ value: part.text,
+ tooltip,
+ location,
+ command: None,
+ })
})
.collect::<Cancellable<_>>()?;
(lsp_types::InlayHintLabel::LabelParts(parts), None)
}
};
- Ok(res)
+ Ok((label, tooltip, something_to_resolve))
}
static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);
@@ -1323,17 +1358,18 @@ pub(crate) fn code_lens(
})
}
}
- AnnotationKind::HasImpls { pos: file_range, data } => {
+ AnnotationKind::HasImpls { pos, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_range.file_id)?;
+ let line_index = snap.file_line_index(pos.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_range.file_id);
+ let url = url(snap, pos.file_id);
+ let pos = position(&line_index, pos.offset);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
- let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
+ let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
let goto_params = lsp_types::request::GotoImplementationParams {
text_document_position_params: doc_pos,
@@ -1356,7 +1392,7 @@ pub(crate) fn code_lens(
command::show_references(
implementation_title(locations.len()),
&url,
- annotation_range.start,
+ pos,
locations,
)
});
@@ -1376,28 +1412,24 @@ pub(crate) fn code_lens(
})(),
})
}
- AnnotationKind::HasReferences { pos: file_range, data } => {
+ AnnotationKind::HasReferences { pos, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_range.file_id)?;
+ let line_index = snap.file_line_index(pos.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_range.file_id);
+ let url = url(snap, pos.file_id);
+ let pos = position(&line_index, pos.offset);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
- let doc_pos = lsp_types::TextDocumentPositionParams::new(id, annotation_range.start);
+ let doc_pos = lsp_types::TextDocumentPositionParams::new(id, pos);
let command = data.map(|ranges| {
let locations: Vec<lsp_types::Location> =
ranges.into_iter().filter_map(|range| location(snap, range).ok()).collect();
- command::show_references(
- reference_title(locations.len()),
- &url,
- annotation_range.start,
- locations,
- )
+ command::show_references(reference_title(locations.len()), &url, pos, locations)
});
acc.push(lsp_types::CodeLens {
@@ -1425,8 +1457,8 @@ pub(crate) mod command {
use crate::{
global_state::GlobalStateSnapshot,
+ lsp::to_proto::{location, location_link},
lsp_ext,
- to_proto::{location, location_link},
};
pub(crate) fn show_references(
@@ -1528,11 +1560,11 @@ pub(crate) fn markup_content(
ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
};
- let value = crate::markdown::format_docs(markup.as_str());
+ let value = format_docs(&Documentation::new(markup.into()));
lsp_types::MarkupContent { kind, value }
}
-pub(crate) fn rename_error(err: RenameError) -> crate::LspError {
+pub(crate) fn rename_error(err: RenameError) -> LspError {
// This is wrong, but we don't have a better alternative I suppose?
// https://github.com/microsoft/language-server-protocol/issues/1341
invalid_params_error(err.to_string())
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
index 74e79e8e6..b388b3175 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/utils.rs
@@ -6,10 +6,10 @@ use lsp_types::request::Request;
use triomphe::Arc;
use crate::{
- from_proto,
global_state::GlobalState,
line_index::{LineEndings, LineIndex, PositionEncoding},
- lsp_ext, LspError,
+ lsp::{from_proto, LspError},
+ lsp_ext,
};
pub(crate) fn invalid_params_error(message: String) -> LspError {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 74036710f..cdf41c955 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -17,11 +17,14 @@ use vfs::FileId;
use crate::{
config::Config,
+ diagnostics::fetch_native_diagnostics,
dispatch::{NotificationDispatcher, RequestDispatcher},
- from_proto,
global_state::{file_id_to_url, url_to_file_id, GlobalState},
+ lsp::{
+ from_proto,
+ utils::{notification_is, Progress},
+ },
lsp_ext,
- lsp_utils::{notification_is, Progress},
reload::{BuildDataProgress, ProcMacroProgress, ProjectWorkspaceProgress},
};
@@ -317,8 +320,11 @@ impl GlobalState {
}
// Refresh inlay hints if the client supports it.
- if self.config.inlay_hints_refresh() {
+ if (self.send_hint_refresh_query || self.proc_macro_changed)
+ && self.config.inlay_hints_refresh()
+ {
self.send_request::<lsp_types::request::InlayHintRefreshRequest>((), |_, _| ());
+ self.send_hint_refresh_query = false;
}
}
@@ -420,6 +426,32 @@ impl GlobalState {
});
}
+ fn update_diagnostics(&mut self) {
+ let db = self.analysis_host.raw_database();
+ let subscriptions = self
+ .mem_docs
+ .iter()
+ .map(|path| self.vfs.read().0.file_id(path).unwrap())
+ .filter(|&file_id| {
+ let source_root = db.file_source_root(file_id);
+ // Only publish diagnostics for files in the workspace, not from crates.io deps
+ // or the sysroot.
+ // While theoretically these should never have errors, we have quite a few false
+ // positives particularly in the stdlib, and those diagnostics would stay around
+ // forever if we emitted them here.
+ !db.source_root(source_root).is_library
+ })
+ .collect::<Vec<_>>();
+ tracing::trace!("updating notifications for {:?}", subscriptions);
+
+ // Diagnostics are triggered by the user typing
+ // so we run them on a latency sensitive thread.
+ self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, {
+ let snapshot = self.snapshot();
+ move || Task::Diagnostics(fetch_native_diagnostics(snapshot, subscriptions))
+ });
+ }
+
fn update_status_or_notify(&mut self) {
let status = self.current_status();
if self.last_reported_status.as_ref() != Some(&status) {
@@ -509,6 +541,7 @@ impl GlobalState {
}
self.switch_workspaces("fetched build data".to_string());
+ self.send_hint_refresh_query = true;
(Some(Progress::End), None)
}
@@ -525,7 +558,7 @@ impl GlobalState {
ProcMacroProgress::End(proc_macro_load_result) => {
self.fetch_proc_macros_queue.op_completed(true);
self.set_proc_macros(proc_macro_load_result);
-
+ self.send_hint_refresh_query = true;
(Some(Progress::End), None)
}
};
@@ -670,6 +703,7 @@ impl GlobalState {
}
use crate::handlers::request as handlers;
+ use lsp_types::request as lsp_request;
dispatcher
// Request handlers that must run on the main thread
@@ -682,30 +716,30 @@ impl GlobalState {
// are run on the main thread to reduce latency:
.on_sync::<lsp_ext::JoinLines>(handlers::handle_join_lines)
.on_sync::<lsp_ext::OnEnter>(handlers::handle_on_enter)
- .on_sync::<lsp_types::request::SelectionRangeRequest>(handlers::handle_selection_range)
+ .on_sync::<lsp_request::SelectionRangeRequest>(handlers::handle_selection_range)
.on_sync::<lsp_ext::MatchingBrace>(handlers::handle_matching_brace)
.on_sync::<lsp_ext::OnTypeFormatting>(handlers::handle_on_type_formatting)
// Formatting should be done immediately as the editor might wait on it, but we can't
// put it on the main thread as we do not want the main thread to block on rustfmt.
// So we have an extra thread just for formatting requests to make sure it gets handled
// as fast as possible.
- .on_fmt_thread::<lsp_types::request::Formatting>(handlers::handle_formatting)
- .on_fmt_thread::<lsp_types::request::RangeFormatting>(handlers::handle_range_formatting)
+ .on_fmt_thread::<lsp_request::Formatting>(handlers::handle_formatting)
+ .on_fmt_thread::<lsp_request::RangeFormatting>(handlers::handle_range_formatting)
// We can’t run latency-sensitive request handlers which do semantic
// analysis on the main thread because that would block other
// requests. Instead, we run these request handlers on higher priority
// threads in the threadpool.
- .on_latency_sensitive::<lsp_types::request::Completion>(handlers::handle_completion)
- .on_latency_sensitive::<lsp_types::request::ResolveCompletionItem>(
+ .on_latency_sensitive::<lsp_request::Completion>(handlers::handle_completion)
+ .on_latency_sensitive::<lsp_request::ResolveCompletionItem>(
handlers::handle_completion_resolve,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensFullRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensFullRequest>(
handlers::handle_semantic_tokens_full,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensFullDeltaRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensFullDeltaRequest>(
handlers::handle_semantic_tokens_full_delta,
)
- .on_latency_sensitive::<lsp_types::request::SemanticTokensRangeRequest>(
+ .on_latency_sensitive::<lsp_request::SemanticTokensRangeRequest>(
handlers::handle_semantic_tokens_range,
)
// All other request handlers
@@ -729,29 +763,25 @@ impl GlobalState {
.on::<lsp_ext::OpenCargoToml>(handlers::handle_open_cargo_toml)
.on::<lsp_ext::MoveItem>(handlers::handle_move_item)
.on::<lsp_ext::WorkspaceSymbol>(handlers::handle_workspace_symbol)
- .on::<lsp_types::request::DocumentSymbolRequest>(handlers::handle_document_symbol)
- .on::<lsp_types::request::GotoDefinition>(handlers::handle_goto_definition)
- .on::<lsp_types::request::GotoDeclaration>(handlers::handle_goto_declaration)
- .on::<lsp_types::request::GotoImplementation>(handlers::handle_goto_implementation)
- .on::<lsp_types::request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
- .on_no_retry::<lsp_types::request::InlayHintRequest>(handlers::handle_inlay_hints)
- .on::<lsp_types::request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
- .on::<lsp_types::request::CodeLensRequest>(handlers::handle_code_lens)
- .on::<lsp_types::request::CodeLensResolve>(handlers::handle_code_lens_resolve)
- .on::<lsp_types::request::FoldingRangeRequest>(handlers::handle_folding_range)
- .on::<lsp_types::request::SignatureHelpRequest>(handlers::handle_signature_help)
- .on::<lsp_types::request::PrepareRenameRequest>(handlers::handle_prepare_rename)
- .on::<lsp_types::request::Rename>(handlers::handle_rename)
- .on::<lsp_types::request::References>(handlers::handle_references)
- .on::<lsp_types::request::DocumentHighlightRequest>(handlers::handle_document_highlight)
- .on::<lsp_types::request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
- .on::<lsp_types::request::CallHierarchyIncomingCalls>(
- handlers::handle_call_hierarchy_incoming,
- )
- .on::<lsp_types::request::CallHierarchyOutgoingCalls>(
- handlers::handle_call_hierarchy_outgoing,
- )
- .on::<lsp_types::request::WillRenameFiles>(handlers::handle_will_rename_files)
+ .on::<lsp_request::DocumentSymbolRequest>(handlers::handle_document_symbol)
+ .on::<lsp_request::GotoDefinition>(handlers::handle_goto_definition)
+ .on::<lsp_request::GotoDeclaration>(handlers::handle_goto_declaration)
+ .on::<lsp_request::GotoImplementation>(handlers::handle_goto_implementation)
+ .on::<lsp_request::GotoTypeDefinition>(handlers::handle_goto_type_definition)
+ .on_no_retry::<lsp_request::InlayHintRequest>(handlers::handle_inlay_hints)
+ .on::<lsp_request::InlayHintResolveRequest>(handlers::handle_inlay_hints_resolve)
+ .on::<lsp_request::CodeLensRequest>(handlers::handle_code_lens)
+ .on::<lsp_request::CodeLensResolve>(handlers::handle_code_lens_resolve)
+ .on::<lsp_request::FoldingRangeRequest>(handlers::handle_folding_range)
+ .on::<lsp_request::SignatureHelpRequest>(handlers::handle_signature_help)
+ .on::<lsp_request::PrepareRenameRequest>(handlers::handle_prepare_rename)
+ .on::<lsp_request::Rename>(handlers::handle_rename)
+ .on::<lsp_request::References>(handlers::handle_references)
+ .on::<lsp_request::DocumentHighlightRequest>(handlers::handle_document_highlight)
+ .on::<lsp_request::CallHierarchyPrepare>(handlers::handle_call_hierarchy_prepare)
+ .on::<lsp_request::CallHierarchyIncomingCalls>(handlers::handle_call_hierarchy_incoming)
+ .on::<lsp_request::CallHierarchyOutgoingCalls>(handlers::handle_call_hierarchy_outgoing)
+ .on::<lsp_request::WillRenameFiles>(handlers::handle_will_rename_files)
.on::<lsp_ext::Ssr>(handlers::handle_ssr)
.on::<lsp_ext::ViewRecursiveMemoryLayout>(handlers::handle_view_recursive_memory_layout)
.finish();
@@ -788,77 +818,4 @@ impl GlobalState {
.finish();
Ok(())
}
-
- fn update_diagnostics(&mut self) {
- let db = self.analysis_host.raw_database();
- let subscriptions = self
- .mem_docs
- .iter()
- .map(|path| self.vfs.read().0.file_id(path).unwrap())
- .filter(|&file_id| {
- let source_root = db.file_source_root(file_id);
- // Only publish diagnostics for files in the workspace, not from crates.io deps
- // or the sysroot.
- // While theoretically these should never have errors, we have quite a few false
- // positives particularly in the stdlib, and those diagnostics would stay around
- // forever if we emitted them here.
- !db.source_root(source_root).is_library
- })
- .collect::<Vec<_>>();
-
- tracing::trace!("updating notifications for {:?}", subscriptions);
-
- let snapshot = self.snapshot();
-
- // Diagnostics are triggered by the user typing
- // so we run them on a latency sensitive thread.
- self.task_pool.handle.spawn(ThreadIntent::LatencySensitive, move || {
- let _p = profile::span("publish_diagnostics");
- let _ctx = stdx::panic_context::enter("publish_diagnostics".to_owned());
- let diagnostics = subscriptions
- .into_iter()
- .filter_map(|file_id| {
- let line_index = snapshot.file_line_index(file_id).ok()?;
- Some((
- file_id,
- line_index,
- snapshot
- .analysis
- .diagnostics(
- &snapshot.config.diagnostics(),
- ide::AssistResolveStrategy::None,
- file_id,
- )
- .ok()?,
- ))
- })
- .map(|(file_id, line_index, it)| {
- (
- file_id,
- it.into_iter()
- .map(move |d| lsp_types::Diagnostic {
- range: crate::to_proto::range(&line_index, d.range),
- severity: Some(crate::to_proto::diagnostic_severity(d.severity)),
- code: Some(lsp_types::NumberOrString::String(
- d.code.as_str().to_string(),
- )),
- code_description: Some(lsp_types::CodeDescription {
- href: lsp_types::Url::parse(&d.code.url()).unwrap(),
- }),
- source: Some("rust-analyzer".to_string()),
- message: d.message,
- related_information: None,
- tags: if d.unused {
- Some(vec![lsp_types::DiagnosticTag::UNNECESSARY])
- } else {
- None
- },
- data: None,
- })
- .collect::<Vec<_>>(),
- )
- });
- Task::Diagnostics(diagnostics.collect())
- });
- }
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
deleted file mode 100644
index 58426c66a..000000000
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/markdown.rs
+++ /dev/null
@@ -1,165 +0,0 @@
-//! Transforms markdown
-use ide_db::rust_doc::is_rust_fence;
-
-const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
-
-pub(crate) fn format_docs(src: &str) -> String {
- let mut processed_lines = Vec::new();
- let mut in_code_block = false;
- let mut is_rust = false;
-
- for mut line in src.lines() {
- if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
- continue;
- }
-
- if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
- {
- in_code_block ^= true;
-
- if in_code_block {
- is_rust = is_rust_fence(header);
-
- if is_rust {
- line = "```rust";
- }
- }
- }
-
- if in_code_block {
- let trimmed = line.trim_start();
- if is_rust && trimmed.starts_with("##") {
- line = &trimmed[1..];
- }
- }
-
- processed_lines.push(line);
- }
- processed_lines.join("\n")
-}
-
-fn code_line_ignored_by_rustdoc(line: &str) -> bool {
- let trimmed = line.trim();
- trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_format_docs_adds_rust() {
- let comment = "```\nfn some_rust() {}\n```";
- assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
- }
-
- #[test]
- fn test_format_docs_handles_plain_text() {
- let comment = "```text\nthis is plain text\n```";
- assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
- }
-
- #[test]
- fn test_format_docs_handles_non_rust() {
- let comment = "```sh\nsupposedly shell code\n```";
- assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
- }
-
- #[test]
- fn test_format_docs_handles_rust_alias() {
- let comment = "```ignore\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_complex_code_block_attrs() {
- let comment = "```rust,no_run\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_error_codes() {
- let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
- }
-
- #[test]
- fn test_format_docs_skips_comments_in_rust_block() {
- let comment =
- "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
- assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
- }
-
- #[test]
- fn test_format_docs_does_not_skip_lines_if_plain_text() {
- let comment =
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
- assert_eq!(
- format_docs(comment),
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
- );
- }
-
- #[test]
- fn test_format_docs_keeps_comments_outside_of_rust_block() {
- let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_format_docs_preserves_newlines() {
- let comment = "this\nis\nmultiline";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_rust() {
- let comment = r#"```rust
-fn main(){}
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_text() {
- let comment = r#"```text
-filler
-text
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_format_docs_handles_escape_double_hashes() {
- let comment = r#"```rust
-let s = "foo
-## bar # baz";
-```"#;
-
- assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
- }
-
- #[test]
- fn test_format_docs_handles_double_hashes_non_rust() {
- let comment = r#"```markdown
-## A second-level heading
-```"#;
- assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
- }
-}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 0a2bb8224..3fae08b82 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -12,6 +12,7 @@
//! correct. Instead, we try to provide a best-effort service. Even if the
//! project is currently loading and we don't have a full project model, we
//! still want to respond to various requests.
+// FIXME: This is a mess that needs some untangling work
use std::{iter, mem};
use flycheck::{FlycheckConfig, FlycheckHandle};
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 0bb29e708..d59914298 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -29,7 +29,7 @@ use lsp_types::{
PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem,
TextDocumentPositionParams, WorkDoneProgressParams,
};
-use rust_analyzer::lsp_ext::{OnEnter, Runnables, RunnablesParams};
+use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams};
use serde_json::json;
use test_utils::skip_slow_tests;
@@ -861,6 +861,7 @@ edition = "2021"
bar = {path = "../bar"}
//- /foo/src/main.rs
+#![allow(internal_features)]
#![feature(rustc_attrs, decl_macro)]
use bar::Bar;
@@ -938,7 +939,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
let res = server.send_request::<HoverRequest>(HoverParams {
text_document_position_params: TextDocumentPositionParams::new(
server.doc_id("foo/src/main.rs"),
- Position::new(11, 9),
+ Position::new(12, 9),
),
work_done_progress_params: Default::default(),
});
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 3c52ef5ef..e49b5768f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -9,7 +9,7 @@ use std::{
use crossbeam_channel::{after, select, Receiver};
use lsp_server::{Connection, Message, Notification, Request};
use lsp_types::{notification::Exit, request::Shutdown, TextDocumentIdentifier, Url};
-use rust_analyzer::{config::Config, lsp_ext, main_loop};
+use rust_analyzer::{config::Config, lsp, main_loop};
use serde::Serialize;
use serde_json::{json, to_string_pretty, Value};
use test_utils::FixtureWithProjectMeta;
@@ -260,9 +260,9 @@ impl Server {
Message::Notification(n) if n.method == "experimental/serverStatus" => {
let status = n
.clone()
- .extract::<lsp_ext::ServerStatusParams>("experimental/serverStatus")
+ .extract::<lsp::ext::ServerStatusParams>("experimental/serverStatus")
.unwrap();
- if status.health != lsp_ext::Health::Ok {
+ if status.health != lsp::ext::Health::Ok {
panic!("server errored/warned while loading workspace: {:?}", status.message);
}
status.quiescent
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index f230cba2b..8b5c92c66 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
@@ -35,7 +35,7 @@ fn check_lsp_extensions_docs() {
let expected_hash = {
let lsp_ext_rs = sh
- .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp_ext.rs"))
+ .read_file(sourcegen::project_root().join("crates/rust-analyzer/src/lsp/ext.rs"))
.unwrap();
stable_hash(lsp_ext_rs.as_str())
};
@@ -45,7 +45,7 @@ fn check_lsp_extensions_docs() {
sh.read_file(sourcegen::project_root().join("docs/dev/lsp-extensions.md")).unwrap();
let text = lsp_extensions_md
.lines()
- .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
+ .find_map(|line| line.strip_prefix("lsp/ext.rs hash:"))
.unwrap()
.trim();
u64::from_str_radix(text, 16).unwrap()
@@ -54,7 +54,7 @@ fn check_lsp_extensions_docs() {
if actual_hash != expected_hash {
panic!(
"
-lsp_ext.rs was changed without touching lsp-extensions.md.
+lsp/ext.rs was changed without touching lsp-extensions.md.
Expected hash: {expected_hash:x}
Actual hash: {actual_hash:x}
@@ -158,7 +158,7 @@ Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT
Apache-2.0/MIT
BSD-3-Clause
BlueOak-1.0.0 OR MIT OR Apache-2.0
-CC0-1.0 OR Artistic-2.0
+CC0-1.0
ISC
MIT
MIT / Apache-2.0
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 138ddd208..3603560d3 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -296,7 +296,7 @@ TypeParam =
ConstParam =
Attr* 'const' Name ':' Type
- ('=' default_val:Expr)?
+ ('=' default_val:ConstArg)?
LifetimeParam =
Attr* Lifetime (':' TypeBoundList?)?
@@ -340,10 +340,10 @@ ExprStmt =
Expr =
ArrayExpr
+| AsmExpr
| AwaitExpr
| BinExpr
| BlockExpr
-| BoxExpr
| BreakExpr
| CallExpr
| CastExpr
@@ -351,6 +351,7 @@ Expr =
| ContinueExpr
| FieldExpr
| ForExpr
+| FormatArgsExpr
| IfExpr
| IndexExpr
| Literal
@@ -358,6 +359,7 @@ Expr =
| MacroExpr
| MatchExpr
| MethodCallExpr
+| OffsetOfExpr
| ParenExpr
| PathExpr
| PrefixExpr
@@ -373,6 +375,21 @@ Expr =
| LetExpr
| UnderscoreExpr
+OffsetOfExpr =
+ Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
+
+AsmExpr =
+ Attr* 'builtin' '#' 'asm' '(' Expr ')'
+
+FormatArgsExpr =
+ Attr* 'builtin' '#' 'format_args' '('
+ template:Expr
+ (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )?
+ ')'
+
+FormatArgsArg =
+ (Name '=')? Expr
+
MacroExpr =
MacroCall
@@ -526,9 +543,6 @@ UnderscoreExpr =
AwaitExpr =
Attr* Expr '.' 'await'
-BoxExpr =
- Attr* 'box' Expr
-
//*************************//
// Types //
//*************************//
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 0b27faa53..7ba0d4dc6 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -709,7 +709,7 @@ impl ConstParam {
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn default_val(&self) -> Option<ConstArg> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -805,6 +805,20 @@ impl ArrayExpr {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct AsmExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for AsmExpr {}
+impl AsmExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AwaitExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -823,16 +837,6 @@ impl ast::HasAttrs for BinExpr {}
impl BinExpr {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct BoxExpr {
- pub(crate) syntax: SyntaxNode,
-}
-impl ast::HasAttrs for BoxExpr {}
-impl BoxExpr {
- pub fn box_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![box]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct BreakExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -916,6 +920,24 @@ impl ForExpr {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FormatArgsExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for FormatArgsExpr {}
+impl FormatArgsExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn format_args_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![format_args])
+ }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn template(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ pub fn args(&self) -> AstChildren<FormatArgsArg> { support::children(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IfExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -985,6 +1007,24 @@ impl MethodCallExpr {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct OffsetOfExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for OffsetOfExpr {}
+impl OffsetOfExpr {
+ pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+ pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
+ pub fn offset_of_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![offset_of])
+ }
+ pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
+ pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
+ pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
+ pub fn fields(&self) -> AstChildren<NameRef> { support::children(&self.syntax) }
+ pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParenExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -1127,6 +1167,16 @@ impl UnderscoreExpr {
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct FormatArgsArg {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasName for FormatArgsArg {}
+impl FormatArgsArg {
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
}
@@ -1555,10 +1605,10 @@ pub enum Type {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Expr {
ArrayExpr(ArrayExpr),
+ AsmExpr(AsmExpr),
AwaitExpr(AwaitExpr),
BinExpr(BinExpr),
BlockExpr(BlockExpr),
- BoxExpr(BoxExpr),
BreakExpr(BreakExpr),
CallExpr(CallExpr),
CastExpr(CastExpr),
@@ -1566,6 +1616,7 @@ pub enum Expr {
ContinueExpr(ContinueExpr),
FieldExpr(FieldExpr),
ForExpr(ForExpr),
+ FormatArgsExpr(FormatArgsExpr),
IfExpr(IfExpr),
IndexExpr(IndexExpr),
Literal(Literal),
@@ -1573,6 +1624,7 @@ pub enum Expr {
MacroExpr(MacroExpr),
MatchExpr(MatchExpr),
MethodCallExpr(MethodCallExpr),
+ OffsetOfExpr(OffsetOfExpr),
ParenExpr(ParenExpr),
PathExpr(PathExpr),
PrefixExpr(PrefixExpr),
@@ -2453,8 +2505,8 @@ impl AstNode for ArrayExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for AwaitExpr {
- fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR }
+impl AstNode for AsmExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
@@ -2464,8 +2516,8 @@ impl AstNode for AwaitExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for BinExpr {
- fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR }
+impl AstNode for AwaitExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == AWAIT_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
@@ -2475,8 +2527,8 @@ impl AstNode for BinExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for BoxExpr {
- fn can_cast(kind: SyntaxKind) -> bool { kind == BOX_EXPR }
+impl AstNode for BinExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == BIN_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
@@ -2563,6 +2615,17 @@ impl AstNode for ForExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for FormatArgsExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for IfExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == IF_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2640,6 +2703,17 @@ impl AstNode for MethodCallExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for OffsetOfExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == OFFSET_OF_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for ParenExpr {
fn can_cast(kind: SyntaxKind) -> bool { kind == PAREN_EXPR }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -2794,6 +2868,17 @@ impl AstNode for UnderscoreExpr {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for FormatArgsArg {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for StmtList {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
@@ -3373,6 +3458,9 @@ impl AstNode for Type {
impl From<ArrayExpr> for Expr {
fn from(node: ArrayExpr) -> Expr { Expr::ArrayExpr(node) }
}
+impl From<AsmExpr> for Expr {
+ fn from(node: AsmExpr) -> Expr { Expr::AsmExpr(node) }
+}
impl From<AwaitExpr> for Expr {
fn from(node: AwaitExpr) -> Expr { Expr::AwaitExpr(node) }
}
@@ -3382,9 +3470,6 @@ impl From<BinExpr> for Expr {
impl From<BlockExpr> for Expr {
fn from(node: BlockExpr) -> Expr { Expr::BlockExpr(node) }
}
-impl From<BoxExpr> for Expr {
- fn from(node: BoxExpr) -> Expr { Expr::BoxExpr(node) }
-}
impl From<BreakExpr> for Expr {
fn from(node: BreakExpr) -> Expr { Expr::BreakExpr(node) }
}
@@ -3406,6 +3491,9 @@ impl From<FieldExpr> for Expr {
impl From<ForExpr> for Expr {
fn from(node: ForExpr) -> Expr { Expr::ForExpr(node) }
}
+impl From<FormatArgsExpr> for Expr {
+ fn from(node: FormatArgsExpr) -> Expr { Expr::FormatArgsExpr(node) }
+}
impl From<IfExpr> for Expr {
fn from(node: IfExpr) -> Expr { Expr::IfExpr(node) }
}
@@ -3427,6 +3515,9 @@ impl From<MatchExpr> for Expr {
impl From<MethodCallExpr> for Expr {
fn from(node: MethodCallExpr) -> Expr { Expr::MethodCallExpr(node) }
}
+impl From<OffsetOfExpr> for Expr {
+ fn from(node: OffsetOfExpr) -> Expr { Expr::OffsetOfExpr(node) }
+}
impl From<ParenExpr> for Expr {
fn from(node: ParenExpr) -> Expr { Expr::ParenExpr(node) }
}
@@ -3474,10 +3565,10 @@ impl AstNode for Expr {
matches!(
kind,
ARRAY_EXPR
+ | ASM_EXPR
| AWAIT_EXPR
| BIN_EXPR
| BLOCK_EXPR
- | BOX_EXPR
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
@@ -3485,6 +3576,7 @@ impl AstNode for Expr {
| CONTINUE_EXPR
| FIELD_EXPR
| FOR_EXPR
+ | FORMAT_ARGS_EXPR
| IF_EXPR
| INDEX_EXPR
| LITERAL
@@ -3492,6 +3584,7 @@ impl AstNode for Expr {
| MACRO_EXPR
| MATCH_EXPR
| METHOD_CALL_EXPR
+ | OFFSET_OF_EXPR
| PAREN_EXPR
| PATH_EXPR
| PREFIX_EXPR
@@ -3511,10 +3604,10 @@ impl AstNode for Expr {
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
ARRAY_EXPR => Expr::ArrayExpr(ArrayExpr { syntax }),
+ ASM_EXPR => Expr::AsmExpr(AsmExpr { syntax }),
AWAIT_EXPR => Expr::AwaitExpr(AwaitExpr { syntax }),
BIN_EXPR => Expr::BinExpr(BinExpr { syntax }),
BLOCK_EXPR => Expr::BlockExpr(BlockExpr { syntax }),
- BOX_EXPR => Expr::BoxExpr(BoxExpr { syntax }),
BREAK_EXPR => Expr::BreakExpr(BreakExpr { syntax }),
CALL_EXPR => Expr::CallExpr(CallExpr { syntax }),
CAST_EXPR => Expr::CastExpr(CastExpr { syntax }),
@@ -3522,6 +3615,7 @@ impl AstNode for Expr {
CONTINUE_EXPR => Expr::ContinueExpr(ContinueExpr { syntax }),
FIELD_EXPR => Expr::FieldExpr(FieldExpr { syntax }),
FOR_EXPR => Expr::ForExpr(ForExpr { syntax }),
+ FORMAT_ARGS_EXPR => Expr::FormatArgsExpr(FormatArgsExpr { syntax }),
IF_EXPR => Expr::IfExpr(IfExpr { syntax }),
INDEX_EXPR => Expr::IndexExpr(IndexExpr { syntax }),
LITERAL => Expr::Literal(Literal { syntax }),
@@ -3529,6 +3623,7 @@ impl AstNode for Expr {
MACRO_EXPR => Expr::MacroExpr(MacroExpr { syntax }),
MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }),
METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }),
+ OFFSET_OF_EXPR => Expr::OffsetOfExpr(OffsetOfExpr { syntax }),
PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }),
PATH_EXPR => Expr::PathExpr(PathExpr { syntax }),
PREFIX_EXPR => Expr::PrefixExpr(PrefixExpr { syntax }),
@@ -3550,10 +3645,10 @@ impl AstNode for Expr {
fn syntax(&self) -> &SyntaxNode {
match self {
Expr::ArrayExpr(it) => &it.syntax,
+ Expr::AsmExpr(it) => &it.syntax,
Expr::AwaitExpr(it) => &it.syntax,
Expr::BinExpr(it) => &it.syntax,
Expr::BlockExpr(it) => &it.syntax,
- Expr::BoxExpr(it) => &it.syntax,
Expr::BreakExpr(it) => &it.syntax,
Expr::CallExpr(it) => &it.syntax,
Expr::CastExpr(it) => &it.syntax,
@@ -3561,6 +3656,7 @@ impl AstNode for Expr {
Expr::ContinueExpr(it) => &it.syntax,
Expr::FieldExpr(it) => &it.syntax,
Expr::ForExpr(it) => &it.syntax,
+ Expr::FormatArgsExpr(it) => &it.syntax,
Expr::IfExpr(it) => &it.syntax,
Expr::IndexExpr(it) => &it.syntax,
Expr::Literal(it) => &it.syntax,
@@ -3568,6 +3664,7 @@ impl AstNode for Expr {
Expr::MacroExpr(it) => &it.syntax,
Expr::MatchExpr(it) => &it.syntax,
Expr::MethodCallExpr(it) => &it.syntax,
+ Expr::OffsetOfExpr(it) => &it.syntax,
Expr::ParenExpr(it) => &it.syntax,
Expr::PathExpr(it) => &it.syntax,
Expr::PrefixExpr(it) => &it.syntax,
@@ -4028,9 +4125,9 @@ impl AstNode for AnyHasAttrs {
| TYPE_PARAM
| LET_STMT
| ARRAY_EXPR
+ | ASM_EXPR
| AWAIT_EXPR
| BIN_EXPR
- | BOX_EXPR
| BREAK_EXPR
| CALL_EXPR
| CAST_EXPR
@@ -4038,12 +4135,14 @@ impl AstNode for AnyHasAttrs {
| CONTINUE_EXPR
| FIELD_EXPR
| FOR_EXPR
+ | FORMAT_ARGS_EXPR
| IF_EXPR
| INDEX_EXPR
| LITERAL
| LOOP_EXPR
| MATCH_EXPR
| METHOD_CALL_EXPR
+ | OFFSET_OF_EXPR
| PAREN_EXPR
| PATH_EXPR
| PREFIX_EXPR
@@ -4179,6 +4278,7 @@ impl AstNode for AnyHasName {
| VARIANT
| CONST_PARAM
| TYPE_PARAM
+ | FORMAT_ARGS_ARG
| IDENT_PAT
)
}
@@ -4620,17 +4720,17 @@ impl std::fmt::Display for ArrayExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for AwaitExpr {
+impl std::fmt::Display for AsmExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for BinExpr {
+impl std::fmt::Display for AwaitExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for BoxExpr {
+impl std::fmt::Display for BinExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
@@ -4670,6 +4770,11 @@ impl std::fmt::Display for ForExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for FormatArgsExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for IfExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4705,6 +4810,11 @@ impl std::fmt::Display for MethodCallExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for OffsetOfExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for ParenExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@@ -4775,6 +4885,11 @@ impl std::fmt::Display for UnderscoreExpr {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for FormatArgsArg {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for StmtList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 4c6db0ef0..17e311c0c 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -503,11 +503,16 @@ pub fn hacky_block_expr(
pub fn expr_unit() -> ast::Expr {
expr_from_text("()")
}
+
pub fn expr_literal(text: &str) -> ast::Literal {
assert_eq!(text.trim(), text);
ast_from_text(&format!("fn f() {{ let _ = {text}; }}"))
}
+pub fn expr_const_value(text: &str) -> ast::ConstArg {
+ ast_from_text(&format!("trait Foo<const N: usize = {text}> {{}}"))
+}
+
pub fn expr_empty_block() -> ast::Expr {
expr_from_text("{}")
}
@@ -1100,7 +1105,7 @@ pub mod tokens {
pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| {
SourceFile::parse(
- "const C: <()>::Item = (1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
+ "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p)\n;\n\n",
)
});
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
index 3308077da..691d0c618 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs
@@ -61,6 +61,14 @@ impl ast::BlockExpr {
pub fn tail_expr(&self) -> Option<ast::Expr> {
self.stmt_list()?.tail_expr()
}
+ /// Block expressions accept outer and inner attributes, but only when they are the outer
+ /// expression of an expression statement or the final expression of another block expression.
+ pub fn may_carry_attributes(&self) -> bool {
+ matches!(
+ self.syntax().parent().map(|it| it.kind()),
+ Some(SyntaxKind::BLOCK_EXPR | SyntaxKind::EXPR_STMT)
+ )
+ }
}
#[derive(Debug, PartialEq, Eq, Clone)]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 4ec388914..8e04ab8be 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -130,7 +130,8 @@ impl Expr {
//
ContinueExpr(_) => (0, 0),
- ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_) => (0, 1),
+ ClosureExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | BreakExpr(_)
+ | OffsetOfExpr(_) | FormatArgsExpr(_) | AsmExpr(_) => (0, 1),
RangeExpr(_) => (5, 5),
@@ -160,7 +161,7 @@ impl Expr {
CastExpr(_) => (25, 26),
- BoxExpr(_) | RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27),
+ RefExpr(_) | LetExpr(_) | PrefixExpr(_) => (0, 27),
AwaitExpr(_) | CallExpr(_) | MethodCallExpr(_) | IndexExpr(_) | TryExpr(_)
| MacroExpr(_) => (29, 0),
@@ -202,7 +203,6 @@ impl Expr {
let rhs = match self {
RefExpr(e) => e.expr(),
BinExpr(e) => e.rhs(),
- BoxExpr(e) => e.expr(),
BreakExpr(e) => e.expr(),
LetExpr(e) => e.expr(),
RangeExpr(e) => e.end(),
@@ -279,7 +279,6 @@ impl Expr {
CastExpr(e) => e.as_token(),
FieldExpr(e) => e.dot_token(),
AwaitExpr(e) => e.dot_token(),
- BoxExpr(e) => e.box_token(),
BreakExpr(e) => e.break_token(),
CallExpr(e) => e.arg_list().and_then(|args| args.l_paren_token()),
ClosureExpr(e) => e.param_list().and_then(|params| params.l_paren_token()),
@@ -293,7 +292,9 @@ impl Expr {
YieldExpr(e) => e.yield_token(),
YeetExpr(e) => e.do_token(),
LetExpr(e) => e.let_token(),
-
+ OffsetOfExpr(e) => e.builtin_token(),
+ FormatArgsExpr(e) => e.builtin_token(),
+ AsmExpr(e) => e.builtin_token(),
ArrayExpr(_) | TupleExpr(_) | Literal(_) | PathExpr(_) | ParenExpr(_)
| IfExpr(_) | WhileExpr(_) | ForExpr(_) | LoopExpr(_) | MatchExpr(_)
| BlockExpr(_) | RecordExpr(_) | UnderscoreExpr(_) | MacroExpr(_) => None,
@@ -310,12 +311,12 @@ impl Expr {
ArrayExpr(_) | AwaitExpr(_) | BlockExpr(_) | CallExpr(_) | CastExpr(_)
| ClosureExpr(_) | FieldExpr(_) | IndexExpr(_) | Literal(_) | LoopExpr(_)
| MacroExpr(_) | MethodCallExpr(_) | ParenExpr(_) | PathExpr(_) | RecordExpr(_)
- | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) => false,
+ | TryExpr(_) | TupleExpr(_) | UnderscoreExpr(_) | OffsetOfExpr(_)
+ | FormatArgsExpr(_) | AsmExpr(_) => false,
// For BinExpr and RangeExpr this is technically wrong -- the child can be on the left...
- BinExpr(_) | RangeExpr(_) | BoxExpr(_) | BreakExpr(_) | ContinueExpr(_)
- | PrefixExpr(_) | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_)
- | LetExpr(_) => self
+ BinExpr(_) | RangeExpr(_) | BreakExpr(_) | ContinueExpr(_) | PrefixExpr(_)
+ | RefExpr(_) | ReturnExpr(_) | YieldExpr(_) | YeetExpr(_) | LetExpr(_) => self
.syntax()
.parent()
.and_then(Expr::cast)
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 4cd668a0c..27c8a13e5 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -195,11 +195,16 @@ impl ast::TokenTree {
// Tag the token as joint if it is float with a fractional part
// we use this jointness to inform the parser about what token split
// event to emit when we encounter a float literal in a field access
- if kind == SyntaxKind::FLOAT_NUMBER && !t.text().ends_with('.') {
- parser_input.was_joint();
+ if kind == SyntaxKind::FLOAT_NUMBER {
+ if !t.text().ends_with('.') {
+ parser_input.was_joint();
+ } else {
+ was_joint = false;
+ }
+ } else {
+ was_joint = true;
}
}
- was_joint = true;
}
}
@@ -250,6 +255,7 @@ impl ast::TokenTree {
if has_pseudo_dot {
assert!(right.is_empty(), "{left}.{right}");
} else {
+ assert!(!right.is_empty(), "{left}.{right}");
builder.start_node(SyntaxKind::NAME_REF);
builder.token(SyntaxKind::INT_NUMBER, right);
builder.finish_node();
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
index e4db33f1c..341bda892 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/ast_src.rs
@@ -70,7 +70,19 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct",
"super", "trait", "true", "try", "type", "unsafe", "use", "where", "while", "yield",
],
- contextual_keywords: &["auto", "default", "existential", "union", "raw", "macro_rules", "yeet"],
+ contextual_keywords: &[
+ "auto",
+ "builtin",
+ "default",
+ "existential",
+ "union",
+ "raw",
+ "macro_rules",
+ "yeet",
+ "offset_of",
+ "asm",
+ "format_args",
+ ],
literals: &["INT_NUMBER", "FLOAT_NUMBER", "CHAR", "BYTE", "STRING", "BYTE_STRING", "C_STRING"],
tokens: &["ERROR", "IDENT", "WHITESPACE", "LIFETIME_IDENT", "COMMENT", "SHEBANG"],
nodes: &[
@@ -154,7 +166,10 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc {
"RECORD_EXPR",
"RECORD_EXPR_FIELD_LIST",
"RECORD_EXPR_FIELD",
- "BOX_EXPR",
+ "OFFSET_OF_EXPR",
+ "ASM_EXPR",
+ "FORMAT_ARGS_EXPR",
+ "FORMAT_ARGS_ARG",
// postfix
"CALL_EXPR",
"INDEX_EXPR",
diff --git a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
index c49c5fa10..56227fce9 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/tests/sourcegen_ast.rs
@@ -623,7 +623,7 @@ fn lower_enum(grammar: &Grammar, rule: &Rule) -> Option<Vec<String>> {
}
fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, rule: &Rule) {
- if lower_comma_list(acc, grammar, label, rule) {
+ if lower_seperated_list(acc, grammar, label, rule) {
return;
}
@@ -689,7 +689,7 @@ fn lower_rule(acc: &mut Vec<Field>, grammar: &Grammar, label: Option<&String>, r
}
// (T (',' T)* ','?)
-fn lower_comma_list(
+fn lower_seperated_list(
acc: &mut Vec<Field>,
grammar: &Grammar,
label: Option<&String>,
@@ -699,19 +699,23 @@ fn lower_comma_list(
Rule::Seq(it) => it,
_ => return false,
};
- let (node, repeat, trailing_comma) = match rule.as_slice() {
- [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_comma)] => {
- (node, repeat, trailing_comma)
+ let (node, repeat, trailing_sep) = match rule.as_slice() {
+ [Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
+ (node, repeat, Some(trailing_sep))
}
+ [Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
_ => return false,
};
let repeat = match &**repeat {
Rule::Seq(it) => it,
_ => return false,
};
- match repeat.as_slice() {
- [comma, Rule::Node(n)] if comma == &**trailing_comma && n == node => (),
- _ => return false,
+ if !matches!(
+ repeat.as_slice(),
+ [comma, Rule::Node(n)]
+ if trailing_sep.map_or(true, |it| comma == &**it) && n == node
+ ) {
+ return false;
}
let ty = grammar[*node].name.clone();
let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 75e7a3fec..3f8b5a089 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -313,7 +313,7 @@ impl FixtureWithProjectMeta {
}
impl MiniCore {
- const RAW_SOURCE: &str = include_str!("./minicore.rs");
+ const RAW_SOURCE: &'static str = include_str!("./minicore.rs");
fn has_flag(&self, flag: &str) -> bool {
self.activated_flags.iter().any(|it| it == flag)
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index c765f4244..573f56b00 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -899,32 +899,90 @@ pub mod fmt {
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
}
- extern "C" {
- type Opaque;
- }
+ mod rt {
- #[lang = "format_argument"]
- pub struct Argument<'a> {
- value: &'a Opaque,
- formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
- }
+ extern "C" {
+ type Opaque;
+ }
+
+ #[lang = "format_argument"]
+ pub struct Argument<'a> {
+ value: &'a Opaque,
+ formatter: fn(&Opaque, &mut Formatter<'_>) -> Result,
+ }
+
+ impl<'a> Argument<'a> {
+ pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
+ use crate::mem::transmute;
+ unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ }
+ }
+
+ #[lang = "format_alignment"]
+ pub enum Alignment {
+ Left,
+ Right,
+ Center,
+ Unknown,
+ }
+
+ #[lang = "format_count"]
+ pub enum Count {
+ Is(usize),
+ Param(usize),
+ Implied,
+ }
+
+ #[lang = "format_placeholder"]
+ pub struct Placeholder {
+ pub position: usize,
+ pub fill: char,
+ pub align: Alignment,
+ pub flags: u32,
+ pub precision: Count,
+ pub width: Count,
+ }
+
+ impl Placeholder {
+ pub const fn new(
+ position: usize,
+ fill: char,
+ align: Alignment,
+ flags: u32,
+ precision: Count,
+ width: Count,
+ ) -> Self;
+ }
+
+ #[lang = "format_unsafe_arg"]
+ pub struct UnsafeArg {
+ _private: (),
+ }
- impl<'a> Argument<'a> {
- pub fn new<'b, T>(x: &'b T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'b> {
- use crate::mem::transmute;
- unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
+ impl UnsafeArg {
+ pub unsafe fn new() -> Self;
}
}
#[lang = "format_arguments"]
pub struct Arguments<'a> {
pieces: &'a [&'static str],
- args: &'a [Argument<'a>],
+ fmt: Option<&'a [rt::Placeholder]>,
+ args: &'a [rt::Argument<'a>],
}
impl<'a> Arguments<'a> {
pub const fn new_v1(pieces: &'a [&'static str], args: &'a [Argument<'a>]) -> Arguments<'a> {
- Arguments { pieces, args }
+ Arguments { pieces, fmt: None, args }
+ }
+
+ pub fn new_v1_formatted(
+ pieces: &'a [&'static str],
+ args: &'a [rt::Argument<'a>],
+ fmt: &'a [rt::Placeholder],
+ _unsafe_arg: rt::UnsafeArg,
+ ) -> Arguments<'a> {
+ Arguments { pieces, fmt: Some(fmt), args }
}
}
@@ -1294,8 +1352,6 @@ mod macros {
/* compiler built-in */
};
}
-
- pub(crate) use panic;
// endregion:panic
// region:fmt
@@ -1306,7 +1362,20 @@ mod macros {
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
}
- pub(crate) use const_format_args;
+ #[macro_export]
+ #[rustc_builtin_macro]
+ macro_rules! format_args {
+ ($fmt:expr) => {{ /* compiler built-in */ }};
+ ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
+ }
+
+ #[macro_export]
+ macro_rules! print {
+ ($($arg:tt)*) => {{
+ $crate::io::_print($crate::format_args!($($arg)*));
+ }};
+ }
+
// endregion:fmt
// region:derive
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index 95c514251..11055f028 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ tracing = "0.1.35"
walkdir = "2.3.2"
crossbeam-channel = "0.5.5"
# We demand 5.1.0 as any higher version pulls in a new windows-sys dupe
-notify = "=5.1.0"
+notify = "6.1.1"
stdx.workspace = true
vfs.workspace = true
diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
index fe3dfe619..06004adad 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs
@@ -184,6 +184,11 @@ impl Vfs {
mem::take(&mut self.changes)
}
+ /// Provides a panic-less way to verify file_id validity.
+ pub fn exists(&self, file_id: FileId) -> bool {
+ self.get(file_id).is_some()
+ }
+
/// Returns the id associated with `path`
///
/// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a