summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:11:38 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:13:23 +0000
commit20431706a863f92cb37dc512fef6e48d192aaf2c (patch)
tree2867f13f5fd5437ba628c67d7f87309ccadcd286 /src/tools/rust-analyzer
parentReleasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff)
downloadrustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz
rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer')
-rw-r--r--src/tools/rust-analyzer/Cargo.lock205
-rw-r--r--src/tools/rust-analyzer/Cargo.toml1
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/fixture.rs8
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs22
-rw-r--r--src/tools/rust-analyzer/crates/cfg/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/flycheck/src/lib.rs103
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/adt.rs244
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/db.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs362
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs23
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/pretty.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs36
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs86
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin_fn_macro.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs25
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/Cargo.toml15
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/builder.rs217
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs75
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs108
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs43
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs27
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs64
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs44
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs114
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs78
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs102
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower.rs235
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs110
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs54
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs159
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/traits.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs101
-rw-r--r--src/tools/rust-analyzer/crates/hir/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/display.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/from_id.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs158
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs46
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs17
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs822
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs35
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs61
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs39
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs141
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs325
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_setter.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs296
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs159
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs91
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs88
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs150
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs14
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs247
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/config.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs1939
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml5
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs46
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string.rs6
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs267
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/node_ext.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs33
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/annotations.rs307
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs (renamed from src/tools/rust-analyzer/crates/ide/src/fn_references.rs)37
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs63
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/highlight_related.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs136
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs83
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/moniker.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/parent_module.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/references.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/status.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html6
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html12
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html8
-rw-r--r--src/tools/rust-analyzer/crates/mbe/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/benchmark.rs30
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander.rs3
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs91
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs57
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/mbe/src/parser.rs41
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs2
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs11
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs9
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs11
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs12
-rw-r--r--src/tools/rust-analyzer/crates/profile/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/Cargo.toml10
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs374
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs184
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs22
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/project_json.rs3
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/rustc_cfg.rs17
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs74
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs66
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs197
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml13
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/logger.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs84
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs31
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs28
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs110
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs211
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs53
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs33
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp_utils.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs188
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs143
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs19
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs1
-rw-r--r--src/tools/rust-analyzer/crates/sourcegen/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/crates/stdx/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/Cargo.toml8
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs61
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs23
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs30
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs53
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs94
-rw-r--r--src/tools/rust-analyzer/crates/text-edit/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/toolchain/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/docs/dev/README.md2
-rw-r--r--src/tools/rust-analyzer/docs/dev/guide.md9
-rw-r--r--src/tools/rust-analyzer/docs/dev/syntax.md6
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc74
-rw-r--r--src/tools/rust-analyzer/docs/user/manual.adoc17
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/Cargo.toml10
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/msg.rs10
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs7
-rw-r--r--src/tools/rust-analyzer/xtask/Cargo.toml4
-rw-r--r--src/tools/rust-analyzer/xtask/src/flags.rs19
-rw-r--r--src/tools/rust-analyzer/xtask/src/main.rs7
229 files changed, 8392 insertions, 3721 deletions
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 9f10d92c4..0ddea2f72 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -37,9 +37,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.62"
+version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
+checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
[[package]]
name = "anymap"
@@ -49,9 +49,9 @@ checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72"
[[package]]
name = "arbitrary"
-version = "1.1.3"
+version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a7924531f38b1970ff630f03eb20a2fde69db5c590c93b0f3482e95dcc5fd60"
+checksum = "d86fd10d912cab78764cc44307d9cd5f164e09abbeb87fb19fb6d95937e8da5f"
[[package]]
name = "arrayvec"
@@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chalk-derive"
-version = "0.84.0"
+version = "0.86.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf29c109d57f8d57b0e7675391be37a9285d86dd93278bd5f14a0ad3c447a6c2"
+checksum = "5499d415d855b5094366a824815341893ad3de0ecb6048c430118bdae6d27402"
dependencies = [
"proc-macro2",
"quote",
@@ -183,9 +183,9 @@ dependencies = [
[[package]]
name = "chalk-ir"
-version = "0.84.0"
+version = "0.86.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d391763027b5e50a5e15caf6d2857ec585fd68160367bbeac9e1804209620918"
+checksum = "3800118c76a48507b0eece3a01f3a429b5c478d203c493096e6040c67ab960e1"
dependencies = [
"bitflags",
"chalk-derive",
@@ -194,9 +194,9 @@ dependencies = [
[[package]]
name = "chalk-recursive"
-version = "0.84.0"
+version = "0.86.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afafd92dcdc7fe0ea940ee94bdd8cc5bd18f4a4a84c593d6d7025fe16c150478"
+checksum = "1baf60628fd73104d1f8562586a52d48f37f1e84435aab2e62674b1fd935b8c8"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -207,9 +207,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
-version = "0.84.0"
+version = "0.86.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3af1d111f11c91c48ace02e93e470c5bae6d2631bd112e4545317da53660d7fc"
+checksum = "0e9c3c068f9358786348e58a1b94ef0a5cf90a9810fc1f10fda896f0b5d80185"
dependencies = [
"chalk-derive",
"chalk-ir",
@@ -270,45 +270,44 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
-version = "0.9.10"
+version = "0.9.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
+checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
- "once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.11"
+version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
+checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
dependencies = [
"cfg-if",
- "once_cell",
]
[[package]]
name = "dashmap"
-version = "5.3.4"
+version = "5.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3495912c9c1ccf2e18976439f4443f3fee0fd61f424ff99fde6a66b15ecb448f"
+checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
dependencies = [
"cfg-if",
"hashbrown",
"lock_api",
+ "once_cell",
"parking_lot_core 0.9.3",
]
[[package]]
name = "derive_arbitrary"
-version = "1.1.3"
+version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9a577516173adb681466d517d39bd468293bc2c2a16439375ef0f35bba45f3d"
+checksum = "226ad66541d865d7a7173ad6a9e691c33fdb910ac723f4bc734b3e5294a1f931"
dependencies = [
"proc-macro2",
"quote",
@@ -394,6 +393,7 @@ dependencies = [
"crossbeam-channel",
"jod-thread",
"paths",
+ "rustc-hash",
"serde",
"serde_json",
"stdx",
@@ -403,11 +403,10 @@ dependencies = [
[[package]]
name = "form_urlencoded"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
+checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
dependencies = [
- "matches",
"percent-encoding",
]
@@ -545,6 +544,7 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"base-db",
+ "chalk-derive",
"chalk-ir",
"chalk-recursive",
"chalk-solve",
@@ -572,9 +572,9 @@ dependencies = [
[[package]]
name = "home"
-version = "0.5.3"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
+checksum = "747309b4b440c06d57b0b25f2aee03ee9b5e5397d288c60e21fc709bb98a7408"
dependencies = [
"winapi",
]
@@ -660,6 +660,7 @@ dependencies = [
"indexmap",
"itertools",
"limit",
+ "memchr",
"once_cell",
"parser",
"profile",
@@ -712,11 +713,10 @@ dependencies = [
[[package]]
name = "idna"
-version = "0.2.3"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
- "matches",
"unicode-bidi",
"unicode-normalization",
]
@@ -762,18 +762,18 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.10.3"
+version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "jod-thread"
@@ -813,9 +813,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.132"
+version = "0.2.135"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
+checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
[[package]]
name = "libloading"
@@ -829,9 +829,9 @@ dependencies = [
[[package]]
name = "libmimalloc-sys"
-version = "0.1.25"
+version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11ca136052550448f55df7898c6dbe651c6b574fe38a0d9ea687a9f8088a2e2c"
+checksum = "8fc093ab289b0bfda3aa1bdfab9c9542be29c7ef385cfcbe77f8c9813588eb48"
dependencies = [
"cc",
]
@@ -842,9 +842,9 @@ version = "0.0.0"
[[package]]
name = "lock_api"
-version = "0.4.7"
+version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
+checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
@@ -861,7 +861,7 @@ dependencies = [
[[package]]
name = "lsp-server"
-version = "0.6.0"
+version = "0.7.0"
dependencies = [
"crossbeam-channel",
"log",
@@ -893,12 +893,6 @@ dependencies = [
]
[[package]]
-name = "matches"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
-
-[[package]]
name = "mbe"
version = "0.0.0"
dependencies = [
@@ -939,18 +933,18 @@ dependencies = [
[[package]]
name = "mimalloc"
-version = "0.1.29"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f64ad83c969af2e732e907564deb0d0ed393cec4af80776f77dd77a1a427698"
+checksum = "76ce6a4b40d3bff9eb3ce9881ca0737a85072f9f975886082640cd46a75cdb35"
dependencies = [
"libmimalloc-sys",
]
[[package]]
name = "miniz_oxide"
-version = "0.5.3"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
+checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
dependencies = [
"adler",
]
@@ -978,9 +972,9 @@ dependencies = [
[[package]]
name = "notify"
-version = "5.0.0-pre.16"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "530f6314d6904508082f4ea424a0275cf62d341e118b313663f266429cb19693"
+checksum = "ed2c66da08abae1c024c01d635253e402341b4060a12e99b31c7594063bf490a"
dependencies = [
"bitflags",
"crossbeam-channel",
@@ -1015,9 +1009,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.13.1"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
[[package]]
name = "oorandom"
@@ -1086,9 +1080,9 @@ dependencies = [
[[package]]
name = "paste"
-version = "1.0.8"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
+checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
[[package]]
name = "paths"
@@ -1096,9 +1090,9 @@ version = "0.0.0"
[[package]]
name = "percent-encoding"
-version = "2.1.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]]
name = "perf-event"
@@ -1188,9 +1182,9 @@ version = "0.0.0"
[[package]]
name = "proc-macro2"
-version = "1.0.43"
+version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
dependencies = [
"unicode-ident",
]
@@ -1263,9 +1257,9 @@ dependencies = [
[[package]]
name = "pulldown-cmark-to-cmark"
-version = "10.0.2"
+version = "10.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1353ac408192fa925228d3e60ff746167d03f4f7e54835d78ef79e08225d913"
+checksum = "0194e6e1966c23cc5fd988714f85b18d548d773e81965413555d96569931833d"
dependencies = [
"pulldown-cmark",
]
@@ -1338,9 +1332,9 @@ checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "rowan"
-version = "0.15.8"
+version = "0.15.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e88acf7b001007e9e8c989fe7449f6601d909e5dd2c56399fc158977ad6c56e8"
+checksum = "5811547e7ba31e903fe48c8ceab10d40d70a101f3d15523c847cce91aa71f332"
dependencies = [
"countme",
"hashbrown",
@@ -1491,27 +1485,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
-version = "1.0.13"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
+checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
dependencies = [
"serde",
]
[[package]]
name = "serde"
-version = "1.0.143"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
+checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.143"
+version = "1.0.145"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
+checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
dependencies = [
"proc-macro2",
"quote",
@@ -1520,9 +1514,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.83"
+version = "1.0.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
+checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
dependencies = [
"indexmap",
"itoa",
@@ -1552,9 +1546,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "smol_str"
@@ -1591,9 +1585,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.99"
+version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
+checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
dependencies = [
"proc-macro2",
"quote",
@@ -1664,18 +1658,18 @@ checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
[[package]]
name = "thiserror"
-version = "1.0.31"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.31"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
dependencies = [
"proc-macro2",
"quote",
@@ -1713,9 +1707,9 @@ dependencies = [
[[package]]
name = "tikv-jemalloc-sys"
-version = "0.5.1+5.3.0-patched"
+version = "0.5.2+5.3.0-patched"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "931e876f91fed0827f863a2d153897790da0b24d882c721a79cb3beb0b903261"
+checksum = "ec45c14da997d0925c7835883e4d5c181f196fa142f8c19d7643d1e9af2592c3"
dependencies = [
"cc",
"fs_extra",
@@ -1756,9 +1750,9 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.36"
+version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
@@ -1768,9 +1762,9 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.22"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
+checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
dependencies = [
"proc-macro2",
"quote",
@@ -1779,9 +1773,9 @@ dependencies = [
[[package]]
name = "tracing-core"
-version = "0.1.29"
+version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7"
+checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
dependencies = [
"once_cell",
"valuable",
@@ -1800,9 +1794,9 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
-version = "0.3.15"
+version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b"
+checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70"
dependencies = [
"matchers",
"once_cell",
@@ -1864,40 +1858,39 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]]
name = "unicode-ident"
-version = "1.0.1"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-normalization"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
+checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "unicode-xid"
-version = "0.2.3"
+version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
-version = "2.2.2"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
+checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"
dependencies = [
"form_urlencoded",
"idna",
- "matches",
"percent-encoding",
"serde",
]
@@ -2080,18 +2073,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
[[package]]
name = "xflags"
-version = "0.2.4"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f14fe1ed41a5a2b5ef3f565586c4a8a559ee55d3953faab360a771135bdee00"
+checksum = "cbf19f5031a1a812e96fede16f8161218883079946cea87619d3613db1efd268"
dependencies = [
"xflags-macros",
]
[[package]]
name = "xflags-macros"
-version = "0.2.4"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022"
+checksum = "2afbd7f2039bb6cad2dd45f0c5dff49c0d4e26118398768b7a605524d4251809"
[[package]]
name = "xshell"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 6b68ca823..286ef1e7d 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -27,6 +27,7 @@ debug = 0
# chalk-solve = { path = "../chalk/chalk-solve" }
# chalk-ir = { path = "../chalk/chalk-ir" }
# chalk-recursive = { path = "../chalk/chalk-recursive" }
+# chalk-derive = { path = "../chalk/chalk-derive" }
# ungrammar = { path = "../ungrammar" }
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 8e6e6a11a..5b7828a26 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/fixture.rs
@@ -196,7 +196,7 @@ impl ChangeFixture {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
} else {
for (from, to, prelude) in crate_deps {
@@ -270,7 +270,7 @@ impl ChangeFixture {
Env::default(),
Ok(proc_macro),
true,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
for krate in all_crates {
@@ -398,7 +398,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
let (version, origin) = match b.split_once(':') {
Some(("CratesIo", data)) => match data.split_once(',') {
Some((version, url)) => {
- (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()) })
+ (version, CrateOrigin::CratesIo { repo: Some(url.to_owned()), name: None })
}
_ => panic!("Bad crates.io parameter: {}", data),
},
@@ -409,7 +409,7 @@ fn parse_crate(crate_str: String) -> (String, CrateOrigin, Option<String>) {
let crate_origin = match &*crate_str {
"std" => CrateOrigin::Lang(LangCrateOrigin::Std),
"core" => CrateOrigin::Lang(LangCrateOrigin::Core),
- _ => CrateOrigin::CratesIo { repo: None },
+ _ => CrateOrigin::CratesIo { repo: None, name: None },
};
(crate_str, crate_origin, None)
}
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index b388e47de..e7f0c4ec2 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -136,7 +136,7 @@ impl ops::Deref for CrateName {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CrateOrigin {
/// Crates that are from crates.io official registry,
- CratesIo { repo: Option<String> },
+ CratesIo { repo: Option<String>, name: Option<String> },
/// Crates that are provided by the language, like std, core, proc-macro, ...
Lang(LangCrateOrigin),
}
@@ -648,7 +648,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@@ -660,7 +660,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate3 = graph.add_crate_root(
FileId(3u32),
@@ -672,7 +672,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -698,7 +698,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@@ -710,7 +710,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -733,7 +733,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@@ -745,7 +745,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate3 = graph.add_crate_root(
FileId(3u32),
@@ -757,7 +757,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
assert!(graph
.add_dep(crate1, Dependency::new(CrateName::new("crate2").unwrap(), crate2))
@@ -780,7 +780,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
let crate2 = graph.add_crate_root(
FileId(2u32),
@@ -792,7 +792,7 @@ mod tests {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
assert!(graph
.add_dep(
diff --git a/src/tools/rust-analyzer/crates/cfg/Cargo.toml b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
index c9664a83a..ee1ad677a 100644
--- a/src/tools/rust-analyzer/crates/cfg/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/cfg/Cargo.toml
@@ -22,5 +22,5 @@ oorandom = "11.1.3"
# We depend on both individually instead of using `features = ["derive"]` to microoptimize the
# build graph: if the feature was enabled, syn would be built early on in the graph if `smolstr`
# supports `arbitrary`. This way, we avoid feature unification.
-arbitrary = "1.1.0"
-derive_arbitrary = "1.1.0"
+arbitrary = "1.1.7"
+derive_arbitrary = "1.1.6"
diff --git a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
index d3d180ece..2ad32d248 100644
--- a/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/flycheck/Cargo.toml
@@ -11,10 +11,11 @@ doctest = false
[dependencies]
crossbeam-channel = "0.5.5"
-tracing = "0.1.35"
+tracing = "0.1.37"
cargo_metadata = "0.15.0"
+rustc-hash = "1.1.0"
serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.81"
+serde_json = "1.0.86"
jod-thread = "0.1.2"
toolchain = { path = "../toolchain", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
index d9f4ef5b7..73c3a48b4 100644
--- a/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/flycheck/src/lib.rs
@@ -12,6 +12,7 @@ use std::{
use crossbeam_channel::{never, select, unbounded, Receiver, Sender};
use paths::AbsPathBuf;
+use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::{process::streaming_output, JodChild};
@@ -20,6 +21,20 @@ pub use cargo_metadata::diagnostic::{
DiagnosticSpanMacroExpansion,
};
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum InvocationStrategy {
+ Once,
+ #[default]
+ PerWorkspace,
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub enum InvocationLocation {
+ Root(AbsPathBuf),
+ #[default]
+ Workspace,
+}
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FlycheckConfig {
CargoCommand {
@@ -30,10 +45,14 @@ pub enum FlycheckConfig {
all_features: bool,
features: Vec<String>,
extra_args: Vec<String>,
+ extra_env: FxHashMap<String, String>,
},
CustomCommand {
command: String,
args: Vec<String>,
+ extra_env: FxHashMap<String, String>,
+ invocation_strategy: InvocationStrategy,
+ invocation_location: InvocationLocation,
},
}
@@ -41,7 +60,7 @@ impl fmt::Display for FlycheckConfig {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {}", command),
- FlycheckConfig::CustomCommand { command, args } => {
+ FlycheckConfig::CustomCommand { command, args, .. } => {
write!(f, "{} {}", command, args.join(" "))
}
}
@@ -133,11 +152,15 @@ enum Restart {
No,
}
+/// A [`FlycheckActor`] is a single check instance of a workspace.
struct FlycheckActor {
+ /// The workspace id of this flycheck instance.
id: usize,
sender: Box<dyn Fn(Message) + Send>,
config: FlycheckConfig,
- workspace_root: AbsPathBuf,
+ /// Either the workspace root of the workspace we are flychecking,
+ /// or the project root of the project.
+ root: AbsPathBuf,
/// CargoHandle exists to wrap around the communication needed to be able to
/// run `cargo check` without blocking. Currently the Rust standard library
/// doesn't provide a way to read sub-process output without blocking, so we
@@ -159,20 +182,27 @@ impl FlycheckActor {
workspace_root: AbsPathBuf,
) -> FlycheckActor {
tracing::info!(%id, ?workspace_root, "Spawning flycheck");
- FlycheckActor { id, sender, config, workspace_root, cargo_handle: None }
+ FlycheckActor { id, sender, config, root: workspace_root, cargo_handle: None }
}
- fn progress(&self, progress: Progress) {
+
+ fn report_progress(&self, progress: Progress) {
self.send(Message::Progress { id: self.id, progress });
}
+
fn next_event(&self, inbox: &Receiver<Restart>) -> Option<Event> {
let check_chan = self.cargo_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::Restart(msg));
+ }
select! {
recv(inbox) -> msg => msg.ok().map(Event::Restart),
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
}
}
+
fn run(mut self, inbox: Receiver<Restart>) {
- while let Some(event) = self.next_event(&inbox) {
+ 'event: while let Some(event) = self.next_event(&inbox) {
match event {
Event::Restart(Restart::No) => {
self.cancel_check_process();
@@ -180,7 +210,12 @@ impl FlycheckActor {
Event::Restart(Restart::Yes) => {
// Cancel the previously spawned process
self.cancel_check_process();
- while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {}
+ while let Ok(restart) = inbox.recv_timeout(Duration::from_millis(50)) {
+ // restart chained with a stop, so just cancel
+ if let Restart::No = restart {
+ continue 'event;
+ }
+ }
let command = self.check_command();
tracing::debug!(?command, "will restart flycheck");
@@ -191,10 +226,10 @@ impl FlycheckActor {
"did restart flycheck"
);
self.cargo_handle = Some(cargo_handle);
- self.progress(Progress::DidStart);
+ self.report_progress(Progress::DidStart);
}
Err(error) => {
- self.progress(Progress::DidFailToRestart(format!(
+ self.report_progress(Progress::DidFailToRestart(format!(
"Failed to run the following command: {:?} error={}",
self.check_command(),
error
@@ -214,17 +249,17 @@ impl FlycheckActor {
self.check_command()
);
}
- self.progress(Progress::DidFinish(res));
+ self.report_progress(Progress::DidFinish(res));
}
Event::CheckEvent(Some(message)) => match message {
CargoMessage::CompilerArtifact(msg) => {
- self.progress(Progress::DidCheckCrate(msg.target.name));
+ self.report_progress(Progress::DidCheckCrate(msg.target.name));
}
CargoMessage::Diagnostic(msg) => {
self.send(Message::AddDiagnostic {
id: self.id,
- workspace_root: self.workspace_root.clone(),
+ workspace_root: self.root.clone(),
diagnostic: msg,
});
}
@@ -242,12 +277,12 @@ impl FlycheckActor {
"did cancel flycheck"
);
cargo_handle.cancel();
- self.progress(Progress::DidCancel);
+ self.report_progress(Progress::DidCancel);
}
}
fn check_command(&self) -> Command {
- let mut cmd = match &self.config {
+ let (mut cmd, args) = match &self.config {
FlycheckConfig::CargoCommand {
command,
target_triple,
@@ -256,12 +291,11 @@ impl FlycheckActor {
all_features,
extra_args,
features,
+ extra_env,
} => {
let mut cmd = Command::new(toolchain::cargo());
cmd.arg(command);
- cmd.current_dir(&self.workspace_root);
- cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
- .arg(self.workspace_root.join("Cargo.toml").as_os_str());
+ cmd.args(&["--workspace", "--message-format=json"]);
if let Some(target) = target_triple {
cmd.args(&["--target", target.as_str()]);
@@ -280,16 +314,41 @@ impl FlycheckActor {
cmd.arg(features.join(" "));
}
}
- cmd.args(extra_args);
- cmd
+ cmd.envs(extra_env);
+ (cmd, extra_args)
}
- FlycheckConfig::CustomCommand { command, args } => {
+ FlycheckConfig::CustomCommand {
+ command,
+ args,
+ extra_env,
+ invocation_strategy,
+ invocation_location,
+ } => {
let mut cmd = Command::new(command);
- cmd.args(args);
- cmd
+ cmd.envs(extra_env);
+
+ match invocation_location {
+ InvocationLocation::Workspace => {
+ match invocation_strategy {
+ InvocationStrategy::Once => {
+ cmd.current_dir(&self.root);
+ }
+ InvocationStrategy::PerWorkspace => {
+ // FIXME: cmd.current_dir(&affected_workspace);
+ cmd.current_dir(&self.root);
+ }
+ }
+ }
+ InvocationLocation::Root(root) => {
+ cmd.current_dir(root);
+ }
+ }
+
+ (cmd, args)
}
};
- cmd.current_dir(&self.workspace_root);
+
+ cmd.args(args);
cmd
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
index e8cff2f3e..4ad8e7597 100644
--- a/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-def/Cargo.toml
@@ -15,17 +15,17 @@ arrayvec = "0.7.2"
bitflags = "1.3.2"
cov-mark = "2.0.0-pre.1"
# We need to freeze the version of the crate, as the raw-api feature is considered unstable
-dashmap = { version = "=5.3.4", features = ["raw-api"] }
+dashmap = { version = "=5.4.0", features = ["raw-api"] }
drop_bomb = "0.1.5"
either = "1.7.0"
fst = { version = "0.4.7", default-features = false }
hashbrown = { version = "0.12.1", default-features = false }
indexmap = "1.9.1"
-itertools = "0.10.3"
+itertools = "0.10.5"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
-once_cell = "1.12.0"
+once_cell = "1.15.0"
rustc-hash = "1.1.0"
-smallvec = "1.9.0"
+smallvec = "1.10.0"
tracing = "0.1.35"
stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
index 277135d6d..938db032f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/adt.rs
@@ -1,12 +1,12 @@
//! Defines hir-level representation of structs, enums and unions
-use std::sync::Arc;
+use std::{num::NonZeroU32, sync::Arc};
use base_db::CrateId;
use either::Either;
use hir_expand::{
name::{AsName, Name},
- InFile,
+ HirFileId, InFile,
};
use la_arena::{Arena, ArenaMap};
use syntax::ast::{self, HasName, HasVisibility};
@@ -14,15 +14,18 @@ use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree};
use crate::{
body::{CfgExpander, LowerCtx},
+ builtin_type::{BuiltinInt, BuiltinUint},
db::DefDatabase,
intern::Interned,
- item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem, RawVisibilityId},
+ item_tree::{AttrOwner, Field, FieldAstId, Fields, ItemTree, ModItem, RawVisibilityId},
+ nameres::diagnostics::DefDiagnostic,
src::HasChildSource,
src::HasSource,
trace::Trace,
type_ref::TypeRef,
visibility::RawVisibility,
- EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
+ EnumId, LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StructId, UnionId,
+ VariantId,
};
use cfg::CfgOptions;
@@ -31,7 +34,7 @@ use cfg::CfgOptions;
pub struct StructData {
pub name: Name,
pub variant_data: Arc<VariantData>,
- pub repr: Option<ReprKind>,
+ pub repr: Option<ReprData>,
pub visibility: RawVisibility,
}
@@ -39,6 +42,7 @@ pub struct StructData {
pub struct EnumData {
pub name: Name,
pub variants: Arena<EnumVariantData>,
+ pub repr: Option<ReprData>,
pub visibility: RawVisibility,
}
@@ -63,10 +67,19 @@ pub struct FieldData {
pub visibility: RawVisibility,
}
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum ReprKind {
- Packed,
- Other,
+ C,
+ BuiltinInt { builtin: Either<BuiltinInt, BuiltinUint>, is_c: bool },
+ Transparent,
+ Default,
+}
+
+#[derive(Copy, Debug, Clone, PartialEq, Eq)]
+pub struct ReprData {
+ pub kind: ReprKind,
+ pub packed: bool,
+ pub align: Option<NonZeroU32>,
}
fn repr_from_value(
@@ -74,25 +87,71 @@ fn repr_from_value(
krate: CrateId,
item_tree: &ItemTree,
of: AttrOwner,
-) -> Option<ReprKind> {
+) -> Option<ReprData> {
item_tree.attrs(db, krate, of).by_key("repr").tt_values().find_map(parse_repr_tt)
}
-fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
+fn parse_repr_tt(tt: &Subtree) -> Option<ReprData> {
match tt.delimiter {
Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {}
_ => return None,
}
- let mut it = tt.token_trees.iter();
- match it.next()? {
- TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed),
- _ => Some(ReprKind::Other),
+ let mut data = ReprData { kind: ReprKind::Default, packed: false, align: None };
+
+ let mut tts = tt.token_trees.iter().peekable();
+ while let Some(tt) = tts.next() {
+ if let TokenTree::Leaf(Leaf::Ident(ident)) = tt {
+ match &*ident.text {
+ "packed" => {
+ data.packed = true;
+ if let Some(TokenTree::Subtree(_)) = tts.peek() {
+ tts.next();
+ }
+ }
+ "align" => {
+ if let Some(TokenTree::Subtree(tt)) = tts.peek() {
+ tts.next();
+ if let Some(TokenTree::Leaf(Leaf::Literal(lit))) = tt.token_trees.first() {
+ if let Ok(align) = lit.text.parse() {
+ data.align = Some(align);
+ }
+ }
+ }
+ }
+ "C" => {
+ if let ReprKind::BuiltinInt { is_c, .. } = &mut data.kind {
+ *is_c = true;
+ } else {
+ data.kind = ReprKind::C;
+ }
+ }
+ "transparent" => data.kind = ReprKind::Transparent,
+ repr => {
+ let is_c = matches!(data.kind, ReprKind::C);
+ if let Some(builtin) = BuiltinInt::from_suffix(repr)
+ .map(Either::Left)
+ .or_else(|| BuiltinUint::from_suffix(repr).map(Either::Right))
+ {
+ data.kind = ReprKind::BuiltinInt { builtin, is_c };
+ }
+ }
+ }
+ }
}
+
+ Some(data)
}
impl StructData {
pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
+ db.struct_data_with_diagnostics(id).0
+ }
+
+ pub(crate) fn struct_data_with_diagnostics_query(
+ db: &dyn DefDatabase,
+ id: StructId,
+ ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
let loc = id.lookup(db);
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
@@ -100,15 +159,35 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let strukt = &item_tree[loc.id.value];
- let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
- Arc::new(StructData {
- name: strukt.name.clone(),
- variant_data: Arc::new(variant_data),
- repr,
- visibility: item_tree[strukt.visibility].clone(),
- })
+ let (variant_data, diagnostics) = lower_fields(
+ db,
+ krate,
+ loc.id.file_id(),
+ loc.container.local_id,
+ &item_tree,
+ &cfg_options,
+ &strukt.fields,
+ None,
+ );
+ (
+ Arc::new(StructData {
+ name: strukt.name.clone(),
+ variant_data: Arc::new(variant_data),
+ repr,
+ visibility: item_tree[strukt.visibility].clone(),
+ }),
+ diagnostics.into(),
+ )
}
+
pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
+ db.union_data_with_diagnostics(id).0
+ }
+
+ pub(crate) fn union_data_with_diagnostics_query(
+ db: &dyn DefDatabase,
+ id: UnionId,
+ ) -> (Arc<StructData>, Arc<[DefDiagnostic]>) {
let loc = id.lookup(db);
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
@@ -116,56 +195,98 @@ impl StructData {
let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
let union = &item_tree[loc.id.value];
- let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
-
- Arc::new(StructData {
- name: union.name.clone(),
- variant_data: Arc::new(variant_data),
- repr,
- visibility: item_tree[union.visibility].clone(),
- })
+ let (variant_data, diagnostics) = lower_fields(
+ db,
+ krate,
+ loc.id.file_id(),
+ loc.container.local_id,
+ &item_tree,
+ &cfg_options,
+ &union.fields,
+ None,
+ );
+ (
+ Arc::new(StructData {
+ name: union.name.clone(),
+ variant_data: Arc::new(variant_data),
+ repr,
+ visibility: item_tree[union.visibility].clone(),
+ }),
+ diagnostics.into(),
+ )
}
}
impl EnumData {
pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
+ db.enum_data_with_diagnostics(e).0
+ }
+
+ pub(crate) fn enum_data_with_diagnostics_query(
+ db: &dyn DefDatabase,
+ e: EnumId,
+ ) -> (Arc<EnumData>, Arc<[DefDiagnostic]>) {
let loc = e.lookup(db);
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
let cfg_options = db.crate_graph()[krate].cfg_options.clone();
+ let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
let enum_ = &item_tree[loc.id.value];
let mut variants = Arena::new();
+ let mut diagnostics = Vec::new();
for tree_id in enum_.variants.clone() {
- if item_tree.attrs(db, krate, tree_id.into()).is_cfg_enabled(&cfg_options) {
- let var = &item_tree[tree_id];
- let var_data = lower_fields(
+ let attrs = item_tree.attrs(db, krate, tree_id.into());
+ let var = &item_tree[tree_id];
+ if attrs.is_cfg_enabled(&cfg_options) {
+ let (var_data, field_diagnostics) = lower_fields(
db,
krate,
+ loc.id.file_id(),
+ loc.container.local_id,
&item_tree,
&cfg_options,
&var.fields,
Some(enum_.visibility),
);
+ diagnostics.extend(field_diagnostics);
variants.alloc(EnumVariantData {
name: var.name.clone(),
variant_data: Arc::new(var_data),
});
+ } else {
+ diagnostics.push(DefDiagnostic::unconfigured_code(
+ loc.container.local_id,
+ InFile::new(loc.id.file_id(), var.ast_id.upcast()),
+ attrs.cfg().unwrap(),
+ cfg_options.clone(),
+ ))
}
}
- Arc::new(EnumData {
- name: enum_.name.clone(),
- variants,
- visibility: item_tree[enum_.visibility].clone(),
- })
+ (
+ Arc::new(EnumData {
+ name: enum_.name.clone(),
+ variants,
+ repr,
+ visibility: item_tree[enum_.visibility].clone(),
+ }),
+ diagnostics.into(),
+ )
}
pub fn variant(&self, name: &Name) -> Option<LocalEnumVariantId> {
let (id, _) = self.variants.iter().find(|(_id, data)| &data.name == name)?;
Some(id)
}
+
+ pub fn variant_body_type(&self) -> Either<BuiltinInt, BuiltinUint> {
+ match self.repr {
+ Some(ReprData { kind: ReprKind::BuiltinInt { builtin, .. }, .. }) => builtin,
+ _ => Either::Left(BuiltinInt::Isize),
+ }
+ }
}
impl HasChildSource<LocalEnumVariantId> for EnumId {
@@ -324,31 +445,64 @@ fn lower_struct(
fn lower_fields(
db: &dyn DefDatabase,
krate: CrateId,
+ current_file_id: HirFileId,
+ container: LocalModuleId,
item_tree: &ItemTree,
cfg_options: &CfgOptions,
fields: &Fields,
override_visibility: Option<RawVisibilityId>,
-) -> VariantData {
+) -> (VariantData, Vec<DefDiagnostic>) {
+ let mut diagnostics = Vec::new();
match fields {
Fields::Record(flds) => {
let mut arena = Arena::new();
for field_id in flds.clone() {
- if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
- arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
+ let attrs = item_tree.attrs(db, krate, field_id.into());
+ let field = &item_tree[field_id];
+ if attrs.is_cfg_enabled(cfg_options) {
+ arena.alloc(lower_field(item_tree, field, override_visibility));
+ } else {
+ diagnostics.push(DefDiagnostic::unconfigured_code(
+ container,
+ InFile::new(
+ current_file_id,
+ match field.ast_id {
+ FieldAstId::Record(it) => it.upcast(),
+ FieldAstId::Tuple(it) => it.upcast(),
+ },
+ ),
+ attrs.cfg().unwrap(),
+ cfg_options.clone(),
+ ))
}
}
- VariantData::Record(arena)
+ (VariantData::Record(arena), diagnostics)
}
Fields::Tuple(flds) => {
let mut arena = Arena::new();
for field_id in flds.clone() {
- if item_tree.attrs(db, krate, field_id.into()).is_cfg_enabled(cfg_options) {
- arena.alloc(lower_field(item_tree, &item_tree[field_id], override_visibility));
+ let attrs = item_tree.attrs(db, krate, field_id.into());
+ let field = &item_tree[field_id];
+ if attrs.is_cfg_enabled(cfg_options) {
+ arena.alloc(lower_field(item_tree, field, override_visibility));
+ } else {
+ diagnostics.push(DefDiagnostic::unconfigured_code(
+ container,
+ InFile::new(
+ current_file_id,
+ match field.ast_id {
+ FieldAstId::Record(it) => it.upcast(),
+ FieldAstId::Tuple(it) => it.upcast(),
+ },
+ ),
+ attrs.cfg().unwrap(),
+ cfg_options.clone(),
+ ))
}
}
- VariantData::Tuple(arena)
+ (VariantData::Tuple(arena), diagnostics)
}
- Fields::Unit => VariantData::Unit,
+ Fields::Unit => (VariantData::Unit, diagnostics),
}
}
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 22f5fb992..759f3b8c0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs
@@ -27,7 +27,7 @@ use crate::{
macro_id_to_def_id,
nameres::DefMap,
path::{ModPath, Path},
- src::HasSource,
+ src::{HasChildSource, HasSource},
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
UnresolvedMacro,
};
@@ -311,7 +311,20 @@ impl Body {
DefWithBodyId::FunctionId(f) => {
let f = f.lookup(db);
let src = f.source(db);
- params = src.value.param_list();
+ params = src.value.param_list().map(|param_list| {
+ let item_tree = f.id.item_tree(db);
+ let func = &item_tree[f.id.value];
+ let krate = f.container.module(db).krate;
+ let crate_graph = db.crate_graph();
+ (
+ param_list,
+ func.params.clone().map(move |param| {
+ item_tree
+ .attrs(db, krate, param.into())
+ .is_cfg_enabled(&crate_graph[krate].cfg_options)
+ }),
+ )
+ });
(src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
}
DefWithBodyId::ConstId(c) => {
@@ -324,10 +337,17 @@ impl Body {
let src = s.source(db);
(src.file_id, s.module(db), src.value.body())
}
+ DefWithBodyId::VariantId(v) => {
+ let e = v.parent.lookup(db);
+ let src = v.parent.child_source(db);
+ let variant = &src.value[v.local_id];
+ (src.file_id, e.container, variant.expr())
+ }
};
let expander = Expander::new(db, file_id, module);
let (mut body, source_map) = Body::new(db, expander, params, body);
body.shrink_to_fit();
+
(Arc::new(body), Arc::new(source_map))
}
@@ -364,7 +384,7 @@ impl Body {
fn new(
db: &dyn DefDatabase,
expander: Expander,
- params: Option<ast::ParamList>,
+ params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
) -> (Body, BodySourceMap) {
lower::lower(db, expander, params, body)
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 3b3297f78..ccc01c3ef 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
@@ -29,8 +29,9 @@ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase,
expr::{
- dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId,
- Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+ dummy_expr_id, Array, BindingAnnotation, ClosureKind, Expr, ExprId, FloatTypeWrapper,
+ Label, LabelId, Literal, MatchArm, Movability, Pat, PatId, RecordFieldPat, RecordLitField,
+ Statement,
},
intern::Interned,
item_scope::BuiltinShadowMode,
@@ -76,7 +77,7 @@ impl<'a> LowerCtx<'a> {
pub(super) fn lower(
db: &dyn DefDatabase,
expander: Expander,
- params: Option<ast::ParamList>,
+ params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
) -> (Body, BodySourceMap) {
ExprCollector {
@@ -97,6 +98,7 @@ pub(super) fn lower(
name_to_pat_grouping: Default::default(),
is_lowering_inside_or_pat: false,
is_lowering_assignee_expr: false,
+ is_lowering_generator: false,
}
.collect(params, body)
}
@@ -111,16 +113,19 @@ struct ExprCollector<'a> {
name_to_pat_grouping: FxHashMap<Name, Vec<PatId>>,
is_lowering_inside_or_pat: bool,
is_lowering_assignee_expr: bool,
+ is_lowering_generator: bool,
}
impl ExprCollector<'_> {
fn collect(
mut self,
- param_list: Option<ast::ParamList>,
+ param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
body: Option<ast::Expr>,
) -> (Body, BodySourceMap) {
- if let Some(param_list) = param_list {
- if let Some(self_param) = param_list.self_param() {
+ if let Some((param_list, mut attr_enabled)) = param_list {
+ if let Some(self_param) =
+ param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+ {
let ptr = AstPtr::new(&self_param);
let param_pat = self.alloc_pat(
Pat::Bind {
@@ -136,7 +141,11 @@ impl ExprCollector<'_> {
self.body.params.push(param_pat);
}
- for pat in param_list.params().filter_map(|param| param.pat()) {
+ for pat in param_list
+ .params()
+ .zip(attr_enabled)
+ .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+ {
let param_pat = self.collect_pat(pat);
self.body.params.push(param_pat);
}
@@ -358,6 +367,7 @@ impl ExprCollector<'_> {
self.alloc_expr(Expr::Return { expr }, syntax_ptr)
}
ast::Expr::YieldExpr(e) => {
+ self.is_lowering_generator = true;
let expr = e.expr().map(|e| self.collect_expr(e));
self.alloc_expr(Expr::Yield { expr }, syntax_ptr)
}
@@ -459,13 +469,31 @@ impl ExprCollector<'_> {
.ret_type()
.and_then(|r| r.ty())
.map(|it| Interned::new(TypeRef::from_ast(&self.ctx(), it)));
+
+ let prev_is_lowering_generator = self.is_lowering_generator;
+ self.is_lowering_generator = false;
+
let body = self.collect_expr_opt(e.body());
+
+ let closure_kind = if self.is_lowering_generator {
+ let movability = if e.static_token().is_some() {
+ Movability::Static
+ } else {
+ Movability::Movable
+ };
+ ClosureKind::Generator(movability)
+ } else {
+ ClosureKind::Closure
+ };
+ self.is_lowering_generator = prev_is_lowering_generator;
+
self.alloc_expr(
Expr::Closure {
args: args.into(),
arg_types: arg_types.into(),
ret_type,
body,
+ closure_kind,
},
syntax_ptr,
)
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 f2fed9544..162d173d5 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,8 +2,10 @@
use std::fmt::{self, Write};
+use syntax::ast::HasName;
+
use crate::{
- expr::{Array, BindingAnnotation, Literal, Statement},
+ expr::{Array, BindingAnnotation, ClosureKind, Literal, Movability, Statement},
pretty::{print_generic_args, print_path, print_type_ref},
type_ref::TypeRef,
};
@@ -32,6 +34,16 @@ pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBo
};
format!("const {} = ", name)
}
+ DefWithBodyId::VariantId(it) => {
+ needs_semi = false;
+ let src = it.parent.child_source(db);
+ let variant = &src.value[it.local_id];
+ let name = match &variant.name() {
+ Some(name) => name.to_string(),
+ None => "_".to_string(),
+ };
+ format!("{}", name)
+ }
};
let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false };
@@ -350,7 +362,10 @@ impl<'a> Printer<'a> {
self.print_expr(*index);
w!(self, "]");
}
- Expr::Closure { args, arg_types, ret_type, body } => {
+ Expr::Closure { args, arg_types, ret_type, body, closure_kind } => {
+ if let ClosureKind::Generator(Movability::Static) = closure_kind {
+ w!(self, "static ");
+ }
w!(self, "|");
for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() {
if i != 0 {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
index 0e7ce5f85..39581b33a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_attr.rs
@@ -379,7 +379,7 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
// ==========================================================================
rustc_attr!(rustc_allocator, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
- rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
+ rustc_attr!(rustc_nounwind, Normal, template!(Word), WarnFollowing, IMPL_DETAIL),
gated!(
alloc_error_handler, Normal, template!(Word), WarnFollowing,
experimental!(alloc_error_handler)
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
index 5b1435e8f..bb1316525 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/child_by_source.rs
@@ -198,6 +198,10 @@ impl ChildBySource for EnumId {
impl ChildBySource for DefWithBodyId {
fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap, file_id: HirFileId) {
let body = db.body(*self);
+ if let &DefWithBodyId::VariantId(v) = self {
+ VariantId::EnumVariantId(v).child_by_source_to(db, res, file_id)
+ }
+
for (_, def_map) in body.blocks(db) {
// All block expressions are merged into the same map, because they logically all add
// inner items to the containing `DefWithBodyId`.
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 631ae3cf1..2dc69b00a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs
@@ -219,7 +219,7 @@ impl TraitData {
pub(crate) fn trait_data_with_diagnostics_query(
db: &dyn DefDatabase,
tr: TraitId,
- ) -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>) {
+ ) -> (Arc<TraitData>, Arc<[DefDiagnostic]>) {
let tr_loc @ ItemLoc { container: module_id, id: tree_id } = tr.lookup(db);
let item_tree = tree_id.item_tree(db);
let tr_def = &item_tree[tree_id.value];
@@ -251,7 +251,7 @@ impl TraitData {
visibility,
skip_array_during_method_dispatch,
}),
- Arc::new(diagnostics),
+ diagnostics.into(),
)
}
@@ -299,7 +299,7 @@ impl ImplData {
pub(crate) fn impl_data_with_diagnostics_query(
db: &dyn DefDatabase,
id: ImplId,
- ) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>) {
+ ) -> (Arc<ImplData>, Arc<[DefDiagnostic]>) {
let _p = profile::span("impl_data_with_diagnostics_query");
let ItemLoc { container: module_id, id: tree_id } = id.lookup(db);
@@ -318,7 +318,7 @@ impl ImplData {
(
Arc::new(ImplData { target_trait, self_ty, items, is_negative, attribute_calls }),
- Arc::new(diagnostics),
+ diagnostics.into(),
)
}
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 40b2f734b..431c82554 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs
@@ -97,24 +97,33 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
#[salsa::invoke(StructData::struct_data_query)]
fn struct_data(&self, id: StructId) -> Arc<StructData>;
+ #[salsa::invoke(StructData::struct_data_with_diagnostics_query)]
+ fn struct_data_with_diagnostics(&self, id: StructId)
+ -> (Arc<StructData>, Arc<[DefDiagnostic]>);
+
#[salsa::invoke(StructData::union_data_query)]
fn union_data(&self, id: UnionId) -> Arc<StructData>;
+ #[salsa::invoke(StructData::union_data_with_diagnostics_query)]
+ fn union_data_with_diagnostics(&self, id: UnionId) -> (Arc<StructData>, Arc<[DefDiagnostic]>);
+
#[salsa::invoke(EnumData::enum_data_query)]
fn enum_data(&self, e: EnumId) -> Arc<EnumData>;
+ #[salsa::invoke(EnumData::enum_data_with_diagnostics_query)]
+ fn enum_data_with_diagnostics(&self, e: EnumId) -> (Arc<EnumData>, Arc<[DefDiagnostic]>);
+
#[salsa::invoke(ImplData::impl_data_query)]
fn impl_data(&self, e: ImplId) -> Arc<ImplData>;
#[salsa::invoke(ImplData::impl_data_with_diagnostics_query)]
- fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, Arc<Vec<DefDiagnostic>>);
+ fn impl_data_with_diagnostics(&self, e: ImplId) -> (Arc<ImplData>, Arc<[DefDiagnostic]>);
#[salsa::invoke(TraitData::trait_data_query)]
fn trait_data(&self, e: TraitId) -> Arc<TraitData>;
#[salsa::invoke(TraitData::trait_data_with_diagnostics_query)]
- fn trait_data_with_diagnostics(&self, tr: TraitId)
- -> (Arc<TraitData>, Arc<Vec<DefDiagnostic>>);
+ fn trait_data_with_diagnostics(&self, tr: TraitId) -> (Arc<TraitData>, Arc<[DefDiagnostic]>);
#[salsa::invoke(TypeAliasData::type_alias_data_query)]
fn type_alias_data(&self, e: TypeAliasId) -> Arc<TypeAliasData>;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
index 419d3feec..162646550 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr.rs
@@ -198,6 +198,7 @@ pub enum Expr {
arg_types: Box<[Option<Interned<TypeRef>>]>,
ret_type: Option<Interned<TypeRef>>,
body: ExprId,
+ closure_kind: ClosureKind,
},
Tuple {
exprs: Box<[ExprId]>,
@@ -211,6 +212,18 @@ pub enum Expr {
Underscore,
}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum ClosureKind {
+ Closure,
+ Generator(Movability),
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Movability {
+ Static,
+ Movable,
+}
+
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Array {
ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool },
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 89e961f84..c70e6fdcc 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
@@ -1,6 +1,6 @@
//! An algorithm to find a path to refer to a certain item.
-use std::iter;
+use std::{cmp::Ordering, iter};
use hir_expand::name::{known, AsName, Name};
use rustc_hash::FxHashSet;
@@ -16,9 +16,14 @@ use crate::{
/// Find a path that can be used to refer to a certain item. This can depend on
/// *from where* you're referring to the item, hence the `from` parameter.
-pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
+pub fn find_path(
+ db: &dyn DefDatabase,
+ item: ItemInNs,
+ from: ModuleId,
+ prefer_no_std: bool,
+) -> Option<ModPath> {
let _p = profile::span("find_path");
- find_path_inner(db, item, from, None)
+ find_path_inner(db, item, from, None, prefer_no_std)
}
pub fn find_path_prefixed(
@@ -26,47 +31,14 @@ pub fn find_path_prefixed(
item: ItemInNs,
from: ModuleId,
prefix_kind: PrefixKind,
+ prefer_no_std: bool,
) -> Option<ModPath> {
let _p = profile::span("find_path_prefixed");
- find_path_inner(db, item, from, Some(prefix_kind))
+ find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
}
const MAX_PATH_LEN: usize = 15;
-trait ModPathExt {
- fn starts_with_std(&self) -> bool;
- fn can_start_with_std(&self) -> bool;
-}
-
-impl ModPathExt for ModPath {
- fn starts_with_std(&self) -> bool {
- self.segments().first() == Some(&known::std)
- }
-
- // Can we replace the first segment with `std::` and still get a valid, identical path?
- fn can_start_with_std(&self) -> bool {
- let first_segment = self.segments().first();
- first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
- }
-}
-
-fn check_self_super(def_map: &DefMap, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
- if item == ItemInNs::Types(from.into()) {
- // - if the item is the module we're in, use `self`
- Some(ModPath::from_segments(PathKind::Super(0), None))
- } else if let Some(parent_id) = def_map[from.local_id].parent {
- // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
- let parent_id = def_map.module_id(parent_id);
- if item == ItemInNs::Types(ModuleDefId::ModuleId(parent_id)) {
- Some(ModPath::from_segments(PathKind::Super(1), None))
- } else {
- None
- }
- } else {
- None
- }
-}
-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrefixKind {
/// Causes paths to always start with either `self`, `super`, `crate` or a crate-name.
@@ -94,135 +66,247 @@ impl PrefixKind {
self == &PrefixKind::ByCrate
}
}
+
/// Attempts to find a path to refer to the given `item` visible from the `from` ModuleId
fn find_path_inner(
db: &dyn DefDatabase,
item: ItemInNs,
from: ModuleId,
prefixed: Option<PrefixKind>,
+ prefer_no_std: bool,
) -> Option<ModPath> {
- // FIXME: Do fast path for std/core libs?
+ // - if the item is a builtin, it's in scope
+ if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
+ return Some(ModPath::from_segments(PathKind::Plain, Some(builtin.as_name())));
+ }
- let mut visited_modules = FxHashSet::default();
let def_map = from.def_map(db);
- find_path_inner_(db, &def_map, from, item, MAX_PATH_LEN, prefixed, &mut visited_modules)
+ let crate_root = def_map.crate_root(db);
+ // - 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();
+ return find_path_for_module(
+ db,
+ &def_map,
+ &mut visited_modules,
+ crate_root,
+ from,
+ module_id,
+ MAX_PATH_LEN,
+ prefixed,
+ prefer_no_std || db.crate_supports_no_std(crate_root.krate),
+ );
+ }
+
+ // - if the item is already in scope, return the name under which it is
+ let scope_name = find_in_scope(db, &def_map, from, item);
+ if prefixed.is_none() {
+ if let Some(scope_name) = scope_name {
+ return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
+ }
+ }
+
+ // - if the item is in the prelude, return the name from there
+ if let Some(value) = find_in_prelude(db, &crate_root.def_map(db), item, from) {
+ return value;
+ }
+
+ if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
+ // - if the item is an enum variant, refer to it via the enum
+ if let Some(mut path) = find_path_inner(
+ db,
+ ItemInNs::Types(variant.parent.into()),
+ from,
+ prefixed,
+ prefer_no_std,
+ ) {
+ let data = db.enum_data(variant.parent);
+ path.push_segment(data.variants[variant.local_id].name.clone());
+ return Some(path);
+ }
+ // If this doesn't work, it seems we have no way of referring to the
+ // enum; that's very weird, but there might still be a reexport of the
+ // variant somewhere
+ }
+
+ let mut visited_modules = FxHashSet::default();
+
+ calculate_best_path(
+ db,
+ &def_map,
+ &mut visited_modules,
+ crate_root,
+ MAX_PATH_LEN,
+ item,
+ from,
+ prefixed,
+ prefer_no_std || db.crate_supports_no_std(crate_root.krate),
+ scope_name,
+ )
}
-fn find_path_inner_(
+fn find_path_for_module(
db: &dyn DefDatabase,
def_map: &DefMap,
+ visited_modules: &mut FxHashSet<ModuleId>,
+ crate_root: ModuleId,
from: ModuleId,
- item: ItemInNs,
+ module_id: ModuleId,
max_len: usize,
- mut prefixed: Option<PrefixKind>,
- visited_modules: &mut FxHashSet<ModuleId>,
+ prefixed: Option<PrefixKind>,
+ prefer_no_std: bool,
) -> Option<ModPath> {
if max_len == 0 {
return None;
}
// Base cases:
-
// - if the item is already in scope, return the name under which it is
- let scope_name = def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
- def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
- });
+ 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)));
}
}
- // - if the item is a builtin, it's in scope
- if let ItemInNs::Types(ModuleDefId::BuiltinType(builtin)) = item {
- return Some(ModPath::from_segments(PathKind::Plain, Some(builtin.as_name())));
- }
-
// - if the item is the crate root, return `crate`
- let crate_root = def_map.crate_root(db);
- if item == ItemInNs::Types(ModuleDefId::ModuleId(crate_root)) {
+ if module_id == crate_root {
return Some(ModPath::from_segments(PathKind::Crate, None));
}
+ // - 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(_) = check_self_super(&def_map, item, from) {
+ if let modpath @ Some(_) = find_self_super(&def_map, module_id, from) {
return modpath;
}
}
// - 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);
- if let ItemInNs::Types(ModuleDefId::ModuleId(item)) = item {
- for (name, &def_id) in root_def_map.extern_prelude() {
- if item == def_id {
- let name = scope_name.unwrap_or_else(|| name.clone());
-
- let name_already_occupied_in_type_ns = def_map
- .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
- def_map[local_id]
- .scope
- .type_(&name)
- .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
- })
- .is_some();
- let kind = if name_already_occupied_in_type_ns {
- cov_mark::hit!(ambiguous_crate_start);
- PathKind::Abs
- } else {
- PathKind::Plain
- };
- return Some(ModPath::from_segments(kind, Some(name)));
- }
+ for (name, &def_id) in root_def_map.extern_prelude() {
+ if module_id == def_id {
+ let name = scope_name.unwrap_or_else(|| name.clone());
+
+ let name_already_occupied_in_type_ns = def_map
+ .with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
+ def_map[local_id]
+ .scope
+ .type_(&name)
+ .filter(|&(id, _)| id != ModuleDefId::ModuleId(def_id))
+ })
+ .is_some();
+ let kind = if name_already_occupied_in_type_ns {
+ cov_mark::hit!(ambiguous_crate_start);
+ PathKind::Abs
+ } else {
+ PathKind::Plain
+ };
+ return Some(ModPath::from_segments(kind, Some(name)));
}
}
- // - if the item is in the prelude, return the name from there
+ if let Some(value) = find_in_prelude(db, &root_def_map, ItemInNs::Types(module_id.into()), from)
+ {
+ return value;
+ }
+ calculate_best_path(
+ db,
+ def_map,
+ visited_modules,
+ crate_root,
+ max_len,
+ ItemInNs::Types(module_id.into()),
+ from,
+ prefixed,
+ prefer_no_std,
+ scope_name,
+ )
+}
+
+fn find_in_scope(
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ from: ModuleId,
+ item: ItemInNs,
+) -> Option<Name> {
+ def_map.with_ancestor_maps(db, from.local_id, &mut |def_map, local_id| {
+ def_map[local_id].scope.name_of(item).map(|(name, _)| name.clone())
+ })
+}
+
+fn find_in_prelude(
+ db: &dyn DefDatabase,
+ root_def_map: &DefMap,
+ item: ItemInNs,
+ from: ModuleId,
+) -> Option<Option<ModPath>> {
if let Some(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;
if let Some((name, vis)) = prelude_scope.name_of(item) {
if vis.is_visible_from(db, from) {
- return Some(ModPath::from_segments(PathKind::Plain, Some(name.clone())));
+ return Some(Some(ModPath::from_segments(PathKind::Plain, Some(name.clone()))));
}
}
}
+ None
+}
- // Recursive case:
- // - if the item is an enum variant, refer to it via the enum
- if let Some(ModuleDefId::EnumVariantId(variant)) = item.as_module_def_id() {
- if let Some(mut path) = find_path(db, ItemInNs::Types(variant.parent.into()), from) {
- let data = db.enum_data(variant.parent);
- path.push_segment(data.variants[variant.local_id].name.clone());
- return Some(path);
+fn find_self_super(def_map: &DefMap, item: ModuleId, from: ModuleId) -> Option<ModPath> {
+ if item == from {
+ // - if the item is the module we're in, use `self`
+ Some(ModPath::from_segments(PathKind::Super(0), None))
+ } else if let Some(parent_id) = def_map[from.local_id].parent {
+ // - if the item is the parent module, use `super` (this is not used recursively, since `super::super` is ugly)
+ let parent_id = def_map.module_id(parent_id);
+ if item == parent_id {
+ Some(ModPath::from_segments(PathKind::Super(1), None))
+ } else {
+ None
}
- // If this doesn't work, it seems we have no way of referring to the
- // enum; that's very weird, but there might still be a reexport of the
- // variant somewhere
+ } else {
+ None
}
+}
- // - otherwise, look for modules containing (reexporting) it and import it from one of those
- let prefer_no_std = db.crate_supports_no_std(crate_root.krate);
+fn calculate_best_path(
+ db: &dyn DefDatabase,
+ def_map: &DefMap,
+ visited_modules: &mut FxHashSet<ModuleId>,
+ crate_root: ModuleId,
+ max_len: usize,
+ item: ItemInNs,
+ from: ModuleId,
+ mut prefixed: Option<PrefixKind>,
+ prefer_no_std: bool,
+ scope_name: Option<Name>,
+) -> Option<ModPath> {
+ if max_len <= 1 {
+ return None;
+ }
let mut best_path = None;
- let mut best_path_len = max_len;
-
+ // Recursive case:
+ // - otherwise, look for modules containing (reexporting) it and import it from one of those
if item.krate(db) == Some(from.krate) {
+ let mut best_path_len = max_len;
// Item was defined in the same crate that wants to import it. It cannot be found in any
// dependency in this case.
- // FIXME: this should have a fast path that doesn't look through the prelude again?
for (module_id, name) in find_local_import_locations(db, item, from) {
if !visited_modules.insert(module_id) {
cov_mark::hit!(recursive_imports);
continue;
}
- if let Some(mut path) = find_path_inner_(
+ if let Some(mut path) = find_path_for_module(
db,
def_map,
+ visited_modules,
+ crate_root,
from,
- ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
+ module_id,
best_path_len - 1,
prefixed,
- visited_modules,
+ prefer_no_std,
) {
path.push_segment(name);
@@ -245,14 +329,16 @@ fn find_path_inner_(
import_map.import_info_for(item).and_then(|info| {
// 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_inner_(
+ let mut path = find_path_for_module(
db,
def_map,
+ visited_modules,
+ crate_root,
from,
- ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
- best_path_len - 1,
+ info.container,
+ max_len - 1,
prefixed,
- visited_modules,
+ prefer_no_std,
)?;
cov_mark::hit!(partially_imported);
path.push_segment(info.path.segments.last()?.clone());
@@ -268,16 +354,12 @@ fn find_path_inner_(
best_path = Some(new_path);
}
}
-
- // If the item is declared inside a block expression, don't use a prefix, as we don't handle
- // that correctly (FIXME).
- if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) {
- if item_module.def_map(db).block_id().is_some() && prefixed.is_some() {
+ if let Some(module) = item.module(db) {
+ if module.def_map(db).block_id().is_some() && prefixed.is_some() {
cov_mark::hit!(prefixed_in_block_expression);
prefixed = Some(PrefixKind::Plain);
}
}
-
match prefixed.map(PrefixKind::prefix) {
Some(prefix) => best_path.or_else(|| {
scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
@@ -287,29 +369,48 @@ fn find_path_inner_(
}
fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
- if old_path.starts_with_std() && new_path.can_start_with_std() {
- if prefer_no_std {
- cov_mark::hit!(prefer_no_std_paths);
- new_path
- } else {
- cov_mark::hit!(prefer_std_paths);
- old_path
+ const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
+ match (old_path.segments().first(), new_path.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 {
+ name if name == &known::core => 0,
+ name if name == &known::alloc => 0,
+ name if name == &known::std => 1,
+ _ => unreachable!(),
+ },
+ true => |name: &Name| match name {
+ name if name == &known::core => 2,
+ name if name == &known::alloc => 1,
+ name if name == &known::std => 0,
+ _ => unreachable!(),
+ },
+ };
+ let nrank = rank(new);
+ let orank = rank(old);
+ match nrank.cmp(&orank) {
+ Ordering::Less => old_path,
+ Ordering::Equal => {
+ if new_path.len() < old_path.len() {
+ new_path
+ } else {
+ old_path
+ }
+ }
+ Ordering::Greater => new_path,
+ }
}
- } else if new_path.starts_with_std() && old_path.can_start_with_std() {
- if prefer_no_std {
- cov_mark::hit!(prefer_no_std_paths);
- old_path
- } else {
- cov_mark::hit!(prefer_std_paths);
- new_path
+ _ => {
+ if new_path.len() < old_path.len() {
+ new_path
+ } else {
+ old_path
+ }
}
- } else if new_path.len() < old_path.len() {
- new_path
- } else {
- old_path
}
}
+// FIXME: Remove allocations
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
fn find_local_import_locations(
db: &dyn DefDatabase,
@@ -428,7 +529,8 @@ mod tests {
.take_types()
.unwrap();
- let found_path = find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind);
+ let found_path =
+ find_path_inner(&db, ItemInNs::Types(resolved), module, prefix_kind, false);
assert_eq!(found_path, Some(mod_path), "{:?}", prefix_kind);
}
@@ -468,8 +570,8 @@ $0
"#,
"E::A",
"E::A",
- "E::A",
- "E::A",
+ "crate::E::A",
+ "self::E::A",
);
}
@@ -788,7 +890,6 @@ pub use super::foo;
#[test]
fn prefer_std_paths_over_alloc() {
- cov_mark::check!(prefer_std_paths);
check_found_path(
r#"
//- /main.rs crate:main deps:alloc,std
@@ -813,7 +914,6 @@ pub mod sync {
#[test]
fn prefer_core_paths_over_std() {
- cov_mark::check!(prefer_no_std_paths);
check_found_path(
r#"
//- /main.rs crate:main deps:core,std
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 a11a92204..7721221c4 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
@@ -18,7 +18,7 @@ use crate::{
ConstId, HasModule, ImplId, LocalModuleId, MacroId, ModuleDefId, ModuleId, TraitId,
};
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
pub(crate) enum ImportType {
Glob,
Named,
@@ -302,13 +302,13 @@ impl ItemScope {
$changed = true;
}
Entry::Occupied(mut entry)
- if $glob_imports.$field.contains(&$lookup)
- && matches!($def_import_type, ImportType::Named) =>
+ if matches!($def_import_type, ImportType::Named) =>
{
- cov_mark::hit!(import_shadowed);
- $glob_imports.$field.remove(&$lookup);
- entry.insert(fld);
- $changed = true;
+ if $glob_imports.$field.remove(&$lookup) {
+ cov_mark::hit!(import_shadowed);
+ entry.insert(fld);
+ $changed = true;
+ }
}
_ => {}
}
@@ -457,8 +457,15 @@ impl ItemInNs {
/// Returns the crate defining this item (or `None` if `self` is built-in).
pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
match self {
- ItemInNs::Types(did) | ItemInNs::Values(did) => did.module(db).map(|m| m.krate),
+ ItemInNs::Types(id) | ItemInNs::Values(id) => id.module(db).map(|m| m.krate),
ItemInNs::Macros(id) => Some(id.module(db).krate),
}
}
+
+ pub fn module(&self, db: &dyn DefDatabase) -> Option<ModuleId> {
+ match self {
+ ItemInNs::Types(id) | ItemInNs::Values(id) => id.module(db),
+ ItemInNs::Macros(id) => Some(id.module(db)),
+ }
+ }
}
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 3342d4db4..570344596 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
@@ -943,6 +943,7 @@ impl AssocItem {
pub struct Variant {
pub name: Name,
pub fields: Fields,
+ pub ast_id: FileAstId<ast::Variant>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -952,10 +953,17 @@ pub enum Fields {
Unit,
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum FieldAstId {
+ Record(FileAstId<ast::RecordField>),
+ Tuple(FileAstId<ast::TupleField>),
+}
+
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Field {
pub name: Name,
pub type_ref: Interned<TypeRef>,
pub visibility: RawVisibilityId,
+ pub ast_id: FieldAstId,
}
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 7f2551e94..077a1b619 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
@@ -184,7 +184,8 @@ impl<'a> Ctx<'a> {
let name = field.name()?.as_name();
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref_opt(field.ty());
- let res = Field { name, type_ref, visibility };
+ let ast_id = FieldAstId::Record(self.source_ast_id_map.ast_id(field));
+ let res = Field { name, type_ref, visibility, ast_id };
Some(res)
}
@@ -203,7 +204,8 @@ impl<'a> Ctx<'a> {
let name = Name::new_tuple_field(idx);
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref_opt(field.ty());
- Field { name, type_ref, visibility }
+ let ast_id = FieldAstId::Tuple(self.source_ast_id_map.ast_id(field));
+ Field { name, type_ref, visibility, ast_id }
}
fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> {
@@ -247,7 +249,8 @@ impl<'a> Ctx<'a> {
fn lower_variant(&mut self, variant: &ast::Variant) -> Option<Variant> {
let name = variant.name()?.as_name();
let fields = self.lower_fields(&variant.kind());
- let res = Variant { name, fields };
+ let ast_id = self.source_ast_id_map.ast_id(variant);
+ let res = Variant { name, fields, ast_id };
Some(res)
}
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 34dd817fd..da1643152 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
@@ -115,7 +115,7 @@ impl<'a> Printer<'a> {
w!(self, "{{");
self.indented(|this| {
for field in fields.clone() {
- let Field { visibility, name, type_ref } = &this.tree[field];
+ let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
w!(this, "{}: ", name);
@@ -129,7 +129,7 @@ impl<'a> Printer<'a> {
w!(self, "(");
self.indented(|this| {
for field in fields.clone() {
- let Field { visibility, name, type_ref } = &this.tree[field];
+ let Field { visibility, name, type_ref, ast_id: _ } = &this.tree[field];
this.print_attrs_of(field);
this.print_visibility(*visibility);
w!(this, "{}: ", name);
@@ -323,7 +323,7 @@ impl<'a> Printer<'a> {
self.print_where_clause_and_opening_brace(generic_params);
self.indented(|this| {
for variant in variants.clone() {
- let Variant { name, fields } = &this.tree[variant];
+ let Variant { name, fields, ast_id: _ } = &this.tree[variant];
this.print_attrs_of(variant);
w!(this, "{}", name);
this.print_fields(fields);
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 32ebfda4f..5c7aa7234 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -474,16 +474,24 @@ pub enum DefWithBodyId {
FunctionId(FunctionId),
StaticId(StaticId),
ConstId(ConstId),
+ VariantId(EnumVariantId),
}
impl_from!(FunctionId, ConstId, StaticId for DefWithBodyId);
+impl From<EnumVariantId> for DefWithBodyId {
+ fn from(id: EnumVariantId) -> Self {
+ DefWithBodyId::VariantId(id)
+ }
+}
+
impl DefWithBodyId {
pub fn as_generic_def_id(self) -> Option<GenericDefId> {
match self {
DefWithBodyId::FunctionId(f) => Some(f.into()),
DefWithBodyId::StaticId(_) => None,
DefWithBodyId::ConstId(c) => Some(c.into()),
+ DefWithBodyId::VariantId(c) => Some(c.into()),
}
}
}
@@ -681,6 +689,7 @@ impl HasModule for DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).module(db),
DefWithBodyId::StaticId(it) => it.lookup(db).module(db),
DefWithBodyId::ConstId(it) => it.lookup(db).module(db),
+ DefWithBodyId::VariantId(it) => it.parent.lookup(db).container,
}
}
}
@@ -691,6 +700,7 @@ impl DefWithBodyId {
DefWithBodyId::FunctionId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::StaticId(it) => it.lookup(db).id.value.into(),
DefWithBodyId::ConstId(it) => it.lookup(db).id.value.into(),
+ DefWithBodyId::VariantId(it) => it.parent.lookup(db).id.value.into(),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index 6819e9114..fafcde25a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -12,11 +12,11 @@ fn test_copy_expand_simple() {
#[derive(Copy)]
struct Foo;
"#,
- expect![[r##"
+ expect![[r#"
#[derive(Copy)]
struct Foo;
-impl < > core::marker::Copy for Foo< > {}"##]],
+impl < > core::marker::Copy for Foo< > {}"#]],
);
}
@@ -33,7 +33,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro derive {}
#[rustc_builtin_macro]
@@ -41,7 +41,7 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
-impl < > crate ::marker::Copy for Foo< > {}"##]],
+impl < > crate ::marker::Copy for Foo< > {}"#]],
);
}
@@ -53,11 +53,11 @@ fn test_copy_expand_with_type_params() {
#[derive(Copy)]
struct Foo<A, B>;
"#,
- expect![[r##"
+ expect![[r#"
#[derive(Copy)]
struct Foo<A, B>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
+impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
);
}
@@ -70,11 +70,11 @@ fn test_copy_expand_with_lifetimes() {
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
"#,
- expect![[r##"
+ expect![[r#"
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
-impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
+impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
);
}
@@ -86,10 +86,26 @@ fn test_clone_expand() {
#[derive(Clone)]
struct Foo<A, B>;
"#,
- expect![[r##"
+ expect![[r#"
#[derive(Clone)]
struct Foo<A, B>;
-impl <T0: core::clone::Clone, T1: core::clone::Clone> core::clone::Clone for Foo<T0, T1> {}"##]],
+impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
+ );
+}
+
+#[test]
+fn test_clone_expand_with_const_generics() {
+ check(
+ r#"
+//- minicore: derive, clone
+#[derive(Clone)]
+struct Foo<const X: usize, T>(u32);
+"#,
+ expect![[r#"
+#[derive(Clone)]
+struct Foo<const X: usize, T>(u32);
+
+impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
);
}
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 4f626105a..c04cd1651 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
@@ -93,12 +93,12 @@ macro_rules! option_env {() => {}}
fn main() { option_env!("TEST_ENV_VAR"); }
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! option_env {() => {}}
-fn main() { std::option::Option::None:: < &str>; }
-"##]],
+fn main() { $crate::option::Option::None:: < &str>; }
+"#]],
);
}
@@ -191,7 +191,7 @@ fn main() {
format_args!("{} {:?}", arg1(a, b, c), arg2);
}
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -199,9 +199,9 @@ macro_rules! format_args {
}
fn main() {
- std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a, b, c)), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(arg2), std::fmt::Display::fmt), ]);
+ $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(arg1(a, b, c)), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(arg2), $crate::fmt::Display::fmt), ]);
}
-"##]],
+"#]],
);
}
@@ -219,7 +219,7 @@ fn main() {
format_args!("{} {:?}", a::<A,B>(), b);
}
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -227,9 +227,9 @@ macro_rules! format_args {
}
fn main() {
- std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A, B>()), std::fmt::Display::fmt), std::fmt::ArgumentV1::new(&(b), std::fmt::Display::fmt), ]);
+ $crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a::<A, B>()), $crate::fmt::Display::fmt), $crate::fmt::ArgumentV1::new(&(b), $crate::fmt::Display::fmt), ]);
}
-"##]],
+"#]],
);
}
@@ -248,7 +248,7 @@ fn main() {
format_args!/*+errors*/("{} {:?}", a.);
}
"#,
- expect![[r##"
+ expect![[r#"
#[rustc_builtin_macro]
macro_rules! format_args {
($fmt:expr) => ({ /* compiler built-in */ });
@@ -258,9 +258,9 @@ macro_rules! format_args {
fn main() {
let _ =
/* parse error: expected field name or number */
-std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a.), std::fmt::Display::fmt), ]);
+$crate::fmt::Arguments::new_v1(&[], &[$crate::fmt::ArgumentV1::new(&(a.), $crate::fmt::Display::fmt), ]);
}
-"##]],
+"#]],
);
}
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 495bbe457..9ffc21881 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
@@ -534,6 +534,7 @@ impl DefCollector<'_> {
match per_ns.types {
Some((ModuleDefId::ModuleId(m), _)) => {
self.def_map.prelude = Some(m);
+ break;
}
types => {
tracing::debug!(
@@ -2121,7 +2122,7 @@ impl ModCollector<'_, '_> {
fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
let ast_id = item.ast_id(self.item_tree);
- let ast_id = InFile::new(self.file_id(), ast_id);
+ let ast_id = InFile::new(self.file_id(), ast_id.upcast());
self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
self.module_id,
ast_id,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
index ed7e920fd..066142291 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/diagnostics.rs
@@ -4,7 +4,7 @@ use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
use hir_expand::MacroCallKind;
use la_arena::Idx;
-use syntax::ast;
+use syntax::ast::{self, AnyHasAttrs};
use crate::{
attr::AttrId,
@@ -22,7 +22,7 @@ pub enum DefDiagnosticKind {
UnresolvedImport { id: ItemTreeId<item_tree::Import>, index: Idx<ast::UseTree> },
- UnconfiguredCode { ast: AstId<ast::Item>, cfg: CfgExpr, opts: CfgOptions },
+ UnconfiguredCode { ast: AstId<AnyHasAttrs>, cfg: CfgExpr, opts: CfgOptions },
UnresolvedProcMacro { ast: MacroCallKind, krate: CrateId },
@@ -75,7 +75,7 @@ impl DefDiagnostic {
pub fn unconfigured_code(
container: LocalModuleId,
- ast: AstId<ast::Item>,
+ ast: AstId<ast::AnyHasAttrs>,
cfg: CfgExpr,
opts: CfgOptions,
) -> Self {
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 8aa5973ca..070f68371 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -839,6 +839,7 @@ impl HasResolver for DefWithBodyId {
DefWithBodyId::ConstId(c) => c.resolver(db),
DefWithBodyId::FunctionId(f) => f.resolver(db),
DefWithBodyId::StaticId(s) => s.resolver(db),
+ DefWithBodyId::VariantId(v) => v.parent.resolver(db),
}
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
index dfd470ffc..3359c99b3 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-expand/Cargo.toml
@@ -15,11 +15,11 @@ tracing = "0.1.35"
either = "1.7.0"
rustc-hash = "1.1.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
-itertools = "0.10.3"
+itertools = "0.10.5"
hashbrown = { version = "0.12.1", features = [
"inline-more",
], default-features = false }
-smallvec = { version = "1.9.0", features = ["const_new"] }
+smallvec = { version = "1.10.0", features = ["const_new"] }
stdx = { path = "../stdx", version = "0.0.0" }
base-db = { path = "../base-db", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
index 11c0a6764..2b27db0e9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/ast_id_map.rs
@@ -93,7 +93,12 @@ impl AstIdMap {
// trait does not change ids of top-level items, which helps caching.
bdfs(node, |it| {
let kind = it.kind();
- if ast::Item::can_cast(kind) || ast::BlockExpr::can_cast(kind) {
+ if ast::Item::can_cast(kind)
+ || ast::BlockExpr::can_cast(kind)
+ || ast::Variant::can_cast(kind)
+ || ast::RecordField::can_cast(kind)
+ || ast::TupleField::can_cast(kind)
+ {
res.alloc(&it);
true
} else {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
index 79989bc2e..8966047c9 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin_derive_macro.rs
@@ -60,7 +60,8 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>
struct BasicAdtInfo {
name: tt::Ident,
- type_or_const_params: usize,
+ /// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
+ param_types: Vec<Option<tt::Subtree>>,
}
fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
@@ -92,50 +93,22 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
- let type_or_const_params =
- params.map_or(0, |type_param_list| type_param_list.type_or_const_params().count());
- Ok(BasicAdtInfo { name: name_token, type_or_const_params })
-}
-
-fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
- let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: '<',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
- })
- .into(),
- );
- for i in 0..n {
- if i > 0 {
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: ',',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
- })
- .into(),
- );
- }
- result.push(
- tt::Leaf::Ident(tt::Ident {
- id: tt::TokenId::unspecified(),
- text: format!("T{}", i).into(),
- })
- .into(),
- );
- result.extend(bound.iter().cloned());
- }
- result.push(
- tt::Leaf::Punct(tt::Punct {
- char: '>',
- spacing: tt::Spacing::Alone,
- id: tt::TokenId::unspecified(),
+ let param_types = params
+ .into_iter()
+ .flat_map(|param_list| param_list.type_or_const_params())
+ .map(|param| {
+ if let ast::TypeOrConstParam::Const(param) = param {
+ let ty = param
+ .ty()
+ .map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
+ .unwrap_or_default();
+ Some(ty)
+ } else {
+ None
+ }
})
- .into(),
- );
- result
+ .collect();
+ Ok(BasicAdtInfo { name: name_token, param_types })
}
fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
@@ -143,14 +116,27 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
Ok(info) => info,
Err(e) => return ExpandResult::only_err(e),
};
+ let (params, args): (Vec<_>, Vec<_>) = info
+ .param_types
+ .into_iter()
+ .enumerate()
+ .map(|(idx, param_ty)| {
+ let ident = tt::Leaf::Ident(tt::Ident {
+ id: tt::TokenId::unspecified(),
+ text: format!("T{idx}").into(),
+ });
+ let ident_ = ident.clone();
+ if let Some(ty) = param_ty {
+ (quote! { const #ident : #ty , }, quote! { #ident_ , })
+ } else {
+ let bound = trait_path.clone();
+ (quote! { #ident : #bound , }, quote! { #ident_ , })
+ }
+ })
+ .unzip();
let name = info.name;
- let trait_path_clone = trait_path.token_trees.clone();
- let bound = (quote! { : ##trait_path_clone }).token_trees;
- let type_params = make_type_args(info.type_or_const_params, bound);
- let type_args = make_type_args(info.type_or_const_params, Vec::new());
- let trait_path = trait_path.token_trees;
let expanded = quote! {
- impl ##type_params ##trait_path for #name ##type_args {}
+ impl < ##params > #trait_path for #name < ##args > {}
};
ExpandResult::ok(expanded)
}
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 8befa7f7d..7b19518e2 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
@@ -238,9 +238,9 @@ fn format_args_expand(
) -> ExpandResult<tt::Subtree> {
// We expand `format_args!("", a1, a2)` to
// ```
- // std::fmt::Arguments::new_v1(&[], &[
- // std::fmt::ArgumentV1::new(&arg1,std::fmt::Display::fmt),
- // std::fmt::ArgumentV1::new(&arg2,std::fmt::Display::fmt),
+ // $crate::fmt::Arguments::new_v1(&[], &[
+ // $crate::fmt::ArgumentV1::new(&arg1,$crate::fmt::Display::fmt),
+ // $crate::fmt::ArgumentV1::new(&arg2,$crate::fmt::Display::fmt),
// ])
// ```,
// which is still not really correct, but close enough for now
@@ -262,10 +262,10 @@ fn format_args_expand(
}
let _format_string = args.remove(0);
let arg_tts = args.into_iter().flat_map(|arg| {
- quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
+ quote! { #DOLLAR_CRATE::fmt::ArgumentV1::new(&(#arg), #DOLLAR_CRATE::fmt::Display::fmt), }
}.token_trees);
let expanded = quote! {
- std::fmt::Arguments::new_v1(&[], &[##arg_tts])
+ #DOLLAR_CRATE::fmt::Arguments::new_v1(&[], &[##arg_tts])
};
ExpandResult::ok(expanded)
}
@@ -675,8 +675,8 @@ fn option_env_expand(
};
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! { std::option::Option::None::<&str> },
- Some(s) => quote! { std::option::Some(#s) },
+ None => quote! { #DOLLAR_CRATE::option::Option::None::<&str> },
+ Some(s) => quote! { #DOLLAR_CRATE::option::Some(#s) },
};
ExpandResult::ok(ExpandedEager::new(expanded))
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index bc97ee15c..87e4db039 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -221,8 +221,16 @@ pub fn expand_speculative(
fixup::reverse_fixups(&mut speculative_expansion.value, &spec_args_tmap, &fixups.undo_info);
let (node, rev_tmap) = token_tree_to_syntax_node(&speculative_expansion.value, expand_to);
- let range = rev_tmap.first_range_by_token(token_id, token_to_map.kind())?;
- let token = node.syntax_node().covering_element(range).into_token()?;
+ let syntax_node = node.syntax_node();
+ let token = rev_tmap
+ .ranges_by_token(token_id, token_to_map.kind())
+ .filter_map(|range| syntax_node.covering_element(range).into_token())
+ .min_by_key(|t| {
+ // prefer tokens of the same kind and text
+ // Note the inversion of the score here, as we want to prefer the first token in case
+ // of all tokens having the same score
+ (t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8
+ })?;
Some((node.syntax_node(), token))
}
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 fc128102f..a5b499fe8 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -811,6 +811,31 @@ impl<'a> InFile<&'a SyntaxNode> {
_ => None,
}
}
+
+ pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
+ // This kind of upmapping can only be achieved in attribute expanded files,
+ // as we don't have node inputs otherwise and therefor can't find an `N` node in the input
+ if !self.file_id.is_macro() {
+ return Some(self.map(Clone::clone));
+ } else if !self.file_id.is_attr_macro(db) {
+ return None;
+ }
+
+ if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
+ {
+ if file_id.is_macro() {
+ let range = first.text_range().cover(last.text_range());
+ tracing::error!("Failed mapping out of macro file for {:?}", range);
+ return None;
+ }
+ // FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
+ let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
+ let kind = self.value.kind();
+ let value = anc.ancestors().find(|it| it.kind() == kind)?;
+ return Some(InFile::new(file_id, value));
+ }
+ None
+ }
}
impl InFile<SyntaxToken> {
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 4ce21a579..2679a1c36 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -263,6 +263,7 @@ pub mod known {
Iterator,
IntoIterator,
Item,
+ IntoIter,
Try,
Ok,
Future,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
index 7f143f396..ed13275ba 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml
@@ -11,18 +11,19 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
+itertools = "0.10.5"
arrayvec = "0.7.2"
-smallvec = "1.9.0"
+smallvec = "1.10.0"
ena = "0.14.0"
tracing = "0.1.35"
rustc-hash = "1.1.0"
scoped-tls = "1.0.0"
-chalk-solve = { version = "0.84.0", default-features = false }
-chalk-ir = "0.84.0"
-chalk-recursive = { version = "0.84.0", default-features = false }
+chalk-solve = { version = "0.86.0", default-features = false }
+chalk-ir = "0.86.0"
+chalk-recursive = { version = "0.86.0", default-features = false }
+chalk-derive = "0.86.0"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
-once_cell = "1.12.0"
+once_cell = "1.15.0"
typed-arena = "2.0.1"
stdx = { path = "../stdx", version = "0.0.0" }
@@ -37,7 +38,7 @@ limit = { path = "../limit", version = "0.0.0" }
test-utils = { path = "../test-utils" }
expect-test = "1.4.0"
tracing = "0.1.35"
-tracing-subscriber = { version = "0.3.14", default-features = false, features = [
+tracing-subscriber = { version = "0.3.16", default-features = false, features = [
"env-filter",
"registry",
] }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
index 344036dd8..78911d8dc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs
@@ -1,7 +1,7 @@
//! In certain situations, rust automatically inserts derefs as necessary: for
//! example, field accesses `foo.bar` still work when `foo` is actually a
//! reference to a type with the field `bar`. This is an approximation of the
-//! logic in rustc (which lives in librustc_typeck/check/autoderef.rs).
+//! logic in rustc (which lives in rustc_hir_analysis/check/autoderef.rs).
use std::sync::Arc;
@@ -123,13 +123,14 @@ fn deref_by_trait(table: &mut InferenceTable<'_>, ty: Ty) -> Option<Ty> {
let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?;
let projection = {
- let b = TyBuilder::assoc_type_projection(db, target);
+ let b = TyBuilder::subst_for_def(db, deref_trait, None);
if b.remaining() != 1 {
// the Target type + Deref trait should only have one generic parameter,
// namely Deref's Self type
return None;
}
- b.push(ty).build()
+ let deref_subst = b.push(ty).build();
+ TyBuilder::assoc_type_projection(db, target, Some(deref_subst)).build()
};
// Check that the type implements Deref at all
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 94d7806cb..9ae752556 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs
@@ -6,19 +6,19 @@ use chalk_ir::{
cast::{Cast, CastTo, Caster},
fold::TypeFoldable,
interner::HasInterner,
- AdtId, BoundVar, DebruijnIndex, Scalar,
+ AdtId, DebruijnIndex, Scalar,
};
use hir_def::{
- builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, GenericDefId, TraitId,
- TypeAliasId,
+ builtin_type::BuiltinType, generics::TypeOrConstParamData, ConstParamId, DefWithBodyId,
+ GenericDefId, TraitId, TypeAliasId,
};
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, CallableSig, ConstData,
- ConstValue, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty,
- TyDefId, TyExt, TyKind, ValueTyDefId,
+ to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
+ GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
+ ValueTyDefId,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -34,17 +34,32 @@ pub struct TyBuilder<D> {
data: D,
vec: SmallVec<[GenericArg; 2]>,
param_kinds: SmallVec<[ParamKind; 2]>,
+ parent_subst: Substitution,
}
impl<A> TyBuilder<A> {
fn with_data<B>(self, data: B) -> TyBuilder<B> {
- TyBuilder { data, param_kinds: self.param_kinds, vec: self.vec }
+ TyBuilder {
+ data,
+ vec: self.vec,
+ param_kinds: self.param_kinds,
+ parent_subst: self.parent_subst,
+ }
}
}
impl<D> TyBuilder<D> {
- fn new(data: D, param_kinds: SmallVec<[ParamKind; 2]>) -> TyBuilder<D> {
- TyBuilder { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds }
+ fn new(
+ data: D,
+ param_kinds: SmallVec<[ParamKind; 2]>,
+ parent_subst: Option<Substitution>,
+ ) -> Self {
+ let parent_subst = parent_subst.unwrap_or_else(|| Substitution::empty(Interner));
+ Self { data, vec: SmallVec::with_capacity(param_kinds.len()), param_kinds, parent_subst }
+ }
+
+ fn new_empty(data: D) -> Self {
+ TyBuilder::new(data, SmallVec::new(), None)
}
fn build_internal(self) -> (D, Substitution) {
@@ -52,13 +67,18 @@ impl<D> TyBuilder<D> {
for (a, e) in self.vec.iter().zip(self.param_kinds.iter()) {
self.assert_match_kind(a, e);
}
- let subst = Substitution::from_iter(Interner, self.vec);
+ let subst = Substitution::from_iter(
+ Interner,
+ self.vec.into_iter().chain(self.parent_subst.iter(Interner).cloned()),
+ );
(self.data, subst)
}
pub fn push(mut self, arg: impl CastTo<GenericArg>) -> Self {
+ assert!(self.remaining() > 0);
let arg = arg.cast(Interner);
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"),
@@ -68,7 +88,9 @@ impl<D> TyBuilder<D> {
}
};
assert_eq!(*expected_kind, arg_kind);
+
self.vec.push(arg);
+
self
}
@@ -79,20 +101,12 @@ impl<D> TyBuilder<D> {
pub fn fill_with_bound_vars(self, debruijn: DebruijnIndex, starting_from: usize) -> Self {
// self.fill is inlined to make borrow checker happy
let mut this = self;
- let other = this.param_kinds.iter().skip(this.vec.len());
+ let other = &this.param_kinds[this.vec.len()..];
let filler = (starting_from..).zip(other).map(|(idx, kind)| match kind {
- ParamKind::Type => {
- GenericArgData::Ty(TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner))
- .intern(Interner)
+ ParamKind::Type => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner),
+ ParamKind::Const(ty) => {
+ BoundVar::new(debruijn, idx).to_const(Interner, ty.clone()).cast(Interner)
}
- ParamKind::Const(ty) => GenericArgData::Const(
- ConstData {
- value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
- ty: ty.clone(),
- }
- .intern(Interner),
- )
- .intern(Interner),
});
this.vec.extend(filler.take(this.remaining()).casted(Interner));
assert_eq!(this.remaining(), 0);
@@ -102,8 +116,8 @@ impl<D> TyBuilder<D> {
pub fn fill_with_unknown(self) -> Self {
// self.fill is inlined to make borrow checker happy
let mut this = self;
- let filler = this.param_kinds.iter().skip(this.vec.len()).map(|x| match x {
- ParamKind::Type => GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner),
+ let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x {
+ ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
});
this.vec.extend(filler.casted(Interner));
@@ -113,33 +127,17 @@ impl<D> TyBuilder<D> {
pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable<'_>) -> Self {
self.fill(|x| match x {
- ParamKind::Type => GenericArgData::Ty(table.new_type_var()).intern(Interner),
- ParamKind::Const(ty) => {
- GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
- }
+ ParamKind::Type => table.new_type_var().cast(Interner),
+ ParamKind::Const(ty) => table.new_const_var(ty.clone()).cast(Interner),
})
}
pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self {
- self.vec.extend(self.param_kinds.iter().skip(self.vec.len()).map(filler));
+ self.vec.extend(self.param_kinds[self.vec.len()..].iter().map(filler));
assert_eq!(self.remaining(), 0);
self
}
- pub fn use_parent_substs(mut self, parent_substs: &Substitution) -> Self {
- assert!(self.vec.is_empty());
- assert!(parent_substs.len(Interner) <= self.param_kinds.len());
- self.extend(parent_substs.iter(Interner).cloned());
- self
- }
-
- fn extend(&mut self, it: impl Iterator<Item = GenericArg> + Clone) {
- for x in it.clone().zip(self.param_kinds.iter().skip(self.vec.len())) {
- self.assert_match_kind(&x.0, &x.1);
- }
- self.vec.extend(it);
- }
-
fn assert_match_kind(&self, a: &chalk_ir::GenericArg<Interner>, e: &ParamKind) {
match (a.data(Interner), e) {
(chalk_ir::GenericArgData::Ty(_), ParamKind::Type)
@@ -188,21 +186,42 @@ impl TyBuilder<()> {
params.placeholder_subst(db)
}
- pub fn subst_for_def(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> TyBuilder<()> {
- let def = def.into();
- let params = generics(db.upcast(), def);
- TyBuilder::new(
- (),
- params
- .iter()
- .map(|(id, data)| match data {
- TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
- TypeOrConstParamData::ConstParamData(_) => {
- ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
- }
- })
- .collect(),
- )
+ pub fn subst_for_def(
+ db: &dyn HirDatabase,
+ def: impl Into<GenericDefId>,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<()> {
+ let generics = generics(db.upcast(), def.into());
+ assert!(generics.parent_generics().is_some() == parent_subst.is_some());
+ let params = generics
+ .iter_self()
+ .map(|(id, data)| match data {
+ TypeOrConstParamData::TypeParamData(_) => ParamKind::Type,
+ TypeOrConstParamData::ConstParamData(_) => {
+ ParamKind::Const(db.const_param_ty(ConstParamId::from_unchecked(id)))
+ }
+ })
+ .collect();
+ TyBuilder::new((), params, parent_subst)
+ }
+
+ /// Creates a `TyBuilder` to build `Substitution` for a generator defined in `parent`.
+ ///
+ /// A generator's substitution consists of:
+ /// - resume type of generator
+ /// - yield type of generator ([`Generator::Yield`](std::ops::Generator::Yield))
+ /// - return type of generator ([`Generator::Return`](std::ops::Generator::Return))
+ /// - generic parameters in scope on `parent`
+ /// in this order.
+ ///
+ /// This method prepopulates the builder with placeholder substitution of `parent`, so you
+ /// should only push exactly 3 `GenericArg`s before building.
+ pub fn subst_for_generator(db: &dyn HirDatabase, parent: DefWithBodyId) -> TyBuilder<()> {
+ let parent_subst =
+ parent.as_generic_def_id().map(|p| generics(db.upcast(), p).placeholder_subst(db));
+ // These represent resume type, yield type, and return type of generator.
+ let params = std::iter::repeat(ParamKind::Type).take(3).collect();
+ TyBuilder::new((), params, parent_subst)
}
pub fn build(self) -> Substitution {
@@ -213,7 +232,7 @@ impl TyBuilder<()> {
impl TyBuilder<hir_def::AdtId> {
pub fn adt(db: &dyn HirDatabase, def: hir_def::AdtId) -> TyBuilder<hir_def::AdtId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ TyBuilder::subst_for_def(db, def, None).with_data(def)
}
pub fn fill_with_defaults(
@@ -221,16 +240,27 @@ impl TyBuilder<hir_def::AdtId> {
db: &dyn HirDatabase,
mut fallback: impl FnMut() -> Ty,
) -> Self {
+ // Note that we're building ADT, so we never have parent generic parameters.
let defaults = db.generic_defaults(self.data.into());
+ let dummy_ty = TyKind::Error.intern(Interner).cast(Interner);
for default_ty in defaults.iter().skip(self.vec.len()) {
- if let GenericArgData::Ty(x) = default_ty.skip_binders().data(Interner) {
+ // NOTE(skip_binders): we only check if the arg type is error type.
+ if let Some(x) = default_ty.skip_binders().ty(Interner) {
if x.is_unknown() {
self.vec.push(fallback().cast(Interner));
continue;
}
- };
- // each default can depend on the previous parameters
- let subst_so_far = Substitution::from_iter(Interner, self.vec.clone());
+ }
+ // Each default can only depend on the previous parameters.
+ // FIXME: we don't handle const generics here.
+ let subst_so_far = Substitution::from_iter(
+ Interner,
+ self.vec
+ .iter()
+ .cloned()
+ .chain(iter::repeat(dummy_ty.clone()))
+ .take(self.param_kinds.len()),
+ );
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
}
self
@@ -245,7 +275,7 @@ impl TyBuilder<hir_def::AdtId> {
pub struct Tuple(usize);
impl TyBuilder<Tuple> {
pub fn tuple(size: usize) -> TyBuilder<Tuple> {
- TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect())
+ TyBuilder::new(Tuple(size), iter::repeat(ParamKind::Type).take(size).collect(), None)
}
pub fn build(self) -> Ty {
@@ -256,7 +286,7 @@ impl TyBuilder<Tuple> {
impl TyBuilder<TraitId> {
pub fn trait_ref(db: &dyn HirDatabase, def: TraitId) -> TyBuilder<TraitId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ TyBuilder::subst_for_def(db, def, None).with_data(def)
}
pub fn build(self) -> TraitRef {
@@ -266,8 +296,12 @@ impl TyBuilder<TraitId> {
}
impl TyBuilder<TypeAliasId> {
- pub fn assoc_type_projection(db: &dyn HirDatabase, def: TypeAliasId) -> TyBuilder<TypeAliasId> {
- TyBuilder::subst_for_def(db, def).with_data(def)
+ pub fn assoc_type_projection(
+ db: &dyn HirDatabase,
+ def: TypeAliasId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<TypeAliasId> {
+ TyBuilder::subst_for_def(db, def, parent_subst).with_data(def)
}
pub fn build(self) -> ProjectionTy {
@@ -277,19 +311,6 @@ impl TyBuilder<TypeAliasId> {
}
impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Binders<T>> {
- fn subst_binders(b: Binders<T>) -> Self {
- let param_kinds = b
- .binders
- .iter(Interner)
- .map(|x| match x {
- chalk_ir::VariableKind::Ty(_) => ParamKind::Type,
- chalk_ir::VariableKind::Lifetime => panic!("Got lifetime parameter"),
- chalk_ir::VariableKind::Const(ty) => ParamKind::Const(ty.clone()),
- })
- .collect();
- TyBuilder::new(b, param_kinds)
- }
-
pub fn build(self) -> T {
let (b, subst) = self.build_internal();
b.substitute(Interner, &subst)
@@ -297,15 +318,41 @@ impl<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>> TyBuilder<Bin
}
impl TyBuilder<Binders<Ty>> {
- pub fn def_ty(db: &dyn HirDatabase, def: TyDefId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.ty(def))
+ pub fn def_ty(
+ db: &dyn HirDatabase,
+ def: TyDefId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<Binders<Ty>> {
+ let poly_ty = db.ty(def);
+ let id: GenericDefId = match def {
+ TyDefId::BuiltinType(_) => {
+ assert!(parent_subst.is_none());
+ return TyBuilder::new_empty(poly_ty);
+ }
+ TyDefId::AdtId(id) => id.into(),
+ TyDefId::TypeAliasId(id) => id.into(),
+ };
+ TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_ty)
}
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.impl_self_ty(def))
+ TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
- pub fn value_ty(db: &dyn HirDatabase, def: ValueTyDefId) -> TyBuilder<Binders<Ty>> {
- TyBuilder::subst_binders(db.value_ty(def))
+ pub fn value_ty(
+ db: &dyn HirDatabase,
+ def: ValueTyDefId,
+ parent_subst: Option<Substitution>,
+ ) -> TyBuilder<Binders<Ty>> {
+ let poly_value_ty = db.value_ty(def);
+ let id = match def.to_generic_def_id() {
+ Some(id) => id,
+ None => {
+ // static items
+ assert!(parent_subst.is_none());
+ return TyBuilder::new_empty(poly_value_ty);
+ }
+ };
+ TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
}
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index faec99c7d..43c3451ca 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -11,6 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
+ expr::Movability,
lang_item::{lang_attr, LangItemTarget},
AssocItemId, GenericDefId, HasModule, ItemContainerId, Lookup, ModuleId, TypeAliasId,
};
@@ -26,9 +27,9 @@ use crate::{
to_assoc_type_id, to_chalk_trait_id,
traits::ChalkContext,
utils::generics,
- AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId, Interner, ProjectionTy,
- ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder,
- TyExt, TyKind, WhereClause,
+ wrap_empty_binders, AliasEq, AliasTy, BoundVar, CallableDefId, DebruijnIndex, FnDefId,
+ Interner, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause, Substitution, TraitRef,
+ TraitRefExt, Ty, TyBuilder, TyExt, TyKind, WhereClause,
};
pub(crate) type AssociatedTyDatum = chalk_solve::rust_ir::AssociatedTyDatum<Interner>;
@@ -372,17 +373,62 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
}
fn generator_datum(
&self,
- _: chalk_ir::GeneratorId<Interner>,
+ id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorDatum<Interner>> {
- // FIXME
- unimplemented!()
+ let (parent, expr) = self.db.lookup_intern_generator(id.into());
+
+ // We fill substitution with unknown type, because we only need to know whether the generic
+ // params are types or consts to build `Binders` and those being filled up are for
+ // `resume_type`, `yield_type`, and `return_type` of the generator in question.
+ let subst = TyBuilder::subst_for_generator(self.db, parent).fill_with_unknown().build();
+
+ let input_output = rust_ir::GeneratorInputOutputDatum {
+ resume_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
+ .intern(Interner),
+ yield_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 1))
+ .intern(Interner),
+ return_type: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 2))
+ .intern(Interner),
+ // FIXME: calculate upvars
+ upvars: vec![],
+ };
+
+ let it = subst
+ .iter(Interner)
+ .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone()));
+ let input_output = crate::make_type_and_const_binders(it, input_output);
+
+ let movability = match self.db.body(parent)[expr] {
+ hir_def::expr::Expr::Closure {
+ closure_kind: hir_def::expr::ClosureKind::Generator(movability),
+ ..
+ } => movability,
+ _ => unreachable!("non generator expression interned as generator"),
+ };
+ let movability = match movability {
+ Movability::Static => rust_ir::Movability::Static,
+ Movability::Movable => rust_ir::Movability::Movable,
+ };
+
+ Arc::new(rust_ir::GeneratorDatum { movability, input_output })
}
fn generator_witness_datum(
&self,
- _: chalk_ir::GeneratorId<Interner>,
+ id: chalk_ir::GeneratorId<Interner>,
) -> std::sync::Arc<chalk_solve::rust_ir::GeneratorWitnessDatum<Interner>> {
- // FIXME
- unimplemented!()
+ // FIXME: calculate inner types
+ let inner_types =
+ rust_ir::GeneratorWitnessExistential { types: wrap_empty_binders(vec![]) };
+
+ let (parent, _) = self.db.lookup_intern_generator(id.into());
+ // See the comment in `generator_datum()` for unknown types.
+ let subst = TyBuilder::subst_for_generator(self.db, parent).fill_with_unknown().build();
+ let it = subst
+ .iter(Interner)
+ .map(|it| it.constant(Interner).map(|c| c.data(Interner).ty.clone()));
+ let inner_types = crate::make_type_and_const_binders(it, inner_types);
+
+ Arc::new(rust_ir::GeneratorWitnessDatum { inner_types })
}
fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> {
@@ -429,10 +475,15 @@ pub(crate) fn associated_ty_data_query(
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
let ctx = crate::TyLoweringContext::new(db, &resolver)
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable);
- let pro_ty = TyBuilder::assoc_type_projection(db, type_alias)
+
+ let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
+ .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, generic_params.len_self())
+ .build();
+ let pro_ty = TyBuilder::assoc_type_projection(db, type_alias, Some(trait_subst))
.fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0)
.build();
let self_ty = TyKind::Alias(AliasTy::Projection(pro_ty)).intern(Interner);
+
let mut bounds: Vec<_> = type_alias_data
.bounds
.iter()
@@ -772,10 +823,10 @@ pub(super) fn generic_predicate_to_inline_bound(
Some(chalk_ir::Binders::new(binders, rust_ir::InlineBound::TraitBound(trait_bound)))
}
WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(projection_ty), ty }) => {
- if projection_ty.self_type_parameter(Interner) != self_ty_shifted_in {
+ let trait_ = projection_ty.trait_(db);
+ if projection_ty.self_type_parameter(db) != self_ty_shifted_in {
return None;
}
- let trait_ = projection_ty.trait_(db);
let args_no_self = projection_ty.substitution.as_slice(Interner)[1..]
.iter()
.map(|ty| ty.clone().cast(Interner))
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 4a5533c64..e2099d7e5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -152,7 +152,7 @@ impl TyExt for Ty {
TyKind::FnDef(def, parameters) => {
let callable_def = db.lookup_intern_callable_def((*def).into());
let sig = db.callable_item_signature(callable_def);
- Some(sig.substitute(Interner, &parameters))
+ Some(sig.substitute(Interner, parameters))
}
TyKind::Closure(.., substs) => {
let sig_param = substs.at(Interner, 0).assert_ty_ref(Interner);
@@ -166,6 +166,8 @@ impl TyExt for Ty {
let trait_ref = match self.kind(Interner) {
// The principal trait bound should be the first element of the bounds. This is an
// invariant ensured by `TyLoweringContext::lower_dyn_trait()`.
+ // FIXME: dyn types may not have principal trait and we don't want to return auto trait
+ // here.
TyKind::Dyn(dyn_ty) => dyn_ty.bounds.skip_binders().interned().get(0).and_then(|b| {
match b.skip_binders() {
WhereClause::Implemented(trait_ref) => Some(trait_ref),
@@ -260,7 +262,7 @@ impl TyExt for Ty {
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
- }) => &proj.self_type_parameter(Interner) == self,
+ }) => &proj.self_type_parameter(db) == self,
_ => false,
})
.collect::<Vec<_>>();
@@ -331,6 +333,7 @@ impl TyExt for Ty {
pub trait ProjectionTyExt {
fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef;
fn trait_(&self, db: &dyn HirDatabase) -> TraitId;
+ fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty;
}
impl ProjectionTyExt for ProjectionTy {
@@ -347,6 +350,10 @@ impl ProjectionTyExt for ProjectionTy {
_ => panic!("projection ty without parent trait"),
}
}
+
+ fn self_type_parameter(&self, db: &dyn HirDatabase) -> Ty {
+ self.trait_ref(db).self_type_parameter(Interner)
+ }
}
pub trait TraitRefExt {
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 6ecb6e6fd..2c0c6e0b8 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -7,14 +7,17 @@ use std::{
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
use hir_def::{
+ builtin_type::BuiltinInt,
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
path::ModPath,
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
+ src::HasChildSource,
type_ref::ConstScalar,
- ConstId, DefWithBodyId,
+ ConstId, DefWithBodyId, EnumVariantId, Lookup,
};
-use la_arena::{Arena, Idx};
+use la_arena::{Arena, Idx, RawIdx};
use stdx::never;
+use syntax::ast::HasName;
use crate::{
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
@@ -77,6 +80,7 @@ pub enum ConstEvalError {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ComputedExpr {
Literal(Literal),
+ Enum(String, EnumVariantId, Literal),
Tuple(Box<[ComputedExpr]>),
}
@@ -104,6 +108,7 @@ impl Display for ComputedExpr {
Literal::String(x) => std::fmt::Debug::fmt(x, f),
Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
},
+ ComputedExpr::Enum(name, _, _) => name.fmt(f),
ComputedExpr::Tuple(t) => {
f.write_char('(')?;
for x in &**t {
@@ -148,13 +153,51 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
}
}
+fn get_name(ctx: &mut ConstEvalCtx<'_>, variant: EnumVariantId) -> String {
+ let loc = variant.parent.lookup(ctx.db.upcast());
+ let children = variant.parent.child_source(ctx.db.upcast());
+ let item_tree = loc.id.item_tree(ctx.db.upcast());
+
+ let variant_name = children.value[variant.local_id].name();
+ let enum_name = item_tree[loc.id.value].name.to_string();
+ enum_name + "::" + &variant_name.unwrap().to_string()
+}
+
pub fn eval_const(
expr_id: ExprId,
ctx: &mut ConstEvalCtx<'_>,
) -> Result<ComputedExpr, ConstEvalError> {
+ let u128_to_i128 = |it: u128| -> Result<i128, ConstEvalError> {
+ it.try_into().map_err(|_| ConstEvalError::NotSupported("u128 is too big"))
+ };
+
let expr = &ctx.exprs[expr_id];
match expr {
- Expr::Missing => Err(ConstEvalError::IncompleteExpr),
+ Expr::Missing => match ctx.owner {
+ // evaluate the implicit variant index of an enum variant without expression
+ // FIXME: This should return the type of the enum representation
+ DefWithBodyId::VariantId(variant) => {
+ let prev_idx: u32 = variant.local_id.into_raw().into();
+ let prev_idx = prev_idx.checked_sub(1).map(RawIdx::from).map(Idx::from_raw);
+ let value = match prev_idx {
+ Some(local_id) => {
+ let prev_variant = EnumVariantId { local_id, parent: variant.parent };
+ 1 + match ctx.db.const_eval_variant(prev_variant)? {
+ ComputedExpr::Literal(Literal::Int(v, _)) => v,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
+ _ => {
+ return Err(ConstEvalError::NotSupported(
+ "Enum can't contain this kind of value",
+ ))
+ }
+ }
+ }
+ _ => 0,
+ };
+ Ok(ComputedExpr::Literal(Literal::Int(value, Some(BuiltinInt::I128))))
+ }
+ _ => Err(ConstEvalError::IncompleteExpr),
+ },
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
&Expr::UnaryOp { expr, op } => {
let ty = &ctx.expr_ty(expr);
@@ -167,9 +210,7 @@ pub fn eval_const(
return Ok(ComputedExpr::Literal(Literal::Bool(!b)))
}
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => v
- .try_into()
- .map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
let r = match ty.kind(Interner) {
@@ -198,9 +239,7 @@ pub fn eval_const(
hir_def::expr::UnaryOp::Neg => {
let v = match ev {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => v
- .try_into()
- .map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
Ok(ComputedExpr::Literal(Literal::Int(
@@ -219,16 +258,12 @@ pub fn eval_const(
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
let v1 = match lhs {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => {
- v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
- }
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
let v2 = match rhs {
ComputedExpr::Literal(Literal::Int(v, _)) => v,
- ComputedExpr::Literal(Literal::Uint(v, _)) => {
- v.try_into().map_err(|_| ConstEvalError::NotSupported("too big u128"))?
- }
+ ComputedExpr::Literal(Literal::Uint(v, _)) => u128_to_i128(v)?,
_ => return Err(ConstEvalError::NotSupported("this kind of operator")),
};
match op {
@@ -339,9 +374,22 @@ pub fn eval_const(
ValueNs::GenericParam(_) => {
Err(ConstEvalError::NotSupported("const generic without substitution"))
}
+ ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
+ ComputedExpr::Literal(lit) => {
+ Ok(ComputedExpr::Enum(get_name(ctx, id), id, lit))
+ }
+ _ => Err(ConstEvalError::NotSupported(
+ "Enums can't evalute to anything but numbers",
+ )),
+ },
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
}
}
+ // FIXME: Handle the cast target
+ &Expr::Cast { expr, .. } => match eval_const(expr, ctx)? {
+ ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
+ _ => Err(ConstEvalError::NotSupported("Can't cast these types")),
+ },
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
}
}
@@ -412,7 +460,15 @@ pub(crate) fn const_eval_recover(
Err(ConstEvalError::Loop)
}
-pub(crate) fn const_eval_query(
+pub(crate) fn const_eval_variant_recover(
+ _: &dyn HirDatabase,
+ _: &[String],
+ _: &EnumVariantId,
+) -> Result<ComputedExpr, ConstEvalError> {
+ Err(ConstEvalError::Loop)
+}
+
+pub(crate) fn const_eval_variant_query(
db: &dyn HirDatabase,
const_id: ConstId,
) -> Result<ComputedExpr, ConstEvalError> {
@@ -433,6 +489,26 @@ pub(crate) fn const_eval_query(
result
}
+pub(crate) fn const_eval_query_variant(
+ db: &dyn HirDatabase,
+ variant_id: EnumVariantId,
+) -> Result<ComputedExpr, ConstEvalError> {
+ let def = variant_id.into();
+ let body = db.body(def);
+ let infer = &db.infer(def);
+ eval_const(
+ body.body_expr,
+ &mut ConstEvalCtx {
+ db,
+ owner: def,
+ exprs: &body.exprs,
+ pats: &body.pats,
+ local_data: HashMap::default(),
+ infer,
+ },
+ )
+}
+
pub(crate) fn eval_to_const<'a>(
expr: Idx<Expr>,
mode: ParamLoweringMode,
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 4a052851a..b76506f6e 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
@@ -88,6 +88,49 @@ fn consts() {
}
#[test]
+fn enums() {
+ check_number(
+ r#"
+ enum E {
+ F1 = 1,
+ F2 = 2 * E::F1 as u8,
+ F3 = 3 * E::F2 as u8,
+ }
+ const GOAL: i32 = E::F3 as u8;
+ "#,
+ 6,
+ );
+ check_number(
+ r#"
+ enum E { F1 = 1, F2, }
+ const GOAL: i32 = E::F2 as u8;
+ "#,
+ 2,
+ );
+ check_number(
+ r#"
+ enum E { F1, }
+ const GOAL: i32 = E::F1 as u8;
+ "#,
+ 0,
+ );
+ let r = eval_goal(
+ r#"
+ enum E { A = 1, }
+ const GOAL: E = E::A;
+ "#,
+ )
+ .unwrap();
+ match r {
+ ComputedExpr::Enum(name, _, Literal::Uint(val, _)) => {
+ assert_eq!(name, "E::A");
+ assert_eq!(val, 1);
+ }
+ x => panic!("Expected enum but found {:?}", x),
+ }
+}
+
+#[test]
fn const_loop() {
check_fail(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index b385b1caf..932fce835 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -6,8 +6,8 @@ use std::sync::Arc;
use arrayvec::ArrayVec;
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
use hir_def::{
- db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
- GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
+ db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId,
+ FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
};
use la_arena::ArenaMap;
@@ -43,10 +43,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::lower::const_param_ty_query)]
fn const_param_ty(&self, def: ConstParamId) -> Ty;
- #[salsa::invoke(crate::consteval::const_eval_query)]
+ #[salsa::invoke(crate::consteval::const_eval_variant_query)]
#[salsa::cycle(crate::consteval::const_eval_recover)]
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
+ #[salsa::invoke(crate::consteval::const_eval_query_variant)]
+ #[salsa::cycle(crate::consteval::const_eval_variant_recover)]
+ fn const_eval_variant(&self, def: EnumVariantId) -> Result<ComputedExpr, ConstEvalError>;
+
#[salsa::invoke(crate::lower::impl_trait_query)]
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
@@ -116,6 +120,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
#[salsa::interned]
fn intern_closure(&self, id: (DefWithBodyId, ExprId)) -> InternedClosureId;
+ #[salsa::interned]
+ fn intern_generator(&self, id: (DefWithBodyId, ExprId)) -> InternedGeneratorId;
#[salsa::invoke(chalk_db::associated_ty_data_query)]
fn associated_ty_data(&self, id: chalk_db::AssocTypeId) -> Arc<chalk_db::AssociatedTyDatum>;
@@ -150,6 +156,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
id: chalk_db::AssociatedTyValueId,
) -> Arc<chalk_db::AssociatedTyValue>;
+ #[salsa::invoke(crate::traits::normalize_projection_query)]
+ #[salsa::transparent]
+ fn normalize_projection(
+ &self,
+ projection: crate::ProjectionTy,
+ env: Arc<crate::TraitEnvironment>,
+ ) -> Ty;
+
#[salsa::invoke(trait_solve_wait)]
#[salsa::transparent]
fn trait_solve(
@@ -180,6 +194,9 @@ fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult>
DefWithBodyId::ConstId(it) => {
db.const_data(it).name.clone().unwrap_or_else(Name::missing).to_string()
}
+ DefWithBodyId::VariantId(it) => {
+ db.enum_data(it.parent).variants[it.local_id].name.to_string()
+ }
});
db.infer_query(def)
}
@@ -218,6 +235,10 @@ impl_intern_key!(InternedOpaqueTyId);
pub struct InternedClosureId(salsa::InternId);
impl_intern_key!(InternedClosureId);
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct InternedGeneratorId(salsa::InternId);
+impl_intern_key!(InternedGeneratorId);
+
/// This exists just for Chalk, because Chalk just has a single `FnDefId` where
/// we have different IDs for struct and enum variant constructors.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
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 161b19a73..431ab949b 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
@@ -18,7 +18,9 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec<ExprId> {
let is_unsafe = match def {
DefWithBodyId::FunctionId(it) => db.function_data(it).has_unsafe_kw(),
- DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
+ DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) => {
+ false
+ }
};
if is_unsafe {
return res;
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 d2f9c2b8b..0221f922f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -20,13 +20,14 @@ use hir_def::{
};
use hir_expand::{hygiene::Hygiene, name::Name};
use itertools::Itertools;
+use smallvec::SmallVec;
use syntax::SmolStr;
use crate::{
db::HirDatabase,
from_assoc_type_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx,
mapping::from_chalk,
- primitive, subst_prefix, to_assoc_type_id,
+ primitive, to_assoc_type_id,
utils::{self, generics},
AdtId, AliasEq, AliasTy, Binders, CallableDefId, CallableSig, Const, ConstValue, DomainGoal,
GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData, LifetimeOutlives, Mutability,
@@ -221,6 +222,7 @@ pub enum DisplaySourceCodeError {
PathNotFound,
UnknownType,
Closure,
+ Generator,
}
pub enum HirDisplayError {
@@ -289,7 +291,7 @@ impl HirDisplay for ProjectionTy {
let trait_ = f.db.trait_data(self.trait_(f.db));
write!(f, "<")?;
- self.self_type_parameter(Interner).hir_fmt(f)?;
+ self.self_type_parameter(f.db).hir_fmt(f)?;
write!(f, " as {}", trait_.name)?;
if self.substitution.len(Interner) > 1 {
write!(f, "<")?;
@@ -504,8 +506,15 @@ impl HirDisplay for Ty {
let total_len = parent_params + self_param + type_params + const_params;
// We print all params except implicit impl Trait params. Still a bit weird; should we leave out parent and self?
if total_len > 0 {
+ // `parameters` are in the order of fn's params (including impl traits),
+ // parent's params (those from enclosing impl or trait, if any).
+ let parameters = parameters.as_slice(Interner);
+ let fn_params_len = self_param + type_params + const_params;
+ let fn_params = parameters.get(..fn_params_len);
+ let parent_params = parameters.get(parameters.len() - parent_params..);
+ let params = parent_params.into_iter().chain(fn_params).flatten();
write!(f, "<")?;
- f.write_joined(&parameters.as_slice(Interner)[..total_len], ", ")?;
+ f.write_joined(params, ", ")?;
write!(f, ">")?;
}
}
@@ -533,6 +542,7 @@ impl HirDisplay for Ty {
f.db.upcast(),
ItemInNs::Types((*def_id).into()),
module_id,
+ false,
) {
write!(f, "{}", path)?;
} else {
@@ -576,9 +586,8 @@ impl HirDisplay for Ty {
Some(x) => x,
None => return true,
};
- let actual_default = default_parameter
- .clone()
- .substitute(Interner, &subst_prefix(parameters, i));
+ let actual_default =
+ default_parameter.clone().substitute(Interner, &parameters);
parameter != &actual_default
}
let mut default_from = 0;
@@ -722,7 +731,7 @@ impl HirDisplay for Ty {
WhereClause::AliasEq(AliasEq {
alias: AliasTy::Projection(proj),
ty: _,
- }) => &proj.self_type_parameter(Interner) == self,
+ }) => &proj.self_type_parameter(f.db) == self,
_ => false,
})
.collect::<Vec<_>>();
@@ -742,9 +751,19 @@ impl HirDisplay for Ty {
}
TyKind::BoundVar(idx) => idx.hir_fmt(f)?,
TyKind::Dyn(dyn_ty) => {
+ // Reorder bounds to satisfy `write_bounds_like_dyn_trait()`'s expectation.
+ // FIXME: `Iterator::partition_in_place()` or `Vec::drain_filter()` may make it
+ // more efficient when either of them hits stable.
+ let mut bounds: SmallVec<[_; 4]> =
+ dyn_ty.bounds.skip_binders().iter(Interner).cloned().collect();
+ let (auto_traits, others): (SmallVec<[_; 4]>, _) =
+ bounds.drain(1..).partition(|b| b.skip_binders().trait_id().is_some());
+ bounds.extend(others);
+ bounds.extend(auto_traits);
+
write_bounds_like_dyn_trait_with_prefix(
"dyn",
- dyn_ty.bounds.skip_binders().interned(),
+ &bounds,
SizedByDefault::NotSized,
f,
)?;
@@ -782,7 +801,34 @@ impl HirDisplay for Ty {
write!(f, "{{unknown}}")?;
}
TyKind::InferenceVar(..) => write!(f, "_")?,
- TyKind::Generator(..) => write!(f, "{{generator}}")?,
+ TyKind::Generator(_, subst) => {
+ if f.display_target.is_source_code() {
+ return Err(HirDisplayError::DisplaySourceCodeError(
+ DisplaySourceCodeError::Generator,
+ ));
+ }
+
+ let subst = subst.as_slice(Interner);
+ let a: Option<SmallVec<[&Ty; 3]>> = subst
+ .get(subst.len() - 3..)
+ .map(|args| args.iter().map(|arg| arg.ty(Interner)).collect())
+ .flatten();
+
+ if let Some([resume_ty, yield_ty, ret_ty]) = a.as_deref() {
+ write!(f, "|")?;
+ resume_ty.hir_fmt(f)?;
+ write!(f, "|")?;
+
+ write!(f, " yields ")?;
+ yield_ty.hir_fmt(f)?;
+
+ write!(f, " -> ")?;
+ ret_ty.hir_fmt(f)?;
+ } else {
+ // This *should* be unreachable, but fallback just in case.
+ write!(f, "{{generator}}")?;
+ }
+ }
TyKind::GeneratorWitness(..) => write!(f, "{{generator witness}}")?,
}
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 10ffde87e..0efff651c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -2,7 +2,7 @@
//! the type of each expression and pattern.
//!
//! For type inference, compare the implementations in rustc (the various
-//! check_* methods in librustc_typeck/check/mod.rs are a good entry point) and
+//! check_* methods in rustc_hir_analysis/check/mod.rs are a good entry point) and
//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for
//! inference here is the `infer` function, which infers the types of all
//! expressions in a given function.
@@ -19,14 +19,15 @@ use std::sync::Arc;
use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use hir_def::{
body::Body,
+ builtin_type::BuiltinType,
data::{ConstData, StaticData},
expr::{BindingAnnotation, ExprId, PatId},
lang_item::LangItemTarget,
path::{path, Path},
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::TypeRef,
- AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
- TraitId, TypeAliasId, VariantId,
+ AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule,
+ ItemContainerId, Lookup, TraitId, TypeAliasId, VariantId,
};
use hir_expand::name::{name, Name};
use itertools::Either;
@@ -67,6 +68,12 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_data(c)),
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
+ DefWithBodyId::VariantId(v) => {
+ ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() {
+ Either::Left(builtin) => BuiltinType::Int(builtin),
+ Either::Right(builtin) => BuiltinType::Uint(builtin),
+ });
+ }
}
ctx.infer_body();
@@ -332,7 +339,7 @@ pub struct InferenceResult {
/// unresolved or missing subpatterns or subpatterns of mismatched types.
pub type_of_pat: ArenaMap<PatId, Ty>,
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
- /// Interned Unknown to return references to.
+ /// Interned common types to return references to.
standard_types: InternedStandardTypes,
/// Stores the types which were implicitly dereferenced in pattern binding modes.
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
@@ -412,6 +419,8 @@ pub(crate) struct InferenceContext<'a> {
/// closures, but currently this is the only field that will change there,
/// so it doesn't make sense.
return_ty: Ty,
+ /// The resume type and the yield type, respectively, of the generator being inferred.
+ resume_yield_tys: Option<(Ty, Ty)>,
diverges: Diverges,
breakables: Vec<BreakableContext>,
}
@@ -476,6 +485,7 @@ impl<'a> InferenceContext<'a> {
table: unify::InferenceTable::new(db, trait_env.clone()),
trait_env,
return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature
+ resume_yield_tys: None,
db,
owner,
body,
@@ -673,10 +683,6 @@ impl<'a> InferenceContext<'a> {
)
}
- fn resolve_obligations_as_possible(&mut self) {
- self.table.resolve_obligations_as_possible();
- }
-
fn push_obligation(&mut self, o: DomainGoal) {
self.table.register_obligation(o.cast(Interner));
}
@@ -696,7 +702,6 @@ impl<'a> InferenceContext<'a> {
}
fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty {
- self.resolve_obligations_as_possible();
self.table.resolve_ty_shallow(ty)
}
@@ -708,6 +713,8 @@ impl<'a> InferenceContext<'a> {
&mut self,
inner_ty: Ty,
assoc_ty: Option<TypeAliasId>,
+ // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be
+ // handled when we support them.
params: &[GenericArg],
) -> Ty {
match assoc_ty {
@@ -799,7 +806,18 @@ impl<'a> InferenceContext<'a> {
self.resolve_variant_on_alias(ty, unresolved, path)
}
TypeNs::TypeAliasId(it) => {
- let ty = TyBuilder::def_ty(self.db, it.into())
+ let container = it.lookup(self.db.upcast()).container;
+ let parent_subst = match container {
+ ItemContainerId::TraitId(id) => {
+ let subst = TyBuilder::subst_for_def(self.db, id, None)
+ .fill_with_inference_vars(&mut self.table)
+ .build();
+ Some(subst)
+ }
+ // Type aliases do not exist in impls.
+ _ => None,
+ };
+ let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table)
.build();
self.resolve_variant_on_alias(ty, unresolved, path)
@@ -878,6 +896,12 @@ impl<'a> InferenceContext<'a> {
fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
let path = path![core::iter::IntoIterator];
let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
+ self.db.trait_data(trait_).associated_type_by_name(&name![IntoIter])
+ }
+
+ fn resolve_iterator_item(&self) -> Option<TypeAliasId> {
+ let path = path![core::iter::Iterator];
+ let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
self.db.trait_data(trait_).associated_type_by_name(&name![Item])
}
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 3ead92909..094e460db 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
@@ -12,6 +12,7 @@ use crate::{
use super::{Expectation, InferenceContext};
impl InferenceContext<'_> {
+ // This function handles both closures and generators.
pub(super) fn deduce_closure_type_from_expectations(
&mut self,
closure_expr: ExprId,
@@ -27,6 +28,11 @@ impl InferenceContext<'_> {
// Deduction from where-clauses in scope, as well as fn-pointer coercion are handled here.
let _ = self.coerce(Some(closure_expr), closure_ty, &expected_ty);
+ // Generators are not Fn* so return early.
+ if matches!(closure_ty.kind(Interner), TyKind::Generator(..)) {
+ return;
+ }
+
// Deduction based on the expected `dyn Fn` is done separately.
if let TyKind::Dyn(dyn_ty) = expected_ty.kind(Interner) {
if let Some(sig) = self.deduce_sig_from_dyn_ty(dyn_ty) {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
index f54440bf5..8df25c83c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs
@@ -3,7 +3,7 @@
//! like going from `&Vec<T>` to `&[T]`.
//!
//! See <https://doc.rust-lang.org/nomicon/coercions.html> and
-//! `librustc_typeck/check/coercion.rs`.
+//! `rustc_hir_analysis/check/coercion.rs`.
use std::{iter, sync::Arc};
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 2d04a864a..f56108b26 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
@@ -10,7 +10,10 @@ use chalk_ir::{
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
};
use hir_def::{
- expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, LabelId, Literal, Statement, UnaryOp},
+ expr::{
+ ArithOp, Array, BinaryOp, ClosureKind, CmpOp, Expr, ExprId, LabelId, Literal, Statement,
+ UnaryOp,
+ },
generics::TypeOrConstParamData,
path::{GenericArg, GenericArgs},
resolver::resolver_for_expr,
@@ -204,8 +207,10 @@ impl<'a> InferenceContext<'a> {
}
&Expr::For { iterable, body, pat, label } => {
let iterable_ty = self.infer_expr(iterable, &Expectation::none());
- let pat_ty =
+ let into_iter_ty =
self.resolve_associated_type(iterable_ty, self.resolve_into_iter_item());
+ let pat_ty =
+ self.resolve_associated_type(into_iter_ty, self.resolve_iterator_item());
self.infer_pat(pat, &pat_ty, BindingMode::default());
self.with_breakable_ctx(BreakableKind::Loop, self.err_ty(), label, |this| {
@@ -216,7 +221,7 @@ impl<'a> InferenceContext<'a> {
self.diverges = Diverges::Maybe;
TyBuilder::unit()
}
- Expr::Closure { body, args, ret_type, arg_types } => {
+ Expr::Closure { body, args, ret_type, arg_types, closure_kind } => {
assert_eq!(args.len(), arg_types.len());
let mut sig_tys = Vec::new();
@@ -244,20 +249,40 @@ impl<'a> InferenceContext<'a> {
),
})
.intern(Interner);
- let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
- let closure_ty =
- TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
- .intern(Interner);
+
+ let (ty, resume_yield_tys) = if matches!(closure_kind, ClosureKind::Generator(_)) {
+ // FIXME: report error when there are more than 1 parameter.
+ let resume_ty = match sig_tys.first() {
+ // When `sig_tys.len() == 1` the first type is the return type, not the
+ // first parameter type.
+ Some(ty) if sig_tys.len() > 1 => ty.clone(),
+ _ => self.result.standard_types.unit.clone(),
+ };
+ let yield_ty = self.table.new_type_var();
+
+ let subst = TyBuilder::subst_for_generator(self.db, self.owner)
+ .push(resume_ty.clone())
+ .push(yield_ty.clone())
+ .push(ret_ty.clone())
+ .build();
+
+ let generator_id = self.db.intern_generator((self.owner, tgt_expr)).into();
+ let generator_ty = TyKind::Generator(generator_id, subst).intern(Interner);
+
+ (generator_ty, Some((resume_ty, yield_ty)))
+ } else {
+ let closure_id = self.db.intern_closure((self.owner, tgt_expr)).into();
+ let closure_ty =
+ TyKind::Closure(closure_id, Substitution::from1(Interner, sig_ty.clone()))
+ .intern(Interner);
+
+ (closure_ty, None)
+ };
// Eagerly try to relate the closure type with the expected
// type, otherwise we often won't have enough information to
// infer the body.
- self.deduce_closure_type_from_expectations(
- tgt_expr,
- &closure_ty,
- &sig_ty,
- expected,
- );
+ self.deduce_closure_type_from_expectations(tgt_expr, &ty, &sig_ty, expected);
// Now go through the argument patterns
for (arg_pat, arg_ty) in args.iter().zip(sig_tys) {
@@ -266,6 +291,8 @@ impl<'a> InferenceContext<'a> {
let prev_diverges = mem::replace(&mut self.diverges, Diverges::Maybe);
let prev_ret_ty = mem::replace(&mut self.return_ty, ret_ty.clone());
+ let prev_resume_yield_tys =
+ mem::replace(&mut self.resume_yield_tys, resume_yield_tys);
self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| {
this.infer_expr_coerce(*body, &Expectation::has_type(ret_ty));
@@ -273,8 +300,9 @@ impl<'a> InferenceContext<'a> {
self.diverges = prev_diverges;
self.return_ty = prev_ret_ty;
+ self.resume_yield_tys = prev_resume_yield_tys;
- closure_ty
+ ty
}
Expr::Call { callee, args, .. } => {
let callee_ty = self.infer_expr(*callee, &Expectation::none());
@@ -423,11 +451,18 @@ impl<'a> InferenceContext<'a> {
TyKind::Never.intern(Interner)
}
Expr::Yield { expr } => {
- // FIXME: track yield type for coercion
- if let Some(expr) = expr {
- self.infer_expr(*expr, &Expectation::none());
+ if let Some((resume_ty, yield_ty)) = self.resume_yield_tys.clone() {
+ if let Some(expr) = expr {
+ self.infer_expr_coerce(*expr, &Expectation::has_type(yield_ty));
+ } else {
+ let unit = self.result.standard_types.unit.clone();
+ let _ = self.coerce(Some(tgt_expr), &unit, &yield_ty);
+ }
+ resume_ty
+ } else {
+ // FIXME: report error (yield expr in non-generator)
+ TyKind::Error.intern(Interner)
}
- TyKind::Never.intern(Interner)
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
@@ -952,11 +987,13 @@ impl<'a> InferenceContext<'a> {
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
let rhs_ty = self.table.new_type_var();
- let func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
- self.db.trait_data(self.resolve_lang_item(lang_item)?.as_trait()?).method_by_name(&name)
+ let trait_func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
+ let trait_id = self.resolve_lang_item(lang_item)?.as_trait()?;
+ let func = self.db.trait_data(trait_id).method_by_name(&name)?;
+ Some((trait_id, func))
});
- let func = match func {
- Some(func) => func,
+ let (trait_, func) = match trait_func {
+ Some(it) => it,
None => {
let rhs_ty = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone());
let rhs_ty = self.infer_expr_coerce(rhs, &Expectation::from_option(rhs_ty));
@@ -966,7 +1003,9 @@ impl<'a> InferenceContext<'a> {
}
};
- let subst = TyBuilder::subst_for_def(self.db, func)
+ // HACK: We can use this substitution for the function because the function itself doesn't
+ // have its own generic parameters.
+ let subst = TyBuilder::subst_for_def(self.db, trait_, None)
.push(lhs_ty.clone())
.push(rhs_ty.clone())
.build();
@@ -1245,19 +1284,7 @@ impl<'a> InferenceContext<'a> {
assert_eq!(self_params, 0); // method shouldn't have another Self param
let total_len = parent_params + type_params + const_params + impl_trait_params;
let mut substs = Vec::with_capacity(total_len);
- // Parent arguments are unknown
- for (id, param) in def_generics.iter_parent() {
- match param {
- TypeOrConstParamData::TypeParamData(_) => {
- substs.push(GenericArgData::Ty(self.table.new_type_var()).intern(Interner));
- }
- TypeOrConstParamData::ConstParamData(_) => {
- let ty = self.db.const_param_ty(ConstParamId::from_unchecked(id));
- substs
- .push(GenericArgData::Const(self.table.new_const_var(ty)).intern(Interner));
- }
- }
- }
+
// handle provided arguments
if let Some(generic_args) = generic_args {
// if args are provided, it should be all of them, but we can't rely on that
@@ -1266,7 +1293,7 @@ impl<'a> InferenceContext<'a> {
.iter()
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
.take(type_params + const_params)
- .zip(def_generics.iter_id().skip(parent_params))
+ .zip(def_generics.iter_id())
{
if let Some(g) = generic_arg_to_chalk(
self.db,
@@ -1290,6 +1317,9 @@ impl<'a> InferenceContext<'a> {
}
}
};
+
+ // Handle everything else as unknown. This also handles generic arguments for the method's
+ // parent (impl or trait), which should come after those for the method.
for (id, data) in def_generics.iter().skip(substs.len()) {
match data {
TypeOrConstParamData::TypeParamData(_) => {
@@ -1327,9 +1357,13 @@ impl<'a> InferenceContext<'a> {
CallableDefId::FunctionId(f) => {
if let ItemContainerId::TraitId(trait_) = f.lookup(self.db.upcast()).container {
// construct a TraitRef
- let substs = crate::subst_prefix(
- &*parameters,
- generics(self.db.upcast(), trait_.into()).len(),
+ let params_len = parameters.len(Interner);
+ let trait_params_len = generics(self.db.upcast(), trait_.into()).len();
+ let substs = Substitution::from_iter(
+ Interner,
+ // The generic parameters for the trait come after those for the
+ // function.
+ &parameters.as_slice(Interner)[params_len - trait_params_len..],
);
self.push_obligation(
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: substs }
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 f580e09e9..7a4754cdc 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
@@ -12,8 +12,8 @@ use crate::{
builder::ParamKind,
consteval,
method_resolution::{self, VisibleFromModule},
- GenericArgData, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
- ValueTyDefId,
+ utils::generics,
+ Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId,
};
use super::{ExprOrPatId, InferenceContext, TraitRef};
@@ -96,17 +96,21 @@ impl<'a> InferenceContext<'a> {
ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
};
- let parent_substs = self_subst.unwrap_or_else(|| Substitution::empty(Interner));
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
let substs = ctx.substs_from_path(path, typable, true);
- let mut it = substs.as_slice(Interner)[parent_substs.len(Interner)..].iter().cloned();
- let ty = TyBuilder::value_ty(self.db, typable)
- .use_parent_substs(&parent_substs)
+ let substs = substs.as_slice(Interner);
+ let parent_substs = self_subst.or_else(|| {
+ let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
+ let parent_params_len = generics.parent_generics()?.len();
+ let parent_args = &substs[substs.len() - parent_params_len..];
+ Some(Substitution::from_iter(Interner, parent_args))
+ });
+ let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
+ let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
+ let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
.fill(|x| {
it.next().unwrap_or_else(|| match x {
- ParamKind::Type => {
- GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
- }
+ ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
})
})
@@ -249,7 +253,7 @@ impl<'a> InferenceContext<'a> {
};
let substs = match container {
ItemContainerId::ImplId(impl_id) => {
- let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
+ let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
.fill_with_inference_vars(&mut self.table)
.build();
let impl_self_ty =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index e77b55670..b00e3216b 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
@@ -4,7 +4,7 @@ use std::{fmt, mem, sync::Arc};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy,
- IntTy, NoSolution, TyVariableKind, UniverseIndex,
+ IntTy, TyVariableKind, UniverseIndex,
};
use chalk_solve::infer::ParameterEnaVariableExt;
use ena::unify::UnifyKey;
@@ -331,7 +331,6 @@ impl<'a> InferenceTable<'a> {
&mut resolve::Resolver { table: self, var_stack, fallback },
DebruijnIndex::INNERMOST,
)
- .expect("fold failed unexpectedly")
}
pub(crate) fn resolve_completely<T>(&mut self, t: T) -> T
@@ -452,13 +451,14 @@ impl<'a> InferenceTable<'a> {
f: impl FnOnce(&mut Self) -> T,
) -> T {
use chalk_ir::fold::TypeFolder;
+
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
struct VarFudger<'a, 'b> {
table: &'a mut InferenceTable<'b>,
highest_known_var: InferenceVar,
}
impl<'a, 'b> TypeFolder<Interner> for VarFudger<'a, 'b> {
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -472,24 +472,24 @@ impl<'a> InferenceTable<'a> {
var: chalk_ir::InferenceVar,
kind: TyVariableKind,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Ty<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Ty<Interner> {
+ if var < self.highest_known_var {
var.to_ty(Interner, kind)
} else {
self.table.new_type_var()
- })
+ }
}
fn fold_inference_lifetime(
&mut self,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Lifetime<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Lifetime<Interner> {
+ if var < self.highest_known_var {
var.to_lifetime(Interner)
} else {
self.table.new_lifetime_var()
- })
+ }
}
fn fold_inference_const(
@@ -497,12 +497,12 @@ impl<'a> InferenceTable<'a> {
ty: chalk_ir::Ty<Interner>,
var: chalk_ir::InferenceVar,
_outer_binder: chalk_ir::DebruijnIndex,
- ) -> chalk_ir::Fallible<chalk_ir::Const<Interner>> {
- Ok(if var < self.highest_known_var {
+ ) -> chalk_ir::Const<Interner> {
+ if var < self.highest_known_var {
var.to_const(Interner, ty)
} else {
self.table.new_const_var(ty)
- })
+ }
}
}
@@ -512,7 +512,6 @@ impl<'a> InferenceTable<'a> {
self.rollback_to(snapshot);
result
.fold_with(&mut VarFudger { table: self, highest_known_var }, DebruijnIndex::INNERMOST)
- .expect("fold_with with VarFudger")
}
/// This checks whether any of the free variables in the `canonicalized`
@@ -598,11 +597,14 @@ impl<'a> InferenceTable<'a> {
.build();
let projection = {
- let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type);
+ let b = TyBuilder::subst_for_def(self.db, fn_once_trait, None);
if b.remaining() != 2 {
return None;
}
- b.push(ty.clone()).push(arg_ty).build()
+ let fn_once_subst = b.push(ty.clone()).push(arg_ty).build();
+
+ TyBuilder::assoc_type_projection(self.db, output_assoc_type, Some(fn_once_subst))
+ .build()
};
let trait_env = self.trait_env.env.clone();
@@ -636,21 +638,24 @@ mod resolve {
use chalk_ir::{
cast::Cast,
fold::{TypeFoldable, TypeFolder},
- Fallible, NoSolution,
};
use hir_def::type_ref::ConstScalar;
- pub(super) struct Resolver<'a, 'b, F> {
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ pub(super) struct Resolver<
+ 'a,
+ 'b,
+ F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
+ > {
pub(super) table: &'a mut InferenceTable<'b>,
pub(super) var_stack: &'a mut Vec<InferenceVar>,
pub(super) fallback: F,
}
- impl<'a, 'b, 'i, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
+ impl<'a, 'b, F> TypeFolder<Interner> for Resolver<'a, 'b, F>
where
- F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg + 'i,
+ F: Fn(InferenceVar, VariableKind, GenericArg, DebruijnIndex) -> GenericArg,
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -664,20 +669,19 @@ mod resolve {
var: InferenceVar,
kind: TyVariableKind,
outer_binder: DebruijnIndex,
- ) -> Fallible<Ty> {
+ ) -> Ty {
let var = self.table.var_unification_table.inference_var_root(var);
if self.var_stack.contains(&var) {
// recursive type
let default = self.table.fallback_value(var, kind).cast(Interner);
- return Ok((self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Ty(kind), default, outer_binder)
.assert_ty_ref(Interner)
- .clone());
+ .clone();
}
let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
// known_ty may contain other variables that are known by now
self.var_stack.push(var);
- let result =
- known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+ let result = known_ty.fold_with(self, outer_binder);
self.var_stack.pop();
result.assert_ty_ref(Interner).clone()
} else {
@@ -686,7 +690,7 @@ mod resolve {
.assert_ty_ref(Interner)
.clone()
};
- Ok(result)
+ result
}
fn fold_inference_const(
@@ -694,7 +698,7 @@ mod resolve {
ty: Ty,
var: InferenceVar,
outer_binder: DebruijnIndex,
- ) -> Fallible<Const> {
+ ) -> Const {
let var = self.table.var_unification_table.inference_var_root(var);
let default = ConstData {
ty: ty.clone(),
@@ -704,35 +708,33 @@ mod resolve {
.cast(Interner);
if self.var_stack.contains(&var) {
// recursive
- return Ok((self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
+ return (self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
.assert_const_ref(Interner)
- .clone());
+ .clone();
}
- let result = if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
+ if let Some(known_ty) = self.table.var_unification_table.probe_var(var) {
// known_ty may contain other variables that are known by now
self.var_stack.push(var);
- let result =
- known_ty.fold_with(self, outer_binder).expect("fold failed unexpectedly");
+ let result = known_ty.fold_with(self, outer_binder);
self.var_stack.pop();
result.assert_const_ref(Interner).clone()
} else {
(self.fallback)(var, VariableKind::Const(ty), default, outer_binder)
.assert_const_ref(Interner)
.clone()
- };
- Ok(result)
+ }
}
fn fold_inference_lifetime(
&mut self,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
- ) -> Fallible<Lifetime> {
+ ) -> Lifetime {
// fall back all lifetimes to 'static -- currently we don't deal
// with any lifetimes, but we can sometimes get some lifetime
// variables through Chalk's unification, and this at least makes
// sure we don't leak them outside of inference
- Ok(crate::static_lifetime())
+ crate::static_lifetime()
}
}
}
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 a82a331d4..c4b700cbc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -196,20 +196,6 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
make_binders_with_count(db, usize::MAX, generics, value)
}
-// FIXME: get rid of this
-pub fn make_canonical<T: HasInterner<Interner = Interner>>(
- value: T,
- kinds: impl IntoIterator<Item = TyVariableKind>,
-) -> Canonical<T> {
- let kinds = kinds.into_iter().map(|tk| {
- chalk_ir::CanonicalVarKind::new(
- chalk_ir::VariableKind::Ty(tk),
- chalk_ir::UniverseIndex::ROOT,
- )
- });
- Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
-}
-
// FIXME: get rid of this, just replace it by FnPointer
/// A function signature as seen by type inference: Several parameter types and
/// one return type.
@@ -268,13 +254,13 @@ impl CallableSig {
}
impl TypeFoldable<Interner> for CallableSig {
- fn fold_with<E>(
+ fn try_fold_with<E>(
self,
- folder: &mut dyn chalk_ir::fold::TypeFolder<Interner, Error = E>,
+ folder: &mut dyn chalk_ir::fold::FallibleTypeFolder<Interner, Error = E>,
outer_binder: DebruijnIndex,
) -> Result<Self, E> {
let vec = self.params_and_return.to_vec();
- let folded = vec.fold_with(folder, outer_binder)?;
+ let folded = vec.try_fold_with(folder, outer_binder)?;
Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs })
}
}
@@ -306,16 +292,19 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
for_ty: impl FnMut(BoundVar, DebruijnIndex) -> Ty,
for_const: impl FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
) -> T {
- use chalk_ir::{fold::TypeFolder, Fallible};
- struct FreeVarFolder<F1, F2>(F1, F2);
+ use chalk_ir::fold::TypeFolder;
+
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ struct FreeVarFolder<
+ F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
+ F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
+ >(F1, F2);
impl<
- 'i,
- F1: FnMut(BoundVar, DebruijnIndex) -> Ty + 'i,
- F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const + 'i,
+ F1: FnMut(BoundVar, DebruijnIndex) -> Ty,
+ F2: FnMut(Ty, BoundVar, DebruijnIndex) -> Const,
> TypeFolder<Interner> for FreeVarFolder<F1, F2>
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -324,12 +313,8 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
Interner
}
- fn fold_free_var_ty(
- &mut self,
- bound_var: BoundVar,
- outer_binder: DebruijnIndex,
- ) -> Fallible<Ty> {
- Ok(self.0(bound_var, outer_binder))
+ fn fold_free_var_ty(&mut self, bound_var: BoundVar, outer_binder: DebruijnIndex) -> Ty {
+ self.0(bound_var, outer_binder)
}
fn fold_free_var_const(
@@ -337,12 +322,11 @@ pub(crate) fn fold_free_vars<T: HasInterner<Interner = Interner> + TypeFoldable<
ty: Ty,
bound_var: BoundVar,
outer_binder: DebruijnIndex,
- ) -> Fallible<Const> {
- Ok(self.1(ty, bound_var, outer_binder))
+ ) -> Const {
+ self.1(ty, bound_var, outer_binder)
}
}
t.fold_with(&mut FreeVarFolder(for_ty, for_const), DebruijnIndex::INNERMOST)
- .expect("fold failed unexpectedly")
}
pub(crate) fn fold_tys<T: HasInterner<Interner = Interner> + TypeFoldable<Interner>>(
@@ -365,16 +349,13 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
f: impl FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>,
binders: DebruijnIndex,
) -> T {
- use chalk_ir::{
- fold::{TypeFolder, TypeSuperFoldable},
- Fallible,
- };
- struct TyFolder<F>(F);
- impl<'i, F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const> + 'i>
- TypeFolder<Interner> for TyFolder<F>
+ use chalk_ir::fold::{TypeFolder, TypeSuperFoldable};
+ #[derive(chalk_derive::FallibleTypeFolder)]
+ #[has_interner(Interner)]
+ struct TyFolder<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>>(F);
+ impl<F: FnMut(Either<Ty, Const>, DebruijnIndex) -> Either<Ty, Const>> TypeFolder<Interner>
+ for TyFolder<F>
{
- type Error = NoSolution;
-
fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
self
}
@@ -383,16 +364,16 @@ pub(crate) fn fold_tys_and_consts<T: HasInterner<Interner = Interner> + TypeFold
Interner
}
- fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
- let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
- Ok(self.0(Either::Left(ty), outer_binder).left().unwrap())
+ fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Ty {
+ let ty = ty.super_fold_with(self.as_dyn(), outer_binder);
+ self.0(Either::Left(ty), outer_binder).left().unwrap()
}
- fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Fallible<Const> {
- Ok(self.0(Either::Right(c), outer_binder).right().unwrap())
+ fn fold_const(&mut self, c: Const, outer_binder: DebruijnIndex) -> Const {
+ self.0(Either::Right(c), outer_binder).right().unwrap()
}
}
- t.fold_with(&mut TyFolder(f), binders).expect("fold failed unexpectedly")
+ t.fold_with(&mut TyFolder(f), binders)
}
/// 'Canonicalizes' the `t` by replacing any errors with new variables. Also
@@ -404,16 +385,16 @@ where
T: HasInterner<Interner = Interner>,
{
use chalk_ir::{
- fold::{TypeFolder, TypeSuperFoldable},
+ fold::{FallibleTypeFolder, TypeSuperFoldable},
Fallible,
};
struct ErrorReplacer {
vars: usize,
}
- impl TypeFolder<Interner> for ErrorReplacer {
+ impl FallibleTypeFolder<Interner> for ErrorReplacer {
type Error = NoSolution;
- fn as_dyn(&mut self) -> &mut dyn TypeFolder<Interner, Error = Self::Error> {
+ fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
self
}
@@ -421,18 +402,17 @@ where
Interner
}
- fn fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
+ fn try_fold_ty(&mut self, ty: Ty, outer_binder: DebruijnIndex) -> Fallible<Ty> {
if let TyKind::Error = ty.kind(Interner) {
let index = self.vars;
self.vars += 1;
Ok(TyKind::BoundVar(BoundVar::new(outer_binder, index)).intern(Interner))
} else {
- let ty = ty.super_fold_with(self.as_dyn(), outer_binder)?;
- Ok(ty)
+ ty.try_super_fold_with(self.as_dyn(), outer_binder)
}
}
- fn fold_inference_ty(
+ fn try_fold_inference_ty(
&mut self,
_var: InferenceVar,
_kind: TyVariableKind,
@@ -447,7 +427,7 @@ where
}
}
- fn fold_free_var_ty(
+ fn try_fold_free_var_ty(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
@@ -461,7 +441,7 @@ where
}
}
- fn fold_inference_const(
+ fn try_fold_inference_const(
&mut self,
ty: Ty,
_var: InferenceVar,
@@ -474,7 +454,7 @@ where
}
}
- fn fold_free_var_const(
+ fn try_fold_free_var_const(
&mut self,
ty: Ty,
_bound_var: BoundVar,
@@ -487,7 +467,7 @@ where
}
}
- fn fold_inference_lifetime(
+ fn try_fold_inference_lifetime(
&mut self,
_var: InferenceVar,
_outer_binder: DebruijnIndex,
@@ -499,7 +479,7 @@ where
}
}
- fn fold_free_var_lifetime(
+ fn try_fold_free_var_lifetime(
&mut self,
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
@@ -512,7 +492,7 @@ where
}
}
let mut error_replacer = ErrorReplacer { vars: 0 };
- let value = match t.clone().fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
+ let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) {
Ok(t) => t,
Err(_) => panic!("Encountered unbound or inference vars in {:?}", t),
};
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 532544fee..223d705b1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -306,7 +306,7 @@ impl<'a> TyLoweringContext<'a> {
// FIXME we're probably doing something wrong here
self.impl_trait_counter.set(idx + count_impl_traits(type_ref) as u16);
let (
- parent_params,
+ _parent_params,
self_params,
list_params,
const_params,
@@ -319,7 +319,7 @@ impl<'a> TyLoweringContext<'a> {
};
TyKind::BoundVar(BoundVar::new(
self.in_binders,
- idx as usize + parent_params + self_params + list_params + const_params,
+ idx as usize + self_params + list_params + const_params,
))
.intern(Interner)
}
@@ -499,14 +499,31 @@ impl<'a> TyLoweringContext<'a> {
.intern(Interner)
}
TypeNs::SelfType(impl_id) => {
- let generics = generics(self.db.upcast(), impl_id.into());
- let substs = match self.type_param_mode {
- ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
+ let def =
+ self.resolver.generic_def().expect("impl should have generic param scope");
+ let generics = generics(self.db.upcast(), def);
+
+ match self.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ // `def` can be either impl itself or item within, and we need impl itself
+ // now.
+ let generics = generics.parent_generics().unwrap_or(&generics);
+ let subst = generics.placeholder_subst(self.db);
+ self.db.impl_self_ty(impl_id).substitute(Interner, &subst)
+ }
ParamLoweringMode::Variable => {
- generics.bound_vars_subst(self.db, self.in_binders)
+ let starting_from = match def {
+ GenericDefId::ImplId(_) => 0,
+ // `def` is an item within impl. We need to substitute `BoundVar`s but
+ // remember that they are for parent (i.e. impl) generic params so they
+ // come after our own params.
+ _ => generics.len_self(),
+ };
+ TyBuilder::impl_self_ty(self.db, impl_id)
+ .fill_with_bound_vars(self.in_binders, starting_from)
+ .build()
}
- };
- self.db.impl_self_ty(impl_id).substitute(Interner, &substs)
+ }
}
TypeNs::AdtSelfType(adt) => {
let generics = generics(self.db.upcast(), adt.into());
@@ -663,40 +680,31 @@ impl<'a> TyLoweringContext<'a> {
fn substs_from_path_segment(
&self,
segment: PathSegment<'_>,
- def_generic: Option<GenericDefId>,
+ def: Option<GenericDefId>,
infer_args: bool,
explicit_self_ty: Option<Ty>,
) -> Substitution {
+ // Remember that the item's own generic args come before its parent's.
let mut substs = Vec::new();
- let def_generics = if let Some(def) = def_generic {
- generics(self.db.upcast(), def)
+ let def = if let Some(d) = def {
+ d
} else {
return Substitution::empty(Interner);
};
+ let def_generics = generics(self.db.upcast(), def);
let (parent_params, self_params, type_params, const_params, impl_trait_params) =
def_generics.provenance_split();
- let total_len =
- parent_params + self_params + type_params + const_params + impl_trait_params;
+ let item_len = self_params + type_params + const_params + impl_trait_params;
+ let total_len = parent_params + item_len;
- let ty_error = GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner);
+ let ty_error = TyKind::Error.intern(Interner).cast(Interner);
let mut def_generic_iter = def_generics.iter_id();
- for _ in 0..parent_params {
- if let Some(eid) = def_generic_iter.next() {
- match eid {
- Either::Left(_) => substs.push(ty_error.clone()),
- Either::Right(x) => {
- substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
- }
- }
- }
- }
-
let fill_self_params = || {
for x in explicit_self_ty
.into_iter()
- .map(|x| GenericArgData::Ty(x).intern(Interner))
+ .map(|x| x.cast(Interner))
.chain(iter::repeat(ty_error.clone()))
.take(self_params)
{
@@ -757,37 +765,40 @@ impl<'a> TyLoweringContext<'a> {
fill_self_params();
}
+ // These params include those of parent.
+ let remaining_params: SmallVec<[_; 2]> = def_generic_iter
+ .map(|eid| match eid {
+ Either::Left(_) => ty_error.clone(),
+ Either::Right(x) => unknown_const_as_generic(self.db.const_param_ty(x)),
+ })
+ .collect();
+ assert_eq!(remaining_params.len() + substs.len(), total_len);
+
// handle defaults. In expression or pattern path segments without
// explicitly specified type arguments, missing type arguments are inferred
// (i.e. defaults aren't used).
if !infer_args || had_explicit_args {
- if let Some(def_generic) = def_generic {
- let defaults = self.db.generic_defaults(def_generic);
- assert_eq!(total_len, defaults.len());
-
- for default_ty in defaults.iter().skip(substs.len()) {
- // each default can depend on the previous parameters
- let substs_so_far = Substitution::from_iter(Interner, substs.clone());
- if let Some(_id) = def_generic_iter.next() {
- substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
- }
- }
+ let defaults = self.db.generic_defaults(def);
+ assert_eq!(total_len, defaults.len());
+ let parent_from = item_len - substs.len();
+
+ for (idx, default_ty) in defaults[substs.len()..item_len].iter().enumerate() {
+ // each default can depend on the previous parameters
+ let substs_so_far = Substitution::from_iter(
+ Interner,
+ substs.iter().cloned().chain(remaining_params[idx..].iter().cloned()),
+ );
+ substs.push(default_ty.clone().substitute(Interner, &substs_so_far));
}
- }
- // add placeholders for args that were not provided
- // FIXME: emit diagnostics in contexts where this is not allowed
- for eid in def_generic_iter {
- match eid {
- Either::Left(_) => substs.push(ty_error.clone()),
- Either::Right(x) => {
- substs.push(unknown_const_as_generic(self.db.const_param_ty(x)))
- }
- }
+ // Keep parent's params as unknown.
+ let mut remaining_params = remaining_params;
+ substs.extend(remaining_params.drain(parent_from..));
+ } else {
+ substs.extend(remaining_params);
}
- // If this assert fails, it means you pushed into subst but didn't call .next() of def_generic_iter
- assert_eq!(substs.len(), total_len);
+ assert_eq!(substs.len(), total_len);
Substitution::from_iter(Interner, substs)
}
@@ -981,10 +992,11 @@ impl<'a> TyLoweringContext<'a> {
fn lower_dyn_trait(&self, bounds: &[Interned<TypeBound>]) -> Ty {
let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner);
- // INVARIANT: The principal trait bound must come first. Others may be in any order but
- // should be in the same order for the same set but possibly different order of bounds in
- // the input.
- // This invariant is used by `TyExt::dyn_trait()` and chalk.
+ // INVARIANT: The principal trait bound, if present, must come first. Others may be in any
+ // order but should be in the same order for the same set but possibly different order of
+ // bounds in the input.
+ // INVARIANT: If this function returns `DynTy`, there should be at least one trait bound.
+ // These invariants are utilized by `TyExt::dyn_trait()` and chalk.
let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
let mut bounds: Vec<_> = bounds
.iter()
@@ -1035,6 +1047,12 @@ impl<'a> TyLoweringContext<'a> {
return None;
}
+ if bounds.first().and_then(|b| b.trait_id()).is_none() {
+ // When there's no trait bound, that's an error. This happens when the trait refs
+ // are unresolved.
+ return None;
+ }
+
// As multiple occurrences of the same auto traits *are* permitted, we dedulicate the
// bounds. We shouldn't have repeated elements besides auto traits at this point.
bounds.dedup();
@@ -1046,7 +1064,8 @@ impl<'a> TyLoweringContext<'a> {
let bounds = crate::make_single_type_binders(bounds);
TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner)
} else {
- // FIXME: report error (additional non-auto traits or associated type rebound)
+ // FIXME: report error
+ // (additional non-auto traits, associated type rebound, or no resolved trait)
TyKind::Error.intern(Interner)
}
}
@@ -1139,11 +1158,28 @@ fn named_associated_type_shorthand_candidates<R>(
};
match res {
- TypeNs::SelfType(impl_id) => search(
+ TypeNs::SelfType(impl_id) => {
// we're _in_ the impl -- the binders get added back later. Correct,
// but it would be nice to make this more explicit
- db.impl_trait(impl_id)?.into_value_and_skipped_binders().0,
- ),
+ let trait_ref = db.impl_trait(impl_id)?.into_value_and_skipped_binders().0;
+
+ let impl_id_as_generic_def: GenericDefId = impl_id.into();
+ if impl_id_as_generic_def != def {
+ // `trait_ref` contains `BoundVar`s bound by impl's `Binders`, but here we need
+ // `BoundVar`s from `def`'s point of view.
+ // FIXME: A `HirDatabase` query may be handy if this process is needed in more
+ // places. It'd be almost identical as `impl_trait_query` where `resolver` would be
+ // of `def` instead of `impl_id`.
+ let starting_idx = generics(db.upcast(), def).len_self();
+ let subst = TyBuilder::subst_for_def(db, impl_id, None)
+ .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
+ .build();
+ let trait_ref = subst.apply(trait_ref, Interner);
+ search(trait_ref)
+ } else {
+ search(trait_ref)
+ }
+ }
TypeNs::GenericParam(param_id) => {
let predicates = db.generic_predicates_for_param(def, param_id.into(), assoc_name);
let res = predicates.iter().find_map(|pred| match pred.skip_binders().skip_binders() {
@@ -1160,10 +1196,18 @@ fn named_associated_type_shorthand_candidates<R>(
}
// Handle `Self::Type` referring to own associated type in trait definitions
if let GenericDefId::TraitId(trait_id) = param_id.parent() {
- let generics = generics(db.upcast(), trait_id.into());
- if generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
+ let trait_generics = generics(db.upcast(), trait_id.into());
+ if trait_generics.params.type_or_consts[param_id.local_id()].is_trait_self() {
+ let def_generics = generics(db.upcast(), def);
+ let starting_idx = match def {
+ GenericDefId::TraitId(_) => 0,
+ // `def` is an item within trait. We need to substitute `BoundVar`s but
+ // remember that they are for parent (i.e. trait) generic params so they
+ // come after our own params.
+ _ => def_generics.len_self(),
+ };
let trait_ref = TyBuilder::trait_ref(db, trait_id)
- .fill_with_bound_vars(DebruijnIndex::INNERMOST, 0)
+ .fill_with_bound_vars(DebruijnIndex::INNERMOST, starting_idx)
.build();
return search(trait_ref);
}
@@ -1405,6 +1449,7 @@ pub(crate) fn generic_defaults_query(
let ctx =
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
let generic_params = generics(db.upcast(), def);
+ let parent_start_idx = generic_params.len_self();
let defaults = generic_params
.iter()
@@ -1417,19 +1462,17 @@ pub(crate) fn generic_defaults_query(
let val = unknown_const_as_generic(
db.const_param_ty(ConstParamId::from_unchecked(id)),
);
- return crate::make_binders_with_count(db, idx, &generic_params, val);
+ return make_binders(db, &generic_params, val);
}
};
let mut ty =
p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t));
// Each default can only refer to previous parameters.
- // type variable default referring to parameter coming
- // after it. This is forbidden (FIXME: report
- // diagnostic)
- ty = fallback_bound_vars(ty, idx);
- let val = GenericArgData::Ty(ty).intern(Interner);
- crate::make_binders_with_count(db, idx, &generic_params, val)
+ // Type variable default referring to parameter coming
+ // after it is forbidden (FIXME: report diagnostic)
+ ty = fallback_bound_vars(ty, idx, parent_start_idx);
+ crate::make_binders(db, &generic_params, ty.cast(Interner))
})
.collect();
@@ -1446,15 +1489,14 @@ pub(crate) fn generic_defaults_recover(
// we still need one default per parameter
let defaults = generic_params
.iter_id()
- .enumerate()
- .map(|(count, id)| {
+ .map(|id| {
let val = match id {
itertools::Either::Left(_) => {
GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
}
itertools::Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)),
};
- crate::make_binders_with_count(db, count, &generic_params, val)
+ crate::make_binders(db, &generic_params, val)
})
.collect();
@@ -1633,6 +1675,19 @@ pub enum ValueTyDefId {
}
impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
+impl ValueTyDefId {
+ pub(crate) fn to_generic_def_id(self) -> Option<GenericDefId> {
+ match self {
+ Self::FunctionId(id) => Some(id.into()),
+ Self::StructId(id) => Some(id.into()),
+ Self::UnionId(id) => Some(id.into()),
+ Self::EnumVariantId(var) => Some(var.into()),
+ Self::ConstId(id) => Some(id.into()),
+ Self::StaticId(_) => None,
+ }
+ }
+}
+
/// Build the declared type of an item. This depends on the namespace; e.g. for
/// `struct Foo(usize)`, we have two types: The type of the struct itself, and
/// the constructor function `(usize) -> Foo` which lives in the values
@@ -1816,26 +1871,48 @@ pub(crate) fn const_or_path_to_chalk(
}
}
-/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
-/// num_vars_to_keep) by `TyKind::Unknown`.
+/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
+/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
+/// appears after the generic parameter of `param_index`.
fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
s: T,
- num_vars_to_keep: usize,
+ param_index: usize,
+ parent_start: usize,
) -> T {
+ // Keep in mind that parent generic parameters, if any, come *after* those of the item in
+ // question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
+ // its parent respectively.
+ let is_allowed = |index| {
+ if param_index < parent_start {
+ // The parameter of `param_index` is one from the item in question. Any parent generic
+ // parameters or the item's generic parameters that come before `param_index` is
+ // allowed.
+ // [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
+ // ^^^^^^ ^^^^^^^^^^ these are allowed
+ !(param_index..parent_start).contains(&index)
+ } else {
+ // The parameter of `param_index` is one from the parent generics. Only parent generic
+ // parameters that come before `param_index` are allowed.
+ // [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
+ // ^^^^^^ these are allowed
+ (parent_start..param_index).contains(&index)
+ }
+ };
+
crate::fold_free_vars(
s,
|bound, binders| {
- if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
- TyKind::Error.intern(Interner)
- } else {
+ if bound.index_if_innermost().map_or(true, is_allowed) {
bound.shifted_in_from(binders).to_ty(Interner)
+ } else {
+ TyKind::Error.intern(Interner)
}
},
|ty, bound, binders| {
- if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
- unknown_const(ty.clone())
- } else {
+ if bound.index_if_innermost().map_or(true, is_allowed) {
bound.shifted_in_from(binders).to_const(Interner, ty)
+ } else {
+ unknown_const(ty.clone())
}
},
)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
index d765fee0e..f80fb39c1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
@@ -103,6 +103,18 @@ impl From<crate::db::InternedClosureId> for chalk_ir::ClosureId<Interner> {
}
}
+impl From<chalk_ir::GeneratorId<Interner>> for crate::db::InternedGeneratorId {
+ fn from(id: chalk_ir::GeneratorId<Interner>) -> Self {
+ Self::from_intern_id(id.0)
+ }
+}
+
+impl From<crate::db::InternedGeneratorId> for chalk_ir::GeneratorId<Interner> {
+ fn from(id: crate::db::InternedGeneratorId) -> Self {
+ chalk_ir::GeneratorId(id.as_intern_id())
+ }
+}
+
pub fn to_foreign_def_id(id: TypeAliasId) -> ForeignDefId {
chalk_ir::ForeignDefId(salsa::InternKey::as_intern_id(&id))
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
index 9a63d5013..3a1a3f4fd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs
@@ -1,7 +1,7 @@
//! This module is concerned with finding methods that a given type provides.
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
-//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
+//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::{iter, ops::ControlFlow, sync::Arc};
use arrayvec::ArrayVec;
@@ -654,7 +654,7 @@ fn find_matching_impl(
let r = table.run_in_snapshot(|table| {
let impl_data = db.impl_data(impl_);
let substs =
- TyBuilder::subst_for_def(db, impl_).fill_with_inference_vars(table).build();
+ TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs);
table
@@ -914,22 +914,10 @@ fn iterate_trait_method_candidates(
let db = table.db;
let env = table.trait_env.clone();
let self_is_array = matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..));
- // if ty is `dyn Trait`, the trait doesn't need to be in scope
- let inherent_trait =
- self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
- let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
- // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
- .then(|| {
- env.traits_in_scope_from_clauses(self_ty.clone())
- .flat_map(|t| all_super_traits(db.upcast(), t))
- })
- .into_iter()
- .flatten();
- let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied());
let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
- 'traits: for t in traits {
+ 'traits: for &t in traits_in_scope {
let data = db.trait_data(t);
// Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
@@ -979,6 +967,44 @@ fn iterate_inherent_methods(
) -> ControlFlow<()> {
let db = table.db;
let env = table.trait_env.clone();
+
+ // For trait object types and placeholder types with trait bounds, the methods of the trait and
+ // its super traits are considered inherent methods. This matters because these methods have
+ // higher priority than the other traits' methods, which would be considered in
+ // `iterate_trait_method_candidates()` only after this function.
+ match self_ty.kind(Interner) {
+ TyKind::Placeholder(_) => {
+ let env = table.trait_env.clone();
+ let traits = env
+ .traits_in_scope_from_clauses(self_ty.clone())
+ .flat_map(|t| all_super_traits(db.upcast(), t));
+ iterate_inherent_trait_methods(
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ callback,
+ traits,
+ )?;
+ }
+ TyKind::Dyn(_) => {
+ if let Some(principal_trait) = self_ty.dyn_trait() {
+ let traits = all_super_traits(db.upcast(), principal_trait);
+ iterate_inherent_trait_methods(
+ self_ty,
+ table,
+ name,
+ receiver_ty,
+ receiver_adjustments.clone(),
+ callback,
+ traits.into_iter(),
+ )?;
+ }
+ }
+ _ => {}
+ }
+
let def_crates = match def_crates(db, self_ty, env.krate) {
Some(k) => k,
None => return ControlFlow::Continue(()),
@@ -1020,6 +1046,28 @@ fn iterate_inherent_methods(
}
return ControlFlow::Continue(());
+ fn iterate_inherent_trait_methods(
+ self_ty: &Ty,
+ table: &mut InferenceTable<'_>,
+ name: Option<&Name>,
+ receiver_ty: Option<&Ty>,
+ receiver_adjustments: Option<ReceiverAdjustments>,
+ callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
+ traits: impl Iterator<Item = TraitId>,
+ ) -> ControlFlow<()> {
+ let db = table.db;
+ for t in traits {
+ let data = db.trait_data(t);
+ for &(_, item) in data.items.iter() {
+ // We don't pass `visible_from_module` as all trait items should be visible.
+ if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
+ callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
+ }
+ }
+ }
+ ControlFlow::Continue(())
+ }
+
fn impls_for_self_ty(
impls: &InherentImpls,
self_ty: &Ty,
@@ -1099,10 +1147,9 @@ fn is_valid_candidate(
}));
if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
let self_ty_matches = table.run_in_snapshot(|table| {
- let subst =
- TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
- let expected_self_ty =
- subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
+ let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id)
+ .fill_with_inference_vars(table)
+ .build();
table.unify(&expected_self_ty, &self_ty)
});
if !self_ty_matches {
@@ -1138,31 +1185,26 @@ fn is_valid_fn_candidate(
table.run_in_snapshot(|table| {
let container = fn_id.lookup(db.upcast()).container;
- let impl_subst = match container {
+ let (impl_subst, expect_self_ty) = match container {
ItemContainerId::ImplId(it) => {
- TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
+ let subst =
+ TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+ let self_ty = db.impl_self_ty(it).substitute(Interner, &subst);
+ (subst, self_ty)
}
ItemContainerId::TraitId(it) => {
- TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build()
+ let subst =
+ TyBuilder::subst_for_def(db, it, None).fill_with_inference_vars(table).build();
+ let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone();
+ (subst, self_ty)
}
_ => unreachable!(),
};
- let fn_subst = TyBuilder::subst_for_def(db, fn_id)
- .use_parent_substs(&impl_subst)
+ let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone()))
.fill_with_inference_vars(table)
.build();
- let expect_self_ty = match container {
- ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(),
- ItemContainerId::ImplId(impl_id) => {
- fn_subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
- }
- // We should only get called for associated items (impl/trait)
- ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
- unreachable!()
- }
- };
check_that!(table.unify(&expect_self_ty, self_ty));
if let Some(receiver_ty) = receiver_ty {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index d2f13e435..ebbc54101 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -16,7 +16,7 @@ use base_db::{fixture::WithFixture, FileRange, SourceDatabaseExt};
use expect_test::Expect;
use hir_def::{
body::{Body, BodySourceMap, SyntheticSyntax},
- db::DefDatabase,
+ db::{DefDatabase, InternDatabase},
expr::{ExprId, PatId},
item_scope::ItemScope,
nameres::DefMap,
@@ -135,6 +135,10 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::VariantId(it) => {
+ let loc = db.lookup_intern_enum(it.parent);
+ loc.source(&db).value.syntax().text_range().start()
+ }
});
let mut unexpected_type_mismatches = String::new();
for def in defs {
@@ -388,6 +392,10 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
let loc = it.lookup(&db);
loc.source(&db).value.syntax().text_range().start()
}
+ DefWithBodyId::VariantId(it) => {
+ let loc = db.lookup_intern_enum(it.parent);
+ loc.source(&db).value.syntax().text_range().start()
+ }
});
for def in defs {
let (_body, source_map) = db.body_with_source_map(def);
@@ -453,6 +461,18 @@ fn visit_module(
let body = db.body(def);
visit_body(db, &body, cb);
}
+ ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => {
+ db.enum_data(it)
+ .variants
+ .iter()
+ .map(|(id, _)| hir_def::EnumVariantId { parent: it, local_id: id })
+ .for_each(|it| {
+ let def = it.into();
+ cb(def);
+ let body = db.body(def);
+ visit_body(db, &body, cb);
+ });
+ }
ModuleDefId::TraitId(it) => {
let trait_data = db.trait_data(it);
for &(_, item) in trait_data.items.iter() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
index bf59fadc2..d301595bc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs
@@ -295,6 +295,24 @@ fn foo() {
}
#[test]
+fn generator_yield_return_coerce() {
+ check_no_mismatches(
+ r#"
+fn test() {
+ let g = || {
+ yield &1u32;
+ yield &&1u32;
+ if true {
+ return &1u32;
+ }
+ &&1u32
+ };
+}
+ "#,
+ );
+}
+
+#[test]
fn assign_coerce() {
check_no_mismatches(
r"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
index 240942e48..8a8ff08cf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs
@@ -56,6 +56,28 @@ fn main() {
}
#[test]
+fn render_dyn_ty_independent_of_order() {
+ check_types_source_code(
+ r#"
+auto trait Send {}
+trait A {
+ type Assoc;
+}
+trait B: A {}
+
+fn test(
+ _: &(dyn A<Assoc = ()> + Send),
+ //^ &(dyn A<Assoc = ()> + Send)
+ _: &(dyn Send + A<Assoc = ()>),
+ //^ &(dyn A<Assoc = ()> + Send)
+ _: &dyn B<Assoc = ()>,
+ //^ &(dyn B<Assoc = ()>)
+) {}
+ "#,
+ );
+}
+
+#[test]
fn render_dyn_for_ty() {
// FIXME
check_types_source_code(
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 81588a7c4..ac8edb841 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1219,6 +1219,40 @@ fn main() {
}
#[test]
+fn dyn_trait_method_priority() {
+ check_types(
+ r#"
+//- minicore: from
+trait Trait {
+ fn into(&self) -> usize { 0 }
+}
+
+fn foo(a: &dyn Trait) {
+ let _ = a.into();
+ //^usize
+}
+ "#,
+ );
+}
+
+#[test]
+fn trait_method_priority_for_placeholder_type() {
+ check_types(
+ r#"
+//- minicore: from
+trait Trait {
+ fn into(&self) -> usize { 0 }
+}
+
+fn foo<T: Trait>(a: &T) {
+ let _ = a.into();
+ //^usize
+}
+ "#,
+ );
+}
+
+#[test]
fn autoderef_visibility_field() {
check(
r#"
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index eb04bf877..74de33117 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -1070,3 +1070,13 @@ fn main() {
"#,
);
}
+
+#[test]
+fn cfg_params() {
+ check_types(
+ r#"
+fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
+ //^^^ u32
+"#,
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 23e51a9c1..a155adcec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -1488,7 +1488,6 @@ fn regression_11688_4() {
#[test]
fn gat_crash_1() {
- cov_mark::check!(ignore_gats);
check_no_mismatches(
r#"
trait ATrait {}
@@ -1527,30 +1526,22 @@ unsafe impl Storage for InlineStorage {
#[test]
fn gat_crash_3() {
- // FIXME: This test currently crashes rust analyzer in a debug build but not in a
- // release build (i.e. for the user). With the assumption that tests will always be run
- // in debug mode, we catch the unwind and expect that it panicked. See the
- // [`crate::utils::generics`] function for more information.
- cov_mark::check!(ignore_gats);
- std::panic::catch_unwind(|| {
- check_no_mismatches(
- r#"
+ check_no_mismatches(
+ r#"
trait Collection {
- type Item;
- type Member<T>: Collection<Item = T>;
- fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
+type Item;
+type Member<T>: Collection<Item = T>;
+fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
}
struct ConstGen<T, const N: usize> {
- data: [T; N],
+data: [T; N],
}
impl<T, const N: usize> Collection for ConstGen<T, N> {
- type Item = T;
- type Member<U> = ConstGen<U, N>;
+type Item = T;
+type Member<U> = ConstGen<U, N>;
}
- "#,
- );
- })
- .expect_err("must panic");
+ "#,
+ );
}
#[test]
@@ -1691,3 +1682,28 @@ fn macrostmts() -> u8 {
"#,
);
}
+
+#[test]
+fn dyn_with_unresolved_trait() {
+ check_types(
+ r#"
+fn foo(a: &dyn DoesNotExist) {
+ a.bar();
+ //^&{unknown}
+}
+ "#,
+ );
+}
+
+#[test]
+fn self_assoc_with_const_generics_crash() {
+ check_no_mismatches(
+ r#"
+trait Trait { type Item; }
+impl<T, const N: usize> Trait for [T; N] {
+ type Item = ();
+ fn f<U>(_: Self::Item) {}
+}
+ "#,
+ );
+}
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 4ea103e5d..080e2ac1b 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
@@ -1693,16 +1693,16 @@ fn infer_type_param() {
fn infer_const() {
check_infer(
r#"
- struct Foo;
- impl Foo { const ASSOC_CONST: u32 = 0; }
- const GLOBAL_CONST: u32 = 101;
- fn test() {
- const LOCAL_CONST: u32 = 99;
- let x = LOCAL_CONST;
- let z = GLOBAL_CONST;
- let id = Foo::ASSOC_CONST;
- }
- "#,
+struct Foo;
+impl Foo { const ASSOC_CONST: u32 = 0; }
+const GLOBAL_CONST: u32 = 101;
+fn test() {
+ const LOCAL_CONST: u32 = 99;
+ let x = LOCAL_CONST;
+ let z = GLOBAL_CONST;
+ let id = Foo::ASSOC_CONST;
+}
+"#,
expect![[r#"
48..49 '0': u32
79..82 '101': u32
@@ -1722,17 +1722,17 @@ fn infer_const() {
fn infer_static() {
check_infer(
r#"
- static GLOBAL_STATIC: u32 = 101;
- static mut GLOBAL_STATIC_MUT: u32 = 101;
- fn test() {
- static LOCAL_STATIC: u32 = 99;
- static mut LOCAL_STATIC_MUT: u32 = 99;
- let x = LOCAL_STATIC;
- let y = LOCAL_STATIC_MUT;
- let z = GLOBAL_STATIC;
- let w = GLOBAL_STATIC_MUT;
- }
- "#,
+static GLOBAL_STATIC: u32 = 101;
+static mut GLOBAL_STATIC_MUT: u32 = 101;
+fn test() {
+ static LOCAL_STATIC: u32 = 99;
+ static mut LOCAL_STATIC_MUT: u32 = 99;
+ let x = LOCAL_STATIC;
+ let y = LOCAL_STATIC_MUT;
+ let z = GLOBAL_STATIC;
+ let w = GLOBAL_STATIC_MUT;
+}
+"#,
expect![[r#"
28..31 '101': u32
69..72 '101': u32
@@ -1752,6 +1752,41 @@ fn infer_static() {
}
#[test]
+fn infer_enum_variant() {
+ check_infer(
+ r#"
+enum Foo {
+ A = 15,
+ B = Foo::A as isize + 1
+}
+"#,
+ expect![[r#"
+ 19..21 '15': isize
+ 31..37 'Foo::A': Foo
+ 31..46 'Foo::A as isize': isize
+ 31..50 'Foo::A...ze + 1': isize
+ 49..50 '1': isize
+ "#]],
+ );
+ check_infer(
+ r#"
+#[repr(u32)]
+enum Foo {
+ A = 15,
+ B = Foo::A as u32 + 1
+}
+"#,
+ expect![[r#"
+ 32..34 '15': u32
+ 44..50 'Foo::A': Foo
+ 44..57 'Foo::A as u32': u32
+ 44..61 'Foo::A...32 + 1': u32
+ 60..61 '1': u32
+ "#]],
+ );
+}
+
+#[test]
fn shadowing_primitive() {
check_types(
r#"
@@ -1918,6 +1953,88 @@ fn closure_return_inferred() {
}
#[test]
+fn generator_types_inferred() {
+ check_infer(
+ r#"
+//- minicore: generator, deref
+use core::ops::{Generator, GeneratorState};
+use core::pin::Pin;
+
+fn f(v: i64) {}
+fn test() {
+ let mut g = |r| {
+ let a = yield 0;
+ let a = yield 1;
+ let a = yield 2;
+ "return value"
+ };
+
+ match Pin::new(&mut g).resume(0usize) {
+ GeneratorState::Yielded(y) => { f(y); }
+ GeneratorState::Complete(r) => {}
+ }
+}
+ "#,
+ expect![[r#"
+ 70..71 'v': i64
+ 78..80 '{}': ()
+ 91..362 '{ ... } }': ()
+ 101..106 'mut g': |usize| yields i64 -> &str
+ 109..218 '|r| { ... }': |usize| yields i64 -> &str
+ 110..111 'r': usize
+ 113..218 '{ ... }': &str
+ 127..128 'a': usize
+ 131..138 'yield 0': usize
+ 137..138 '0': i64
+ 152..153 'a': usize
+ 156..163 'yield 1': usize
+ 162..163 '1': i64
+ 177..178 'a': usize
+ 181..188 'yield 2': usize
+ 187..188 '2': i64
+ 198..212 '"return value"': &str
+ 225..360 'match ... }': ()
+ 231..239 'Pin::new': fn new<&mut |usize| yields i64 -> &str>(&mut |usize| yields i64 -> &str) -> Pin<&mut |usize| yields i64 -> &str>
+ 231..247 'Pin::n...mut g)': Pin<&mut |usize| yields i64 -> &str>
+ 231..262 'Pin::n...usize)': GeneratorState<i64, &str>
+ 240..246 '&mut g': &mut |usize| yields i64 -> &str
+ 245..246 'g': |usize| yields i64 -> &str
+ 255..261 '0usize': usize
+ 273..299 'Genera...ded(y)': GeneratorState<i64, &str>
+ 297..298 'y': i64
+ 303..312 '{ f(y); }': ()
+ 305..306 'f': fn f(i64)
+ 305..309 'f(y)': ()
+ 307..308 'y': i64
+ 321..348 'Genera...ete(r)': GeneratorState<i64, &str>
+ 346..347 'r': &str
+ 352..354 '{}': ()
+ "#]],
+ );
+}
+
+#[test]
+fn generator_resume_yield_return_unit() {
+ check_no_mismatches(
+ r#"
+//- minicore: generator, deref
+use core::ops::{Generator, GeneratorState};
+use core::pin::Pin;
+fn test() {
+ let mut g = || {
+ let () = yield;
+ };
+
+ match Pin::new(&mut g).resume(()) {
+ GeneratorState::Yielded(()) => {}
+ GeneratorState::Complete(()) => {}
+ }
+}
+ "#,
+ );
+}
+
+#[test]
fn fn_pointer_return() {
check_infer(
r#"
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 21a863197..555b6972f 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
@@ -279,6 +279,10 @@ fn test() {
pub mod iter {
pub trait IntoIterator {
type Item;
+ type IntoIter: Iterator<Item = Self::Item>;
+ }
+ pub trait Iterator {
+ type Item;
}
}
pub mod prelude {
@@ -297,7 +301,13 @@ pub mod collections {
}
impl<T> IntoIterator for Vec<T> {
- type Item=T;
+ type Item = T;
+ type IntoIter = IntoIter<T>;
+ }
+
+ struct IntoIter<T> {}
+ impl<T> Iterator for IntoIter<T> {
+ type Item = T;
}
}
"#,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
index 77afeb321..c425f35ac 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs
@@ -1,6 +1,6 @@
//! Trait solving using Chalk.
-use std::env::var;
+use std::{env::var, sync::Arc};
use chalk_ir::GoalData;
use chalk_recursive::Cache;
@@ -12,8 +12,9 @@ use stdx::panic_context;
use syntax::SmolStr;
use crate::{
- db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
- Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
+ db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
+ Guidance, InEnvironment, Interner, ProjectionTy, ProjectionTyExt, Solution, TraitRefExt, Ty,
+ TyKind, WhereClause,
};
/// This controls how much 'time' we give the Chalk solver before giving up.
@@ -64,6 +65,16 @@ impl TraitEnvironment {
}
}
+pub(crate) fn normalize_projection_query(
+ db: &dyn HirDatabase,
+ projection: ProjectionTy,
+ env: Arc<TraitEnvironment>,
+) -> Ty {
+ let mut table = InferenceTable::new(db, env);
+ let ty = table.normalize_projection_ty(projection);
+ table.resolve_completely(ty)
+}
+
/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
@@ -84,7 +95,7 @@ pub(crate) fn trait_solve_query(
..
}))) = &goal.value.goal.data(Interner)
{
- if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(Interner).kind(Interner) {
+ if let TyKind::BoundVar(_) = projection_ty.self_type_parameter(db).kind(Interner) {
// Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
return Some(Solution::Ambig(Guidance::Unknown));
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index d6638db02..e54bcb421 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
@@ -4,7 +4,7 @@
use std::iter;
use base_db::CrateId;
-use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex};
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar, DebruijnIndex};
use hir_def::{
db::DefDatabase,
generics::{
@@ -24,8 +24,7 @@ use smallvec::{smallvec, SmallVec};
use syntax::SmolStr;
use crate::{
- db::HirDatabase, ChalkTraitId, ConstData, ConstValue, GenericArgData, Interner, Substitution,
- TraitRef, TraitRefExt, TyKind, WhereClause,
+ db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, WhereClause,
};
pub(crate) fn fn_traits(db: &dyn DefDatabase, krate: CrateId) -> impl Iterator<Item = TraitId> {
@@ -174,31 +173,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
- if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
- let params = db.generic_params(def);
- let parent_params = &parent_generics.as_ref().unwrap().params;
- let has_consts =
- params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
- let parent_has_consts =
- parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
- return if has_consts || parent_has_consts {
- // XXX: treat const generic associated types as not existing to avoid crashes
- // (#11769)
- //
- // Note: Also crashes when the parent has const generics (also even if the GAT
- // doesn't use them), see `tests::regression::gat_crash_3` for an example.
- // Avoids that by disabling GATs when the parent (i.e. `impl` block) has
- // const generics (#12193).
- //
- // Chalk expects the inner associated type's parameters to come
- // *before*, not after the trait's generics as we've always done it.
- // Adapting to this requires a larger refactoring
- cov_mark::hit!(ignore_gats);
- Generics { def, params: Interned::new(Default::default()), parent_generics }
- } else {
- Generics { def, params, parent_generics }
- };
- }
Generics { def, params: db.generic_params(def), parent_generics }
}
@@ -221,23 +195,30 @@ impl Generics {
})
}
- /// Iterator over types and const params of parent, then self.
+ /// Iterator over types and const params of self, then parent.
pub(crate) fn iter<'a>(
&'a self,
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
let to_toc_id = |it: &'a Generics| {
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
};
- self.parent_generics()
- .into_iter()
- .flat_map(move |it| it.params.iter().map(to_toc_id(it)))
- .chain(self.params.iter().map(to_toc_id(self)))
+ self.params.iter().map(to_toc_id(self)).chain(self.iter_parent())
+ }
+
+ /// Iterate over types and const params without parent params.
+ pub(crate) fn iter_self<'a>(
+ &'a self,
+ ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
+ let to_toc_id = |it: &'a Generics| {
+ move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p)
+ };
+ self.params.iter().map(to_toc_id(self))
}
/// Iterator over types and const params of parent.
pub(crate) fn iter_parent<'a>(
&'a self,
- ) -> impl Iterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
+ ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a {
self.parent_generics().into_iter().flat_map(|it| {
let to_toc_id =
move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p);
@@ -245,12 +226,18 @@ impl Generics {
})
}
+ /// Returns total number of generic parameters in scope, including those from parent.
pub(crate) fn len(&self) -> usize {
let parent = self.parent_generics().map_or(0, Generics::len);
let child = self.params.type_or_consts.len();
parent + child
}
+ /// Returns numbers of generic parameters excluding those from parent.
+ pub(crate) fn len_self(&self) -> usize {
+ self.params.type_or_consts.len()
+ }
+
/// (parent total, self param, type param list, const param list, impl trait)
pub(crate) fn provenance_split(&self) -> (usize, usize, usize, usize, usize) {
let ty_iter = || self.params.iter().filter_map(|x| x.1.type_param());
@@ -275,15 +262,17 @@ impl Generics {
if param.parent == self.def {
let (idx, (_local_id, data)) =
self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
- let parent_len = self.parent_generics().map_or(0, Generics::len);
- Some((parent_len + idx, data))
+ Some((idx, data))
} else {
- self.parent_generics().and_then(|g| g.find_param(param))
+ self.parent_generics()
+ .and_then(|g| g.find_param(param))
+ // Remember that parent parameters come after parameters for self.
+ .map(|(idx, data)| (self.len_self() + idx, data))
}
}
- fn parent_generics(&self) -> Option<&Generics> {
- self.parent_generics.as_ref().map(|it| &**it)
+ pub(crate) fn parent_generics(&self) -> Option<&Generics> {
+ self.parent_generics.as_deref()
}
/// Returns a Substitution that replaces each parameter by a bound variable.
@@ -295,18 +284,10 @@ impl Generics {
Substitution::from_iter(
Interner,
self.iter_id().enumerate().map(|(idx, id)| match id {
- Either::Left(_) => GenericArgData::Ty(
- TyKind::BoundVar(BoundVar::new(debruijn, idx)).intern(Interner),
- )
- .intern(Interner),
- Either::Right(id) => GenericArgData::Const(
- ConstData {
- value: ConstValue::BoundVar(BoundVar::new(debruijn, idx)),
- ty: db.const_param_ty(id),
- }
- .intern(Interner),
- )
- .intern(Interner),
+ Either::Left(_) => BoundVar::new(debruijn, idx).to_ty(Interner).cast(Interner),
+ Either::Right(id) => BoundVar::new(debruijn, idx)
+ .to_const(Interner, db.const_param_ty(id))
+ .cast(Interner),
}),
)
}
@@ -316,18 +297,12 @@ impl Generics {
Substitution::from_iter(
Interner,
self.iter_id().map(|id| match id {
- Either::Left(id) => GenericArgData::Ty(
- TyKind::Placeholder(crate::to_placeholder_idx(db, id.into())).intern(Interner),
- )
- .intern(Interner),
- Either::Right(id) => GenericArgData::Const(
- ConstData {
- value: ConstValue::Placeholder(crate::to_placeholder_idx(db, id.into())),
- ty: db.const_param_ty(id),
- }
- .intern(Interner),
- )
- .intern(Interner),
+ Either::Left(id) => {
+ crate::to_placeholder_idx(db, id.into()).to_ty(Interner).cast(Interner)
+ }
+ Either::Right(id) => crate::to_placeholder_idx(db, id.into())
+ .to_const(Interner, db.const_param_ty(id))
+ .cast(Interner),
}),
)
}
diff --git a/src/tools/rust-analyzer/crates/hir/Cargo.toml b/src/tools/rust-analyzer/crates/hir/Cargo.toml
index 8e6a2441b..e1418de3c 100644
--- a/src/tools/rust-analyzer/crates/hir/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/hir/Cargo.toml
@@ -13,9 +13,9 @@ doctest = false
rustc-hash = "1.1.0"
either = "1.7.0"
arrayvec = "0.7.2"
-itertools = "0.10.3"
-smallvec = "1.9.0"
-once_cell = "1.12.0"
+itertools = "0.10.5"
+smallvec = "1.10.0"
+once_cell = "1.15.0"
stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index 5edc16d8b..c5dc60f1e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -1,7 +1,7 @@
//! Re-export diagnostics such that clients of `hir` don't have to depend on
//! low-level crates.
//!
-//! This probably isn't the best way to do this -- ideally, diagnistics should
+//! This probably isn't the best way to do this -- ideally, diagnostics should
//! be expressed in terms of hir types themselves.
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs
index 0e29c52ad..27b2f445d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/display.rs
@@ -492,6 +492,9 @@ impl HirDisplay for TypeAlias {
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
let data = f.db.type_alias_data(self.id);
write!(f, "type {}", data.name)?;
+ let def_id = GenericDefId::TypeAliasId(self.id);
+ write_generic_params(def_id, f)?;
+ write_where_clause(def_id, f)?;
if !data.bounds.is_empty() {
f.write_str(": ")?;
f.write_joined(&data.bounds, " + ")?;
diff --git a/src/tools/rust-analyzer/crates/hir/src/from_id.rs b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
index 9c7558d19..f825a72c0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs
@@ -140,6 +140,7 @@ impl From<DefWithBody> for DefWithBodyId {
DefWithBody::Function(it) => DefWithBodyId::FunctionId(it.id),
DefWithBody::Static(it) => DefWithBodyId::StaticId(it.id),
DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id),
+ DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()),
}
}
}
@@ -150,6 +151,7 @@ impl From<DefWithBodyId> for DefWithBody {
DefWithBodyId::FunctionId(it) => DefWithBody::Function(it.into()),
DefWithBodyId::StaticId(it) => DefWithBody::Static(it.into()),
DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()),
+ DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()),
}
}
}
@@ -172,9 +174,7 @@ impl From<GenericDef> for GenericDefId {
GenericDef::Trait(it) => GenericDefId::TraitId(it.id),
GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id),
GenericDef::Impl(it) => GenericDefId::ImplId(it.id),
- GenericDef::Variant(it) => {
- GenericDefId::EnumVariantId(EnumVariantId { parent: it.parent.id, local_id: it.id })
- }
+ GenericDef::Variant(it) => GenericDefId::EnumVariantId(it.into()),
GenericDef::Const(it) => GenericDefId::ConstId(it.id),
}
}
@@ -188,9 +188,7 @@ impl From<GenericDefId> for GenericDef {
GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
GenericDefId::ImplId(it) => GenericDef::Impl(it.into()),
- GenericDefId::EnumVariantId(it) => {
- GenericDef::Variant(Variant { parent: it.parent.into(), id: it.local_id })
- }
+ GenericDefId::EnumVariantId(it) => GenericDef::Variant(it.into()),
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index e4bb63a86..f5324208c 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -39,7 +39,7 @@ use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
use either::Either;
use hir_def::{
- adt::{ReprKind, VariantData},
+ adt::{ReprData, VariantData},
body::{BodyDiagnostic, SyntheticSyntax},
expr::{BindingAnnotation, LabelId, Pat, PatId},
generics::{TypeOrConstParamData, TypeParamProvenance},
@@ -50,7 +50,7 @@ use hir_def::{
resolver::{HasResolver, Resolver},
src::HasSource as _,
AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId,
- FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
+ EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId,
LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId,
TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
};
@@ -61,12 +61,10 @@ use hir_ty::{
diagnostics::BodyValidationDiagnostic,
method_resolution::{self, TyFingerprint},
primitive::UintTy,
- subst_prefix,
traits::FnTrait,
- AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
- ClosureId, DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind,
- QuantifiedWhereClause, Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty,
- TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind, WhereClause,
+ AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
+ GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
+ TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
@@ -74,7 +72,7 @@ use once_cell::unsync::Lazy;
use rustc_hash::FxHashSet;
use stdx::{impl_from, never};
use syntax::{
- ast::{self, HasAttrs as _, HasDocComments, HasName},
+ ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
};
@@ -349,7 +347,10 @@ impl ModuleDef {
ModuleDef::Module(it) => it.id.into(),
ModuleDef::Const(it) => it.id.into(),
ModuleDef::Static(it) => it.id.into(),
- _ => return Vec::new(),
+ ModuleDef::Variant(it) => {
+ EnumVariantId { parent: it.parent.into(), local_id: it.id }.into()
+ }
+ ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(),
};
let module = match self.module(db) {
@@ -378,10 +379,10 @@ impl ModuleDef {
ModuleDef::Function(it) => Some(it.into()),
ModuleDef::Const(it) => Some(it.into()),
ModuleDef::Static(it) => Some(it.into()),
+ ModuleDef::Variant(it) => Some(it.into()),
ModuleDef::Module(_)
| ModuleDef::Adt(_)
- | ModuleDef::Variant(_)
| ModuleDef::Trait(_)
| ModuleDef::TypeAlias(_)
| ModuleDef::Macro(_)
@@ -538,6 +539,30 @@ impl Module {
}
acc.extend(decl.diagnostics(db))
}
+ ModuleDef::Adt(adt) => {
+ match adt {
+ Adt::Struct(s) => {
+ for diag in db.struct_data_with_diagnostics(s.id).1.iter() {
+ emit_def_diagnostic(db, acc, diag);
+ }
+ }
+ Adt::Union(u) => {
+ for diag in db.union_data_with_diagnostics(u.id).1.iter() {
+ emit_def_diagnostic(db, acc, diag);
+ }
+ }
+ Adt::Enum(e) => {
+ for v in e.variants(db) {
+ acc.extend(ModuleDef::Variant(v).diagnostics(db));
+ }
+
+ for diag in db.enum_data_with_diagnostics(e.id).1.iter() {
+ emit_def_diagnostic(db, acc, diag);
+ }
+ }
+ }
+ acc.extend(decl.diagnostics(db))
+ }
_ => acc.extend(decl.diagnostics(db)),
}
}
@@ -582,8 +607,13 @@ impl Module {
/// Finds a path that can be used to refer to the given item from within
/// this module, if possible.
- pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> {
- hir_def::find_path::find_path(db, item.into().into(), self.into())
+ pub fn find_use_path(
+ self,
+ db: &dyn DefDatabase,
+ item: impl Into<ItemInNs>,
+ prefer_no_std: bool,
+ ) -> Option<ModPath> {
+ hir_def::find_path::find_path(db, item.into().into(), self.into(), prefer_no_std)
}
/// Finds a path that can be used to refer to the given item from within
@@ -593,8 +623,15 @@ impl Module {
db: &dyn DefDatabase,
item: impl Into<ItemInNs>,
prefix_kind: PrefixKind,
+ prefer_no_std: bool,
) -> Option<ModPath> {
- hir_def::find_path::find_path_prefixed(db, item.into().into(), self.into(), prefix_kind)
+ hir_def::find_path::find_path_prefixed(
+ db,
+ item.into().into(),
+ self.into(),
+ prefix_kind,
+ prefer_no_std,
+ )
}
}
@@ -863,7 +900,7 @@ impl Struct {
Type::from_def(db, self.id)
}
- pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
+ pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprData> {
db.struct_data(self.id).repr.clone()
}
@@ -941,6 +978,21 @@ impl Enum {
pub fn ty(self, db: &dyn HirDatabase) -> Type {
Type::from_def(db, self.id)
}
+
+ /// The type of the enum variant bodies.
+ pub fn variant_body_ty(self, db: &dyn HirDatabase) -> Type {
+ Type::new_for_crate(
+ self.id.lookup(db.upcast()).container.krate(),
+ TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() {
+ Either::Left(builtin) => hir_def::builtin_type::BuiltinType::Int(builtin),
+ Either::Right(builtin) => hir_def::builtin_type::BuiltinType::Uint(builtin),
+ }),
+ )
+ }
+
+ pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool {
+ self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit))
+ }
}
impl HasVisibility for Enum {
@@ -949,6 +1001,12 @@ impl HasVisibility for Enum {
}
}
+impl From<&Variant> for DefWithBodyId {
+ fn from(&v: &Variant) -> Self {
+ DefWithBodyId::VariantId(v.into())
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Variant {
pub(crate) parent: Enum,
@@ -983,6 +1041,14 @@ impl Variant {
pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
}
+
+ pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> {
+ self.source(db)?.value.expr()
+ }
+
+ pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
+ db.const_eval_variant(self.into())
+ }
}
/// Variants inherit visibility from the parent enum.
@@ -1023,7 +1089,7 @@ impl Adt {
pub fn ty_with_args(self, db: &dyn HirDatabase, args: &[Type]) -> Type {
let id = AdtId::from(self);
let mut it = args.iter().map(|t| t.ty.clone());
- let ty = TyBuilder::def_ty(db, id.into())
+ let ty = TyBuilder::def_ty(db, id.into(), None)
.fill(|x| {
let r = it.next().unwrap_or_else(|| TyKind::Error.intern(Interner));
match x {
@@ -1118,8 +1184,9 @@ pub enum DefWithBody {
Function(Function),
Static(Static),
Const(Const),
+ Variant(Variant),
}
-impl_from!(Function, Const, Static for DefWithBody);
+impl_from!(Function, Const, Static, Variant for DefWithBody);
impl DefWithBody {
pub fn module(self, db: &dyn HirDatabase) -> Module {
@@ -1127,6 +1194,7 @@ impl DefWithBody {
DefWithBody::Const(c) => c.module(db),
DefWithBody::Function(f) => f.module(db),
DefWithBody::Static(s) => s.module(db),
+ DefWithBody::Variant(v) => v.module(db),
}
}
@@ -1135,6 +1203,7 @@ impl DefWithBody {
DefWithBody::Function(f) => Some(f.name(db)),
DefWithBody::Static(s) => Some(s.name(db)),
DefWithBody::Const(c) => c.name(db),
+ DefWithBody::Variant(v) => Some(v.name(db)),
}
}
@@ -1144,6 +1213,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.ret_type(db),
DefWithBody::Static(it) => it.ty(db),
DefWithBody::Const(it) => it.ty(db),
+ DefWithBody::Variant(it) => it.parent.variant_body_ty(db),
}
}
@@ -1152,6 +1222,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.id.into(),
DefWithBody::Static(it) => it.id.into(),
DefWithBody::Const(it) => it.id.into(),
+ DefWithBody::Variant(it) => it.into(),
}
}
@@ -1368,6 +1439,7 @@ impl DefWithBody {
DefWithBody::Function(it) => it.into(),
DefWithBody::Static(it) => it.into(),
DefWithBody::Const(it) => it.into(),
+ DefWithBody::Variant(it) => it.into(),
};
for diag in hir_ty::diagnostics::incorrect_case(db, krate, def.into()) {
acc.push(diag.into())
@@ -2474,7 +2546,7 @@ impl TypeParam {
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_prefix(&subst, local_idx));
+ let ty = ty.substitute(Interner, &subst);
match ty.data(Interner) {
GenericArgData::Ty(x) => Some(Type::new_with_resolver_inner(db, &resolver, x.clone())),
_ => None,
@@ -2728,7 +2800,22 @@ impl Type {
}
fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type {
- let ty = TyBuilder::def_ty(db, def.into()).fill_with_unknown().build();
+ let ty_def = def.into();
+ let parent_subst = match ty_def {
+ TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(id) => {
+ let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
+ Some(subst)
+ }
+ ItemContainerId::ImplId(id) => {
+ let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
+ Some(subst)
+ }
+ _ => None,
+ },
+ _ => None,
+ };
+ let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build();
Type::new(db, def, ty)
}
@@ -2868,7 +2955,11 @@ impl Type {
alias: TypeAlias,
) -> Option<Type> {
let mut args = args.iter();
- let projection = TyBuilder::assoc_type_projection(db, alias.id)
+ let trait_id = match alias.id.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(id) => id,
+ _ => unreachable!("non assoc type alias reached in normalize_trait_assoc_type()"),
+ };
+ let parent_subst = TyBuilder::subst_for_def(db, trait_id, None)
.push(self.ty.clone())
.fill(|x| {
// FIXME: this code is not covered in tests.
@@ -2880,27 +2971,14 @@ impl Type {
}
})
.build();
- let goal = hir_ty::make_canonical(
- InEnvironment::new(
- &self.env.env,
- AliasEq {
- alias: AliasTy::Projection(projection),
- ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
- .intern(Interner),
- }
- .cast(Interner),
- ),
- [TyVariableKind::General].into_iter(),
- );
+ // FIXME: We don't handle GATs yet.
+ let projection = TyBuilder::assoc_type_projection(db, alias.id, Some(parent_subst)).build();
- match db.trait_solve(self.env.krate, goal)? {
- Solution::Unique(s) => s
- .value
- .subst
- .as_slice(Interner)
- .first()
- .map(|ty| self.derived(ty.assert_ty_ref(Interner).clone())),
- Solution::Ambig(_) => None,
+ let ty = db.normalize_projection(projection, self.env.clone());
+ if ty.is_unknown() {
+ None
+ } else {
+ Some(self.derived(ty))
}
}
@@ -2944,7 +3022,7 @@ impl Type {
let adt = adt_id.into();
match adt {
- Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)),
+ Adt::Struct(s) => matches!(s.repr(db), Some(ReprData { packed: true, .. })),
_ => false,
}
}
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 416b6f580..119ec3210 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -257,6 +257,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
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)
@@ -956,6 +961,16 @@ impl<'db> SemanticsImpl<'db> {
)
}
+ 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 }| {
+ self.cache(find_root(&value), file_id);
+ value
+ },
+ )
+ }
+
fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
let root = self.parse_or_expand(src.file_id).unwrap();
let node = src.map(|it| it.to_node(&root));
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index ba9a1cfb6..fa45e3c12 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -115,7 +115,7 @@ pub(super) struct SourceToDefCtx<'a, 'b> {
}
impl SourceToDefCtx<'_, '_> {
- pub(super) fn file_to_def(&mut self, file: FileId) -> SmallVec<[ModuleId; 1]> {
+ pub(super) fn file_to_def(&self, file: FileId) -> SmallVec<[ModuleId; 1]> {
let _p = profile::span("SourceBinder::to_module_def");
let mut mods = SmallVec::new();
for &crate_id in self.db.relevant_crates(file).iter() {
@@ -130,7 +130,7 @@ impl SourceToDefCtx<'_, '_> {
mods
}
- pub(super) fn module_to_def(&mut self, src: InFile<ast::Module>) -> Option<ModuleId> {
+ pub(super) fn module_to_def(&self, src: InFile<ast::Module>) -> Option<ModuleId> {
let _p = profile::span("module_to_def");
let parent_declaration = src
.syntax()
@@ -151,7 +151,7 @@ impl SourceToDefCtx<'_, '_> {
Some(def_map.module_id(child_id))
}
- pub(super) fn source_file_to_def(&mut self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
+ pub(super) fn source_file_to_def(&self, src: InFile<ast::SourceFile>) -> Option<ModuleId> {
let _p = profile::span("source_file_to_def");
let file_id = src.file_id.original_file(self.db.upcast());
self.file_to_def(file_id).get(0).copied()
@@ -384,7 +384,7 @@ impl SourceToDefCtx<'_, '_> {
} else {
let it = ast::Variant::cast(container.value)?;
let def = self.enum_variant_to_def(InFile::new(container.file_id, it))?;
- VariantId::from(def).into()
+ DefWithBodyId::from(def).into()
};
Some(cont)
}
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 342912b67..07bae2b38 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -22,7 +22,7 @@ use hir_def::{
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
AsMacroCall, AssocItemId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, LocalFieldId,
- Lookup, ModuleDefId, VariantId,
+ Lookup, ModuleDefId, TraitId, VariantId,
};
use hir_expand::{
builtin_fn_macro::BuiltinFnLikeExpander,
@@ -302,10 +302,15 @@ impl SourceAnalyzer {
}
}
+ let future_trait = db
+ .lang_item(self.resolver.krate(), hir_expand::name![future_trait].to_smol_str())?
+ .as_trait()?;
let poll_fn = db
.lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())?
.as_function()?;
- let substs = hir_ty::TyBuilder::subst_for_def(db, poll_fn).push(ty.clone()).build();
+ // HACK: subst for `poll()` coincides with that for `Future` because `poll()` itself
+ // doesn't have any generic parameters, so we skip building another subst for `poll()`.
+ let substs = hir_ty::TyBuilder::subst_for_def(db, future_trait, None).push(ty).build();
Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs))
}
@@ -321,8 +326,10 @@ impl SourceAnalyzer {
};
let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?;
- let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
- let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
+ let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
+ // HACK: subst for all methods coincides with that for their trait because the methods
+ // don't have any generic parameters, so we skip building another subst for the methods.
+ let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
}
@@ -337,8 +344,10 @@ impl SourceAnalyzer {
let lang_item_name = name![index];
- let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
- let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn)
+ let (op_trait, op_fn) = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
+ // HACK: subst for all methods coincides with that for their trait because the methods
+ // don't have any generic parameters, so we skip building another subst for the methods.
+ let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None)
.push(base_ty.clone())
.push(index_ty.clone())
.build();
@@ -354,10 +363,14 @@ impl SourceAnalyzer {
let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?;
let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?;
- let op_fn = lang_names_for_bin_op(op)
+ let (op_trait, op_fn) = lang_names_for_bin_op(op)
.and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?;
- let substs =
- hir_ty::TyBuilder::subst_for_def(db, op_fn).push(lhs.clone()).push(rhs.clone()).build();
+ // HACK: subst for `index()` coincides with that for `Index` because `index()` itself
+ // doesn't have any generic parameters, so we skip building another subst for `index()`.
+ let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None)
+ .push(lhs.clone())
+ .push(rhs.clone())
+ .build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
}
@@ -371,7 +384,13 @@ impl SourceAnalyzer {
let op_fn =
db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?;
- let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
+ let op_trait = match op_fn.lookup(db.upcast()).container {
+ ItemContainerId::TraitId(id) => id,
+ _ => return None,
+ };
+ // HACK: subst for `branch()` coincides with that for `Try` because `branch()` itself
+ // doesn't have any generic parameters, so we skip building another subst for `branch()`.
+ let substs = hir_ty::TyBuilder::subst_for_def(db, op_trait, None).push(ty.clone()).build();
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
}
@@ -799,9 +818,10 @@ impl SourceAnalyzer {
db: &dyn HirDatabase,
lang_trait: &Name,
method_name: &Name,
- ) -> Option<FunctionId> {
- db.trait_data(db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?)
- .method_by_name(method_name)
+ ) -> Option<(TraitId, FunctionId)> {
+ let trait_id = db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?;
+ let fn_id = db.trait_data(trait_id).method_by_name(method_name)?;
+ Some((trait_id, fn_id))
}
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 616a406c7..fd78decda 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -244,6 +244,10 @@ impl<'a> SymbolCollector<'a> {
DefWithBodyId::ConstId(id) => Some(
id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(),
),
+ DefWithBodyId::VariantId(id) => Some({
+ let db = self.db.upcast();
+ id.parent.lookup(db).source(db).value.name()?.text().into()
+ }),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
index fca09d384..57a41f3d9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
+itertools = "0.10.5"
either = "1.7.0"
stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
index d4d148c77..60d1588a4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs
@@ -13,4 +13,5 @@ pub struct AssistConfig {
pub snippet_cap: Option<SnippetCap>,
pub allowed: Option<Vec<AssistKind>>,
pub insert_use: InsertUseConfig,
+ pub prefer_no_std: bool,
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
index 1a7919a5a..73f4db4e5 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs
@@ -87,7 +87,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
.into_iter()
.filter_map(|variant| {
Some((
- build_pat(ctx.db(), module, variant)?,
+ build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)?,
variant.should_be_hidden(ctx.db(), module.krate()),
))
})
@@ -132,8 +132,9 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>)
let is_hidden = variants
.iter()
.any(|variant| variant.should_be_hidden(ctx.db(), module.krate()));
- let patterns =
- variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
+ let patterns = variants.into_iter().filter_map(|variant| {
+ build_pat(ctx.db(), module, variant, ctx.config.prefer_no_std)
+ });
(ast::Pat::from(make::tuple_pat(patterns)), is_hidden)
})
@@ -349,10 +350,16 @@ fn resolve_tuple_of_enum_def(
.collect()
}
-fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
+fn build_pat(
+ db: &RootDatabase,
+ module: hir::Module,
+ var: ExtendedVariant,
+ prefer_no_std: bool,
+) -> Option<ast::Pat> {
match var {
ExtendedVariant::Variant(var) => {
- let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
+ let path =
+ mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var), prefer_no_std)?);
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
let pat: ast::Pat = match var.source(db)?.value.kind() {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
index 949cf3167..678dc877d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/auto_import.rs
@@ -89,8 +89,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
// ```
pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
- let mut proposed_imports =
- import_assets.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind);
+ let mut proposed_imports = import_assets.search_for_imports(
+ &ctx.sema,
+ ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
+ );
if proposed_imports.is_empty() {
return None;
}
@@ -153,6 +156,8 @@ pub(super) fn find_importable_node(
{
ImportAssets::for_method_call(&method_under_caret, &ctx.sema)
.zip(Some(method_under_caret.syntax().clone().into()))
+ } else if let Some(_) = ctx.find_node_at_offset_with_descend::<ast::Param>() {
+ None
} else if let Some(pat) = ctx
.find_node_at_offset_with_descend::<ast::IdentPat>()
.filter(ast::IdentPat::is_simple_ident)
@@ -266,6 +271,20 @@ mod tests {
}
#[test]
+ fn ignore_parameter_name() {
+ check_assist_not_applicable(
+ auto_import,
+ r"
+ mod foo {
+ pub mod bar {}
+ }
+
+ fn foo(bar$0: &str) {}
+ ",
+ );
+ }
+
+ #[test]
fn prefer_shorter_paths() {
let before = r"
//- /main.rs crate:main deps:foo,bar
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
index 30f6dd41a..95d11abe8 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -50,7 +50,7 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) -
_ => return None,
};
- mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def)?)
+ mod_path_to_ast(&module.find_use_path(ctx.db(), src_type_def, ctx.config.prefer_no_std)?)
};
let dest_type = match &ast_trait {
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
new file mode 100644
index 000000000..8d11e0bac
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -0,0 +1,822 @@
+use either::Either;
+use ide_db::defs::Definition;
+use itertools::Itertools;
+use syntax::{
+ ast::{self, AstNode, HasGenericParams, HasVisibility},
+ match_ast, SyntaxKind, SyntaxNode,
+};
+
+use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: convert_named_struct_to_tuple_struct
+//
+// Converts struct with named fields to tuple struct, and analogously for enum variants with named
+// fields.
+//
+// ```
+// struct Point$0 { x: f32, y: f32 }
+//
+// impl Point {
+// pub fn new(x: f32, y: f32) -> Self {
+// Point { x, y }
+// }
+//
+// pub fn x(&self) -> f32 {
+// self.x
+// }
+//
+// pub fn y(&self) -> f32 {
+// self.y
+// }
+// }
+// ```
+// ->
+// ```
+// struct Point(f32, f32);
+//
+// impl Point {
+// pub fn new(x: f32, y: f32) -> Self {
+// Point(x, y)
+// }
+//
+// pub fn x(&self) -> f32 {
+// self.0
+// }
+//
+// pub fn y(&self) -> f32 {
+// self.1
+// }
+// }
+// ```
+pub(crate) fn convert_named_struct_to_tuple_struct(
+ acc: &mut Assists,
+ ctx: &AssistContext<'_>,
+) -> Option<()> {
+ let strukt = ctx
+ .find_node_at_offset::<ast::Struct>()
+ .map(Either::Left)
+ .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
+ let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
+ let record_fields = match field_list {
+ ast::FieldList::RecordFieldList(it) => it,
+ ast::FieldList::TupleFieldList(_) => return None,
+ };
+ let strukt_def = match &strukt {
+ Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
+ Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
+ };
+ let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
+
+ acc.add(
+ AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
+ "Convert to tuple struct",
+ target,
+ |edit| {
+ edit_field_references(ctx, edit, record_fields.fields());
+ edit_struct_references(ctx, edit, strukt_def);
+ edit_struct_def(ctx, edit, &strukt, record_fields);
+ },
+ )
+}
+
+fn edit_struct_def(
+ ctx: &AssistContext<'_>,
+ edit: &mut SourceChangeBuilder,
+ strukt: &Either<ast::Struct, ast::Variant>,
+ record_fields: ast::RecordFieldList,
+) {
+ let tuple_fields = record_fields
+ .fields()
+ .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
+ let tuple_fields = ast::make::tuple_field_list(tuple_fields);
+ let record_fields_text_range = record_fields.syntax().text_range();
+
+ edit.edit_file(ctx.file_id());
+ edit.replace(record_fields_text_range, tuple_fields.syntax().text());
+
+ if let Either::Left(strukt) = strukt {
+ if let Some(w) = strukt.where_clause() {
+ let mut where_clause = w.to_string();
+ if where_clause.ends_with(',') {
+ where_clause.pop();
+ }
+ where_clause.push(';');
+
+ edit.delete(w.syntax().text_range());
+ edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
+ edit.insert(record_fields_text_range.end(), where_clause);
+ edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
+
+ if let Some(tok) = strukt
+ .generic_param_list()
+ .and_then(|l| l.r_angle_token())
+ .and_then(|tok| tok.next_token())
+ .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
+ {
+ edit.delete(tok.text_range());
+ }
+ } else {
+ edit.insert(record_fields_text_range.end(), ";");
+ }
+ }
+
+ if let Some(tok) = record_fields
+ .l_curly_token()
+ .and_then(|tok| tok.prev_token())
+ .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
+ {
+ edit.delete(tok.text_range())
+ }
+}
+
+fn edit_struct_references(
+ ctx: &AssistContext<'_>,
+ edit: &mut SourceChangeBuilder,
+ strukt: Either<hir::Struct, hir::Variant>,
+) {
+ let strukt_def = match strukt {
+ Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
+ Either::Right(v) => Definition::Variant(v),
+ };
+ let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
+
+ let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
+ match_ast! {
+ match node {
+ ast::RecordPat(record_struct_pat) => {
+ edit.replace(
+ record_struct_pat.syntax().text_range(),
+ ast::make::tuple_struct_pat(
+ record_struct_pat.path()?,
+ record_struct_pat
+ .record_pat_field_list()?
+ .fields()
+ .filter_map(|pat| pat.pat())
+ )
+ .to_string()
+ );
+ },
+ ast::RecordExpr(record_expr) => {
+ let path = record_expr.path()?;
+ let args = record_expr
+ .record_expr_field_list()?
+ .fields()
+ .filter_map(|f| f.expr())
+ .join(", ");
+
+ edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
+ },
+ _ => return None,
+ }
+ }
+ Some(())
+ };
+
+ for (file_id, refs) in usages {
+ edit.edit_file(file_id);
+ for r in refs {
+ for node in r.name.syntax().ancestors() {
+ if edit_node(edit, node).is_some() {
+ break;
+ }
+ }
+ }
+ }
+}
+
+fn edit_field_references(
+ ctx: &AssistContext<'_>,
+ edit: &mut SourceChangeBuilder,
+ fields: impl Iterator<Item = ast::RecordField>,
+) {
+ for (index, field) in fields.enumerate() {
+ let field = match ctx.sema.to_def(&field) {
+ Some(it) => it,
+ None => continue,
+ };
+ let def = Definition::Field(field);
+ let usages = def.usages(&ctx.sema).all();
+ for (file_id, refs) in usages {
+ edit.edit_file(file_id);
+ for r in refs {
+ if let Some(name_ref) = r.name.as_name_ref() {
+ // Only edit the field reference if it's part of a `.field` access
+ if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
+ edit.replace(name_ref.syntax().text_range(), index.to_string());
+ }
+ }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn not_applicable_other_than_record_struct() {
+ check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0(u32)"#);
+ check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0;"#);
+ }
+
+ #[test]
+ fn convert_simple_struct() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Inner;
+struct A$0 { inner: Inner }
+
+impl A {
+ fn new(inner: Inner) -> A {
+ A { inner }
+ }
+
+ fn new_with_default() -> A {
+ A::new(Inner)
+ }
+
+ fn into_inner(self) -> Inner {
+ self.inner
+ }
+}"#,
+ r#"
+struct Inner;
+struct A(Inner);
+
+impl A {
+ fn new(inner: Inner) -> A {
+ A(inner)
+ }
+
+ fn new_with_default() -> A {
+ A::new(Inner)
+ }
+
+ fn into_inner(self) -> Inner {
+ self.0
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_struct_referenced_via_self_kw() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Inner;
+struct A$0 { inner: Inner }
+
+impl A {
+ fn new(inner: Inner) -> Self {
+ Self { inner }
+ }
+
+ fn new_with_default() -> Self {
+ Self::new(Inner)
+ }
+
+ fn into_inner(self) -> Inner {
+ self.inner
+ }
+}"#,
+ r#"
+struct Inner;
+struct A(Inner);
+
+impl A {
+ fn new(inner: Inner) -> Self {
+ Self(inner)
+ }
+
+ fn new_with_default() -> Self {
+ Self::new(Inner)
+ }
+
+ fn into_inner(self) -> Inner {
+ self.0
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_destructured_struct() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Inner;
+struct A$0 { inner: Inner }
+
+impl A {
+ fn into_inner(self) -> Inner {
+ let A { inner: a } = self;
+ a
+ }
+
+ fn into_inner_via_self(self) -> Inner {
+ let Self { inner } = self;
+ inner
+ }
+}"#,
+ r#"
+struct Inner;
+struct A(Inner);
+
+impl A {
+ fn into_inner(self) -> Inner {
+ let A(a) = self;
+ a
+ }
+
+ fn into_inner_via_self(self) -> Inner {
+ let Self(inner) = self;
+ inner
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_struct_with_visibility() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct A$0 {
+ pub first: u32,
+ pub(crate) second: u64
+}
+
+impl A {
+ fn new() -> A {
+ A { first: 42, second: 42 }
+ }
+
+ fn into_first(self) -> u32 {
+ self.first
+ }
+
+ fn into_second(self) -> u64 {
+ self.second
+ }
+}"#,
+ r#"
+struct A(pub u32, pub(crate) u64);
+
+impl A {
+ fn new() -> A {
+ A(42, 42)
+ }
+
+ fn into_first(self) -> u32 {
+ self.0
+ }
+
+ fn into_second(self) -> u64 {
+ self.1
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_struct_with_wrapped_references() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Inner$0 { uint: u32 }
+struct Outer { inner: Inner }
+
+impl Outer {
+ fn new() -> Self {
+ Self { inner: Inner { uint: 42 } }
+ }
+
+ fn into_inner(self) -> u32 {
+ self.inner.uint
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer { inner: Inner { uint: x } } = self;
+ x
+ }
+}"#,
+ r#"
+struct Inner(u32);
+struct Outer { inner: Inner }
+
+impl Outer {
+ fn new() -> Self {
+ Self { inner: Inner(42) }
+ }
+
+ fn into_inner(self) -> u32 {
+ self.inner.0
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer { inner: Inner(x) } = self;
+ x
+ }
+}"#,
+ );
+
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Inner { uint: u32 }
+struct Outer$0 { inner: Inner }
+
+impl Outer {
+ fn new() -> Self {
+ Self { inner: Inner { uint: 42 } }
+ }
+
+ fn into_inner(self) -> u32 {
+ self.inner.uint
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer { inner: Inner { uint: x } } = self;
+ x
+ }
+}"#,
+ r#"
+struct Inner { uint: u32 }
+struct Outer(Inner);
+
+impl Outer {
+ fn new() -> Self {
+ Self(Inner { uint: 42 })
+ }
+
+ fn into_inner(self) -> u32 {
+ self.0.uint
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer(Inner { uint: x }) = self;
+ x
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_struct_with_multi_file_references() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+//- /main.rs
+struct Inner;
+struct A$0 { inner: Inner }
+
+mod foo;
+
+//- /foo.rs
+use crate::{A, Inner};
+fn f() {
+ let a = A { inner: Inner };
+}
+"#,
+ r#"
+//- /main.rs
+struct Inner;
+struct A(Inner);
+
+mod foo;
+
+//- /foo.rs
+use crate::{A, Inner};
+fn f() {
+ let a = A(Inner);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_struct_with_where_clause() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+struct Wrap$0<T>
+where
+ T: Display,
+{ field1: T }
+"#,
+ r#"
+struct Wrap<T>(T)
+where
+ T: Display;
+
+"#,
+ );
+ }
+
+ #[test]
+ fn not_applicable_other_than_record_variant() {
+ check_assist_not_applicable(
+ convert_named_struct_to_tuple_struct,
+ r#"enum Enum { Variant$0(usize) };"#,
+ );
+ check_assist_not_applicable(
+ convert_named_struct_to_tuple_struct,
+ r#"enum Enum { Variant$0 }"#,
+ );
+ }
+
+ #[test]
+ fn convert_simple_variant() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+enum A {
+ $0Variant { field1: usize },
+}
+
+impl A {
+ fn new(value: usize) -> A {
+ A::Variant { field1: value }
+ }
+
+ fn new_with_default() -> A {
+ A::new(Default::default())
+ }
+
+ fn value(self) -> usize {
+ match self {
+ A::Variant { field1: value } => value,
+ }
+ }
+}"#,
+ r#"
+enum A {
+ Variant(usize),
+}
+
+impl A {
+ fn new(value: usize) -> A {
+ A::Variant(value)
+ }
+
+ fn new_with_default() -> A {
+ A::new(Default::default())
+ }
+
+ fn value(self) -> usize {
+ match self {
+ A::Variant(value) => value,
+ }
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_variant_referenced_via_self_kw() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+enum A {
+ $0Variant { field1: usize },
+}
+
+impl A {
+ fn new(value: usize) -> A {
+ Self::Variant { field1: value }
+ }
+
+ fn new_with_default() -> A {
+ Self::new(Default::default())
+ }
+
+ fn value(self) -> usize {
+ match self {
+ Self::Variant { field1: value } => value,
+ }
+ }
+}"#,
+ r#"
+enum A {
+ Variant(usize),
+}
+
+impl A {
+ fn new(value: usize) -> A {
+ Self::Variant(value)
+ }
+
+ fn new_with_default() -> A {
+ Self::new(Default::default())
+ }
+
+ fn value(self) -> usize {
+ match self {
+ Self::Variant(value) => value,
+ }
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_destructured_variant() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+enum A {
+ $0Variant { field1: usize },
+}
+
+impl A {
+ fn into_inner(self) -> usize {
+ let A::Variant { field1: first } = self;
+ first
+ }
+
+ fn into_inner_via_self(self) -> usize {
+ let Self::Variant { field1: first } = self;
+ first
+ }
+}"#,
+ r#"
+enum A {
+ Variant(usize),
+}
+
+impl A {
+ fn into_inner(self) -> usize {
+ let A::Variant(first) = self;
+ first
+ }
+
+ fn into_inner_via_self(self) -> usize {
+ let Self::Variant(first) = self;
+ first
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_variant_with_wrapped_references() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+enum Inner {
+ $0Variant { field1: usize },
+}
+enum Outer {
+ Variant(Inner),
+}
+
+impl Outer {
+ fn new() -> Self {
+ Self::Variant(Inner::Variant { field1: 42 })
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer::Variant(Inner::Variant { field1: x }) = self;
+ x
+ }
+}"#,
+ r#"
+enum Inner {
+ Variant(usize),
+}
+enum Outer {
+ Variant(Inner),
+}
+
+impl Outer {
+ fn new() -> Self {
+ Self::Variant(Inner::Variant(42))
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer::Variant(Inner::Variant(x)) = self;
+ x
+ }
+}"#,
+ );
+
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+enum Inner {
+ Variant(usize),
+}
+enum Outer {
+ $0Variant { field1: Inner },
+}
+
+impl Outer {
+ fn new() -> Self {
+ Self::Variant { field1: Inner::Variant(42) }
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer::Variant { field1: Inner::Variant(x) } = self;
+ x
+ }
+}"#,
+ r#"
+enum Inner {
+ Variant(usize),
+}
+enum Outer {
+ Variant(Inner),
+}
+
+impl Outer {
+ fn new() -> Self {
+ Self::Variant(Inner::Variant(42))
+ }
+
+ fn into_inner_destructed(self) -> u32 {
+ let Outer::Variant(Inner::Variant(x)) = self;
+ x
+ }
+}"#,
+ );
+ }
+
+ #[test]
+ fn convert_variant_with_multi_file_references() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+//- /main.rs
+struct Inner;
+enum A {
+ $0Variant { field1: Inner },
+}
+
+mod foo;
+
+//- /foo.rs
+use crate::{A, Inner};
+fn f() {
+ let a = A::Variant { field1: Inner };
+}
+"#,
+ r#"
+//- /main.rs
+struct Inner;
+enum A {
+ Variant(Inner),
+}
+
+mod foo;
+
+//- /foo.rs
+use crate::{A, Inner};
+fn f() {
+ let a = A::Variant(Inner);
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn convert_directly_used_variant() {
+ check_assist(
+ convert_named_struct_to_tuple_struct,
+ r#"
+//- /main.rs
+struct Inner;
+enum A {
+ $0Variant { field1: Inner },
+}
+
+mod foo;
+
+//- /foo.rs
+use crate::{A::Variant, Inner};
+fn f() {
+ let a = Variant { field1: Inner };
+}
+"#,
+ r#"
+//- /main.rs
+struct Inner;
+enum A {
+ Variant(Inner),
+}
+
+mod foo;
+
+//- /foo.rs
+use crate::{A::Variant, Inner};
+fn f() {
+ let a = Variant(Inner);
+}
+"#,
+ );
+ }
+}
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 52a55ead3..d6c8ea785 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
@@ -152,6 +152,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
ctx.sema.db,
ModuleDef::from(control_flow_enum),
ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
);
if let Some(mod_path) = mod_path {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index ddc2052e7..970e948df 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -9,7 +9,7 @@ use ide_db::{
search::FileReference,
FxHashSet, RootDatabase,
};
-use itertools::{Itertools, Position};
+use itertools::Itertools;
use syntax::{
ast::{
self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
@@ -298,37 +298,7 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList
let name = variant.name()?;
let ty = generics
.filter(|generics| generics.generic_params().count() > 0)
- .map(|generics| {
- let mut generic_str = String::with_capacity(8);
-
- for (p, more) in generics.generic_params().with_position().map(|p| match p {
- Position::First(p) | Position::Middle(p) => (p, true),
- Position::Last(p) | Position::Only(p) => (p, false),
- }) {
- match p {
- ast::GenericParam::ConstParam(konst) => {
- if let Some(name) = konst.name() {
- generic_str.push_str(name.text().as_str());
- }
- }
- ast::GenericParam::LifetimeParam(lt) => {
- if let Some(lt) = lt.lifetime() {
- generic_str.push_str(lt.text().as_str());
- }
- }
- ast::GenericParam::TypeParam(ty) => {
- if let Some(name) = ty.name() {
- generic_str.push_str(name.text().as_str());
- }
- }
- }
- if more {
- generic_str.push_str(", ");
- }
- }
-
- make::ty(&format!("{}<{}>", &name.text(), &generic_str))
- })
+ .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
.unwrap_or_else(|| make::ty(&name.text()));
// change from a record to a tuple field list
@@ -409,6 +379,7 @@ fn process_references(
ctx.sema.db,
*enum_module_def,
ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
);
if let Some(mut mod_path) = mod_path {
mod_path.pop_segment();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
index eaa6de73e..ccdfcb0d9 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_constant.rs
@@ -77,7 +77,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
target_data_for_generate_constant(ctx, current_module, constant_module).unwrap_or_else(
|| {
let indent = IndentLevel::from_node(statement.syntax());
- (statement.syntax().text_range().start(), indent, None, format!("\n{}", indent))
+ (statement.syntax().text_range().start(), indent, None, format!("\n{indent}"))
},
);
@@ -90,7 +90,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
if let Some(file_id) = file_id {
builder.edit_file(file_id);
}
- builder.insert(offset, format!("{}{}", text, post_string));
+ builder.insert(offset, format!("{text}{post_string}"));
},
)
}
@@ -103,13 +103,13 @@ fn get_text_for_generate_constant(
) -> Option<String> {
let constant_token = not_exist_name_ref.pop()?;
let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
- let mut text = format!("{}const {}: {} = $0;", vis, constant_token, type_name);
+ let mut text = format!("{vis}const {constant_token}: {type_name} = $0;");
while let Some(name_ref) = not_exist_name_ref.pop() {
let vis = if not_exist_name_ref.len() == 0 && !outer_exists { "" } else { "\npub " };
text = text.replace("\n", "\n ");
- text = format!("{}mod {} {{{}\n}}", vis, name_ref.to_string(), text);
+ text = format!("{vis}mod {name_ref} {{{text}\n}}");
}
- Some(text.replace("\n", &format!("\n{}", indent)))
+ Some(text.replace("\n", &format!("\n{indent}")))
}
fn target_data_for_generate_constant(
@@ -134,7 +134,7 @@ fn target_data_for_generate_constant(
.find(|it| it.kind() == SyntaxKind::WHITESPACE && it.to_string().contains("\n"))
.is_some();
let post_string =
- if siblings_has_newline { format!("{}", indent) } else { format!("\n{}", indent) };
+ if siblings_has_newline { format!("{indent}") } else { format!("\n{indent}") };
Some((offset, indent + 1, Some(file_id), post_string))
}
_ => Some((TextSize::from(0), 0.into(), Some(file_id), "\n".into())),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
index 5e9995a98..a6e3d49e0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -55,12 +55,11 @@ pub(crate) fn generate_default_from_enum_variant(
let buf = format!(
r#"
-impl Default for {0} {{
+impl Default for {enum_name} {{
fn default() -> Self {{
- Self::{1}
+ Self::{variant_name}
}}
}}"#,
- enum_name, variant_name
);
edit.insert(start_offset, buf);
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
index cbd33de19..49d9fd707 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
@@ -1,8 +1,7 @@
use ide_db::famous_defs::FamousDefs;
-use itertools::Itertools;
use stdx::format_to;
use syntax::{
- ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl},
+ ast::{self, make, HasGenericParams, HasName, Impl},
AstNode,
};
@@ -77,45 +76,47 @@ pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext<'
)
}
+// FIXME: based on from utils::generate_impl_text_inner
fn generate_trait_impl_text_from_impl(impl_: &ast::Impl, trait_text: &str, code: &str) -> String {
- let generic_params = impl_.generic_param_list();
- let mut buf = String::with_capacity(code.len());
- buf.push_str("\n\n");
- buf.push_str("impl");
-
- if let Some(generic_params) = &generic_params {
- let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
- let toc_params = generic_params.type_or_const_params().map(|toc_param| match toc_param {
- ast::TypeOrConstParam::Type(type_param) => {
- let mut buf = String::new();
- if let Some(it) = type_param.name() {
- format_to!(buf, "{}", it.syntax());
- }
- if let Some(it) = type_param.colon_token() {
- format_to!(buf, "{} ", it);
+ let impl_ty = impl_.self_ty().unwrap();
+ let generic_params = impl_.generic_param_list().map(|generic_params| {
+ let lifetime_params =
+ generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
+ let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+ // remove defaults since they can't be specified in impls
+ match param {
+ ast::TypeOrConstParam::Type(param) => {
+ let param = param.clone_for_update();
+ param.remove_default();
+ Some(ast::GenericParam::TypeParam(param))
}
- if let Some(it) = type_param.type_bound_list() {
- format_to!(buf, "{}", it.syntax());
+ ast::TypeOrConstParam::Const(param) => {
+ let param = param.clone_for_update();
+ param.remove_default();
+ Some(ast::GenericParam::ConstParam(param))
}
- buf
}
- ast::TypeOrConstParam::Const(const_param) => const_param.syntax().to_string(),
});
- let generics = lifetimes.chain(toc_params).format(", ");
- format_to!(buf, "<{}>", generics);
- }
- buf.push(' ');
- buf.push_str(trait_text);
- buf.push_str(" for ");
- buf.push_str(&impl_.self_ty().unwrap().syntax().text().to_string());
+ make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
+ });
+
+ let mut buf = String::with_capacity(code.len());
+ buf.push_str("\n\n");
+
+ // `impl{generic_params} {trait_text} for {impl_.self_ty()}`
+ buf.push_str("impl");
+ if let Some(generic_params) = &generic_params {
+ format_to!(buf, "{generic_params}")
+ }
+ format_to!(buf, " {trait_text} for {impl_ty}");
match impl_.where_clause() {
Some(where_clause) => {
- format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
+ format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
}
None => {
- format_to!(buf, " {{\n{}\n}}", code);
+ format_to!(buf, " {{\n{code}\n}}");
}
}
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 85b193663..ceae80755 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
@@ -51,14 +51,14 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(field) => {
let field_name = field.name()?;
let field_ty = field.ty()?;
- (format!("{}", field_name), field_ty, field.syntax().text_range())
+ (field_name.to_string(), field_ty, field.syntax().text_range())
}
None => {
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
let field_list_index = field_list.fields().position(|it| it == field)?;
let field_ty = field.ty()?;
- (format!("{}", field_list_index), field_ty, field.syntax().text_range())
+ (field_list_index.to_string(), field_ty, field.syntax().text_range())
}
};
@@ -77,7 +77,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
for method in methods {
let adt = ast::Adt::Struct(strukt.clone());
let name = method.name(ctx.db()).to_string();
- let impl_def = find_struct_impl(ctx, &adt, &name).flatten();
+ let impl_def = find_struct_impl(ctx, &adt, &[name]).flatten();
acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate),
@@ -151,7 +151,7 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
Some(cap) => {
let offset = strukt.syntax().text_range().end();
let snippet = render_snippet(cap, impl_def.syntax(), cursor);
- let snippet = format!("\n\n{}", snippet);
+ let snippet = format!("\n\n{snippet}");
builder.insert_snippet(cap, offset, snippet);
}
None => {
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
index b48463512..55b7afb3d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_deref.rs
@@ -58,14 +58,15 @@ fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
- let trait_path = module.find_use_path(ctx.db(), ModuleDef::Trait(trait_))?;
+ let trait_path =
+ module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;
let field_type = field.ty()?;
let field_name = field.name()?;
let target = field.syntax().text_range();
acc.add(
AssistId("generate_deref", AssistKind::Generate),
- format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field_name),
+ format!("Generate `{deref_type_to_generate:?}` impl using `{field_name}`"),
target,
|edit| {
generate_edit(
@@ -98,13 +99,14 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()
let module = ctx.sema.to_def(&strukt)?.module(ctx.db());
let trait_ = deref_type_to_generate.to_trait(&ctx.sema, module.krate())?;
- let trait_path = module.find_use_path(ctx.db(), ModuleDef::Trait(trait_))?;
+ let trait_path =
+ module.find_use_path(ctx.db(), ModuleDef::Trait(trait_), ctx.config.prefer_no_std)?;
let field_type = field.ty()?;
let target = field.syntax().text_range();
acc.add(
AssistId("generate_deref", AssistKind::Generate),
- format!("Generate `{:?}` impl using `{}`", deref_type_to_generate, field.syntax()),
+ format!("Generate `{deref_type_to_generate:?}` impl using `{field}`"),
target,
|edit| {
generate_edit(
@@ -130,18 +132,16 @@ fn generate_edit(
let start_offset = strukt.syntax().text_range().end();
let impl_code = match deref_type {
DerefType::Deref => format!(
- r#" type Target = {0};
+ r#" type Target = {field_type_syntax};
fn deref(&self) -> &Self::Target {{
- &self.{1}
+ &self.{field_name}
}}"#,
- field_type_syntax, field_name
),
DerefType::DerefMut => format!(
r#" fn deref_mut(&mut self) -> &mut Self::Target {{
- &mut self.{}
+ &mut self.{field_name}
}}"#,
- field_name
),
};
let strukt_adt = ast::Adt::Struct(strukt);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
index c91141f8e..b8415c72a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs
@@ -139,40 +139,44 @@ fn make_example_for_fn(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<St
let mut example = String::new();
+ let use_path = build_path(ast_func, ctx)?;
let is_unsafe = ast_func.unsafe_token().is_some();
let param_list = ast_func.param_list()?;
let ref_mut_params = ref_mut_params(&param_list);
let self_name = self_name(ast_func);
- format_to!(example, "use {};\n\n", build_path(ast_func, ctx)?);
+ format_to!(example, "use {use_path};\n\n");
if let Some(self_name) = &self_name {
- if let Some(mtbl) = is_ref_mut_self(ast_func) {
- let mtbl = if mtbl == true { " mut" } else { "" };
- format_to!(example, "let{} {} = ;\n", mtbl, self_name);
+ if let Some(mut_) = is_ref_mut_self(ast_func) {
+ let mut_ = if mut_ == true { "mut " } else { "" };
+ format_to!(example, "let {mut_}{self_name} = ;\n");
}
}
for param_name in &ref_mut_params {
- format_to!(example, "let mut {} = ;\n", param_name);
+ format_to!(example, "let mut {param_name} = ;\n");
}
// Call the function, check result
let function_call = function_call(ast_func, &param_list, self_name.as_deref(), is_unsafe)?;
if returns_a_value(ast_func, ctx) {
if count_parameters(&param_list) < 3 {
- format_to!(example, "assert_eq!({}, );\n", function_call);
+ format_to!(example, "assert_eq!({function_call}, );\n");
} else {
- format_to!(example, "let result = {};\n", function_call);
+ format_to!(example, "let result = {function_call};\n");
example.push_str("assert_eq!(result, );\n");
}
} else {
- format_to!(example, "{};\n", function_call);
+ format_to!(example, "{function_call};\n");
}
// Check the mutated values
- if is_ref_mut_self(ast_func) == Some(true) {
- format_to!(example, "assert_eq!({}, );", self_name?);
+ if let Some(self_name) = &self_name {
+ if is_ref_mut_self(ast_func) == Some(true) {
+ format_to!(example, "assert_eq!({self_name}, );");
+ }
}
for param_name in &ref_mut_params {
- format_to!(example, "assert_eq!({}, );", param_name);
+ format_to!(example, "assert_eq!({param_name}, );");
}
+
Some(example)
}
@@ -189,7 +193,8 @@ fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<S
let intro_for_new = || {
let is_new = name == "new";
if is_new && ret_ty == self_ty {
- Some(format!("Creates a new [`{}`].", linkable_self_ty?))
+ let self_ty = linkable_self_ty?;
+ Some(format!("Creates a new [`{self_ty}`]."))
} else {
None
}
@@ -214,7 +219,9 @@ fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<S
} else {
""
};
- Some(format!("Returns{reference} the {what} of this [`{}`].", linkable_self_ty?))
+
+ let self_ty = linkable_self_ty?;
+ Some(format!("Returns{reference} the {what} of this [`{self_ty}`]."))
}
_ => None,
};
@@ -228,7 +235,9 @@ fn introduction_builder(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<S
if what == "len" {
what = "length".into()
};
- Some(format!("Sets the {what} of this [`{}`].", linkable_self_ty?))
+
+ let self_ty = linkable_self_ty?;
+ Some(format!("Sets the {what} of this [`{self_ty}`]."))
};
if let Some(intro) = intro_for_new() {
@@ -404,7 +413,7 @@ fn arguments_from_params(param_list: &ast::ParamList) -> String {
// instance `TuplePat`) could be managed later.
Some(ast::Pat::IdentPat(ident_pat)) => match ident_pat.name() {
Some(name) => match is_a_ref_mut_param(&param) {
- true => format!("&mut {}", name),
+ true => format!("&mut {name}"),
false => name.to_string(),
},
None => "_".to_string(),
@@ -424,14 +433,15 @@ fn function_call(
let name = ast_func.name()?;
let arguments = arguments_from_params(param_list);
let function_call = if param_list.self_param().is_some() {
- format!("{}.{}({})", self_name?, name, arguments)
+ let self_ = self_name?;
+ format!("{self_}.{name}({arguments})")
} else if let Some(implementation) = self_partial_type(ast_func) {
- format!("{}::{}({})", implementation, name, arguments)
+ format!("{implementation}::{name}({arguments})")
} else {
- format!("{}({})", name, arguments)
+ format!("{name}({arguments})")
};
match is_unsafe {
- true => Some(format!("unsafe {{ {} }}", function_call)),
+ true => Some(format!("unsafe {{ {function_call} }}")),
false => Some(function_call),
}
}
@@ -469,8 +479,8 @@ fn build_path(ast_func: &ast::Fn, ctx: &AssistContext<'_>) -> Option<String> {
.unwrap_or_else(|| "*".into());
let module_def: ModuleDef = ctx.sema.to_def(ast_func)?.module(ctx.db()).into();
match module_def.canonical_path(ctx.db()) {
- Some(path) => Some(format!("{}::{}::{}", crate_name, path, leaf)),
- None => Some(format!("{}::{}", crate_name, leaf)),
+ Some(path) => Some(format!("{crate_name}::{path}::{leaf}")),
+ None => Some(format!("{crate_name}::{leaf}")),
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs
index 52d27d8a7..63e91b835 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_is_method.rs
@@ -52,7 +52,7 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>
let fn_name = format!("is_{}", &to_lower_snake_case(&variant_name.text()));
// Return early if we've found an existing new fn
- let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
+ let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?;
let target = variant.syntax().text_range();
acc.add_group(
@@ -61,21 +61,15 @@ pub(crate) fn generate_enum_is_method(acc: &mut Assists, ctx: &AssistContext<'_>
"Generate an `is_` method for this enum variant",
target,
|builder| {
- let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
+ let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
let method = format!(
- " /// Returns `true` if the {} is [`{variant}`].
+ " /// Returns `true` if the {enum_lowercase_name} is [`{variant_name}`].
///
- /// [`{variant}`]: {}::{variant}
+ /// [`{variant_name}`]: {enum_name}::{variant_name}
#[must_use]
- {}fn {}(&self) -> bool {{
- matches!(self, Self::{variant}{})
+ {vis}fn {fn_name}(&self) -> bool {{
+ matches!(self, Self::{variant_name}{pattern_suffix})
}}",
- enum_lowercase_name,
- enum_name,
- vis,
- fn_name,
- pattern_suffix,
- variant = variant_name
);
add_method_to_adt(builder, &parent_enum, impl_def, &method);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
index b19aa0f65..bdd3cf4f0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -116,6 +116,14 @@ fn generate_enum_projection_method(
assist_description: &str,
props: ProjectionProps,
) -> Option<()> {
+ let ProjectionProps {
+ fn_name_prefix,
+ self_param,
+ return_prefix,
+ return_suffix,
+ happy_case,
+ sad_case,
+ } = props;
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?;
let parent_enum = ast::Adt::Enum(variant.parent_enum());
@@ -125,7 +133,7 @@ fn generate_enum_projection_method(
let (field,) = record.fields().collect_tuple()?;
let name = field.name()?.to_string();
let ty = field.ty()?;
- let pattern_suffix = format!(" {{ {} }}", name);
+ let pattern_suffix = format!(" {{ {name} }}");
(pattern_suffix, ty, name)
}
ast::StructKind::Tuple(tuple) => {
@@ -136,11 +144,10 @@ fn generate_enum_projection_method(
ast::StructKind::Unit => return None,
};
- let fn_name =
- format!("{}_{}", props.fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
+ let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text()));
// Return early if we've found an existing new fn
- let impl_def = find_struct_impl(ctx, &parent_enum, &fn_name)?;
+ let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?;
let target = variant.syntax().text_range();
acc.add_group(
@@ -149,27 +156,15 @@ fn generate_enum_projection_method(
assist_description,
target,
|builder| {
- let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v));
+ let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} "));
let method = format!(
- " {0}fn {1}({2}) -> {3}{4}{5} {{
- if let Self::{6}{7} = self {{
- {8}({9})
+ " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{
+ if let Self::{variant_name}{pattern_suffix} = self {{
+ {happy_case}({bound_name})
}} else {{
- {10}
+ {sad_case}
}}
- }}",
- vis,
- fn_name,
- props.self_param,
- props.return_prefix,
- field_type.syntax(),
- props.return_suffix,
- variant_name,
- pattern_suffix,
- props.happy_case,
- bound_name,
- props.sad_case,
- );
+ }}");
add_method_to_adt(builder, &parent_enum, impl_def, &method);
},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
index 507ea012b..7c81d2c6a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
@@ -56,23 +56,18 @@ pub(crate) fn generate_from_impl_for_enum(
target,
|edit| {
let start_offset = variant.parent_enum().syntax().text_range().end();
- let from_trait = format!("From<{}>", field_type.syntax());
+ let from_trait = format!("From<{field_type}>");
let impl_code = if let Some(name) = field_name {
format!(
- r#" fn from({0}: {1}) -> Self {{
- Self::{2} {{ {0} }}
- }}"#,
- name.text(),
- field_type.syntax(),
- variant_name,
+ r#" fn from({name}: {field_type}) -> Self {{
+ Self::{variant_name} {{ {name} }}
+ }}"#
)
} else {
format!(
- r#" fn from(v: {}) -> Self {{
- Self::{}(v)
- }}"#,
- field_type.syntax(),
- variant_name,
+ r#" fn from(v: {field_type}) -> Self {{
+ Self::{variant_name}(v)
+ }}"#
)
};
let from_impl = generate_trait_impl_text(&enum_, &from_trait, &impl_code);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
index e26c76da1..c229127e4 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs
@@ -1,4 +1,4 @@
-use hir::{HasSource, HirDisplay, Module, Semantics, TypeInfo};
+use hir::{Adt, HasSource, HirDisplay, Module, Semantics, TypeInfo};
use ide_db::{
base_db::FileId,
defs::{Definition, NameRefClass},
@@ -145,7 +145,8 @@ fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
return None;
}
let (impl_, file) = get_adt_source(ctx, &adt, fn_name.text().as_str())?;
- let (target, insert_offset) = get_method_target(ctx, &target_module, &impl_)?;
+ let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
+
let function_builder =
FunctionBuilder::from_method_call(ctx, &call, &fn_name, target_module, target)?;
let text_range = call.syntax().text_range();
@@ -174,10 +175,11 @@ fn add_func_to_accumulator(
label: String,
) -> Option<()> {
acc.add(AssistId("generate_function", AssistKind::Generate), label, text_range, |builder| {
- let function_template = function_builder.render();
+ let indent = IndentLevel::from_node(function_builder.target.syntax());
+ let function_template = function_builder.render(adt_name.is_some());
let mut func = function_template.to_string(ctx.config.snippet_cap);
if let Some(name) = adt_name {
- func = format!("\nimpl {} {{\n{}\n}}", name, func);
+ func = format!("\n{indent}impl {name} {{\n{func}\n{indent}}}");
}
builder.edit_file(file);
match ctx.config.snippet_cap {
@@ -196,7 +198,7 @@ fn get_adt_source(
let file = ctx.sema.parse(range.file_id);
let adt_source =
ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
- find_struct_impl(ctx, &adt_source, fn_name).map(|impl_| (impl_, range.file_id))
+ find_struct_impl(ctx, &adt_source, &[fn_name.to_string()]).map(|impl_| (impl_, range.file_id))
}
struct FunctionTemplate {
@@ -210,23 +212,26 @@ struct FunctionTemplate {
impl FunctionTemplate {
fn to_string(&self, cap: Option<SnippetCap>) -> String {
+ let Self { leading_ws, fn_def, ret_type, should_focus_return_type, trailing_ws, tail_expr } =
+ self;
+
let f = match cap {
Some(cap) => {
- let cursor = if self.should_focus_return_type {
+ let cursor = if *should_focus_return_type {
// Focus the return type if there is one
- match self.ret_type {
- Some(ref ret_type) => ret_type.syntax(),
- None => self.tail_expr.syntax(),
+ match ret_type {
+ Some(ret_type) => ret_type.syntax(),
+ None => tail_expr.syntax(),
}
} else {
- self.tail_expr.syntax()
+ tail_expr.syntax()
};
- render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(cursor))
+ render_snippet(cap, fn_def.syntax(), Cursor::Replace(cursor))
}
- None => self.fn_def.to_string(),
+ None => fn_def.to_string(),
};
- format!("{}{}{}", self.leading_ws, f, self.trailing_ws)
+ format!("{leading_ws}{f}{trailing_ws}")
}
}
@@ -307,7 +312,7 @@ impl FunctionBuilder {
})
}
- fn render(self) -> FunctionTemplate {
+ fn render(self, is_method: bool) -> FunctionTemplate {
let placeholder_expr = make::ext::expr_todo();
let fn_body = make::block_expr(vec![], Some(placeholder_expr));
let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
@@ -325,16 +330,23 @@ impl FunctionBuilder {
match self.target {
GeneratedFunctionTarget::BehindItem(it) => {
- let indent = IndentLevel::from_node(&it);
- leading_ws = format!("\n\n{}", indent);
+ let mut indent = IndentLevel::from_node(&it);
+ if is_method {
+ indent = indent + 1;
+ leading_ws = format!("{indent}");
+ } else {
+ leading_ws = format!("\n\n{indent}");
+ }
+
fn_def = fn_def.indent(indent);
trailing_ws = String::new();
}
GeneratedFunctionTarget::InEmptyItemList(it) => {
let indent = IndentLevel::from_node(&it);
- leading_ws = format!("\n{}", indent + 1);
- fn_def = fn_def.indent(indent + 1);
- trailing_ws = format!("\n{}", indent);
+ let leading_indent = indent + 1;
+ leading_ws = format!("\n{leading_indent}");
+ fn_def = fn_def.indent(leading_indent);
+ trailing_ws = format!("\n{indent}");
}
};
@@ -411,14 +423,13 @@ fn get_fn_target(
fn get_method_target(
ctx: &AssistContext<'_>,
- target_module: &Module,
impl_: &Option<ast::Impl>,
+ adt: &Adt,
) -> Option<(GeneratedFunctionTarget, TextSize)> {
let target = match impl_ {
Some(impl_) => next_space_for_fn_in_impl(impl_)?,
None => {
- next_space_for_fn_in_module(ctx.sema.db, &target_module.definition_source(ctx.sema.db))?
- .1
+ GeneratedFunctionTarget::BehindItem(adt.source(ctx.sema.db)?.syntax().value.clone())
}
};
Some((target.clone(), get_insert_offset(&target)))
@@ -437,7 +448,7 @@ fn assoc_fn_target_info(
return None;
}
let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?;
- let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?;
+ let (target, insert_offset) = get_method_target(ctx, &impl_, &adt)?;
let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None };
Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset))
}
@@ -1468,14 +1479,12 @@ fn foo() {S.bar$0();}
",
r"
struct S;
-fn foo() {S.bar();}
impl S {
-
-
-fn bar(&self) ${0:-> _} {
- todo!()
-}
+ fn bar(&self) ${0:-> _} {
+ todo!()
+ }
}
+fn foo() {S.bar();}
",
)
}
@@ -1516,14 +1525,12 @@ fn foo() {s::S.bar$0();}
r"
mod s {
pub struct S;
-impl S {
-
-
- pub(crate) fn bar(&self) ${0:-> _} {
- todo!()
+ impl S {
+ pub(crate) fn bar(&self) ${0:-> _} {
+ todo!()
+ }
}
}
-}
fn foo() {s::S.bar();}
",
)
@@ -1544,18 +1551,16 @@ mod s {
",
r"
struct S;
+impl S {
+ fn bar(&self) ${0:-> _} {
+ todo!()
+ }
+}
mod s {
fn foo() {
super::S.bar();
}
}
-impl S {
-
-
-fn bar(&self) ${0:-> _} {
- todo!()
-}
-}
",
)
@@ -1571,14 +1576,12 @@ fn foo() {$0S.bar();}
",
r"
struct S;
-fn foo() {S.bar();}
impl S {
-
-
-fn bar(&self) ${0:-> _} {
- todo!()
-}
+ fn bar(&self) ${0:-> _} {
+ todo!()
+ }
}
+fn foo() {S.bar();}
",
)
}
@@ -1593,14 +1596,12 @@ fn foo() {S::bar$0();}
",
r"
struct S;
-fn foo() {S::bar();}
impl S {
-
-
-fn bar() ${0:-> _} {
- todo!()
-}
+ fn bar() ${0:-> _} {
+ todo!()
+ }
}
+fn foo() {S::bar();}
",
)
}
@@ -1641,14 +1642,12 @@ fn foo() {s::S::bar$0();}
r"
mod s {
pub struct S;
-impl S {
-
-
- pub(crate) fn bar() ${0:-> _} {
- todo!()
+ impl S {
+ pub(crate) fn bar() ${0:-> _} {
+ todo!()
+ }
}
}
-}
fn foo() {s::S::bar();}
",
)
@@ -1664,14 +1663,12 @@ fn foo() {$0S::bar();}
",
r"
struct S;
-fn foo() {S::bar();}
impl S {
-
-
-fn bar() ${0:-> _} {
- todo!()
-}
+ fn bar() ${0:-> _} {
+ todo!()
+ }
}
+fn foo() {S::bar();}
",
)
}
@@ -1841,15 +1838,13 @@ fn main() {
",
r"
enum Foo {}
-fn main() {
- Foo::new();
-}
impl Foo {
-
-
-fn new() ${0:-> _} {
- todo!()
+ fn new() ${0:-> _} {
+ todo!()
+ }
}
+fn main() {
+ Foo::new();
}
",
)
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
index 76fcef0ca..5e7191428 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter.rs
@@ -1,6 +1,9 @@
use ide_db::famous_defs::FamousDefs;
use stdx::{format_to, to_lower_snake_case};
-use syntax::ast::{self, AstNode, HasName, HasVisibility};
+use syntax::{
+ ast::{self, AstNode, HasName, HasVisibility},
+ TextRange,
+};
use crate::{
utils::{convert_reference_type, find_impl_block_end, find_struct_impl, generate_impl_text},
@@ -72,92 +75,259 @@ pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext<'_>) ->
generate_getter_impl(acc, ctx, true)
}
+#[derive(Clone, Debug)]
+struct RecordFieldInfo {
+ field_name: syntax::ast::Name,
+ field_ty: syntax::ast::Type,
+ fn_name: String,
+ target: TextRange,
+}
+
+struct GetterInfo {
+ impl_def: Option<ast::Impl>,
+ strukt: ast::Struct,
+ mutable: bool,
+}
+
pub(crate) fn generate_getter_impl(
acc: &mut Assists,
ctx: &AssistContext<'_>,
mutable: bool,
) -> Option<()> {
- let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
- let field = ctx.find_node_at_offset::<ast::RecordField>()?;
+ // This if condition denotes two modes this assist can work in:
+ // - First is acting upon selection of record fields
+ // - Next is acting upon a single record field
+ //
+ // This is the only part where implementation diverges a bit,
+ // subsequent code is generic for both of these modes
- let field_name = field.name()?;
- let field_ty = field.ty()?;
+ let (strukt, info_of_record_fields, fn_names) = if !ctx.has_empty_selection() {
+ // Selection Mode
+ let node = ctx.covering_element();
- // Return early if we've found an existing fn
- let mut fn_name = to_lower_snake_case(&field_name.to_string());
- if mutable {
- format_to!(fn_name, "_mut");
+ let node = match node {
+ syntax::NodeOrToken::Node(n) => n,
+ syntax::NodeOrToken::Token(t) => t.parent()?,
+ };
+
+ let parent_struct = node.ancestors().find_map(ast::Struct::cast)?;
+
+ let (info_of_record_fields, field_names) =
+ extract_and_parse_record_fields(&parent_struct, ctx.selection_trimmed(), mutable)?;
+
+ (parent_struct, info_of_record_fields, field_names)
+ } else {
+ // Single Record Field mode
+ let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
+ let field = ctx.find_node_at_offset::<ast::RecordField>()?;
+
+ let record_field_info = parse_record_field(field, mutable)?;
+
+ let fn_name = record_field_info.fn_name.clone();
+
+ (strukt, vec![record_field_info], vec![fn_name])
+ };
+
+ // No record fields to do work on :(
+ if info_of_record_fields.len() == 0 {
+ return None;
}
- let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
+
+ let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &fn_names)?;
let (id, label) = if mutable {
("generate_getter_mut", "Generate a mut getter method")
} else {
("generate_getter", "Generate a getter method")
};
- let target = field.syntax().text_range();
+
+ // Computing collective text range of all record fields in selected region
+ let target: TextRange = info_of_record_fields
+ .iter()
+ .map(|record_field_info| record_field_info.target)
+ .reduce(|acc, target| acc.cover(target))?;
+
+ let getter_info = GetterInfo { impl_def, strukt, mutable };
+
acc.add_group(
&GroupLabel("Generate getter/setter".to_owned()),
AssistId(id, AssistKind::Generate),
label,
target,
|builder| {
+ let record_fields_count = info_of_record_fields.len();
+
let mut buf = String::with_capacity(512);
- if impl_def.is_some() {
- buf.push('\n');
+ // Check if an impl exists
+ if let Some(impl_def) = &getter_info.impl_def {
+ // Check if impl is empty
+ if let Some(assoc_item_list) = impl_def.assoc_item_list() {
+ if assoc_item_list.assoc_items().next().is_some() {
+ // If not empty then only insert a new line
+ buf.push('\n');
+ }
+ }
}
- let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
- let (ty, body) = if mutable {
- (format!("&mut {}", field_ty), format!("&mut self.{}", field_name))
- } else {
- (|| {
- let krate = ctx.sema.scope(field_ty.syntax())?.krate();
- let famous_defs = &FamousDefs(&ctx.sema, krate);
- ctx.sema
- .resolve_type(&field_ty)
- .and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs))
- .map(|conversion| {
- cov_mark::hit!(convert_reference_type);
- (
- conversion.convert_type(ctx.db()),
- conversion.getter(field_name.to_string()),
- )
- })
- })()
- .unwrap_or_else(|| (format!("&{}", field_ty), format!("&self.{}", field_name)))
- };
-
- format_to!(
- buf,
- " {}fn {}(&{}self) -> {} {{
- {}
- }}",
- vis,
- fn_name,
- mutable.then(|| "mut ").unwrap_or_default(),
- ty,
- body,
- );
-
- let start_offset = impl_def
- .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
+ for (i, record_field_info) in info_of_record_fields.iter().enumerate() {
+ // this buf inserts a newline at the end of a getter
+ // automatically, if one wants to add one more newline
+ // for separating it from other assoc items, that needs
+ // to be handled spearately
+ let mut getter_buf =
+ generate_getter_from_info(ctx, &getter_info, &record_field_info);
+
+ // Insert `$0` only for last getter we generate
+ if i == record_fields_count - 1 {
+ getter_buf = getter_buf.replacen("fn ", "fn $0", 1);
+ }
+
+ // For first element we do not merge with '\n', as
+ // that can be inserted by impl_def check defined
+ // above, for other cases which are:
+ //
+ // - impl exists but it empty, here we would ideally
+ // not want to keep newline between impl <struct> {
+ // and fn <fn-name>() { line
+ //
+ // - next if impl itself does not exist, in this
+ // case we ourselves generate a new impl and that
+ // again ends up with the same reasoning as above
+ // for not keeping newline
+ if i == 0 {
+ buf = buf + &getter_buf;
+ } else {
+ buf = buf + "\n" + &getter_buf;
+ }
+
+ // We don't insert a new line at the end of
+ // last getter as it will end up in the end
+ // of an impl where we would not like to keep
+ // getter and end of impl ( i.e. `}` ) with an
+ // extra line for no reason
+ if i < record_fields_count - 1 {
+ buf = buf + "\n";
+ }
+ }
+
+ let start_offset = getter_info
+ .impl_def
+ .as_ref()
+ .and_then(|impl_def| find_impl_block_end(impl_def.to_owned(), &mut buf))
.unwrap_or_else(|| {
- buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
- strukt.syntax().text_range().end()
+ buf = generate_impl_text(&ast::Adt::Struct(getter_info.strukt.clone()), &buf);
+ getter_info.strukt.syntax().text_range().end()
});
match ctx.config.snippet_cap {
- Some(cap) => {
- builder.insert_snippet(cap, start_offset, buf.replacen("fn ", "fn $0", 1))
- }
+ Some(cap) => builder.insert_snippet(cap, start_offset, buf),
None => builder.insert(start_offset, buf),
}
},
)
}
+fn generate_getter_from_info(
+ ctx: &AssistContext<'_>,
+ info: &GetterInfo,
+ record_field_info: &RecordFieldInfo,
+) -> String {
+ let mut buf = String::with_capacity(512);
+
+ let vis = info.strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
+ let (ty, body) = if info.mutable {
+ (
+ format!("&mut {}", record_field_info.field_ty),
+ format!("&mut self.{}", record_field_info.field_name),
+ )
+ } else {
+ (|| {
+ let krate = ctx.sema.scope(record_field_info.field_ty.syntax())?.krate();
+ let famous_defs = &FamousDefs(&ctx.sema, krate);
+ ctx.sema
+ .resolve_type(&record_field_info.field_ty)
+ .and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs))
+ .map(|conversion| {
+ cov_mark::hit!(convert_reference_type);
+ (
+ conversion.convert_type(ctx.db()),
+ conversion.getter(record_field_info.field_name.to_string()),
+ )
+ })
+ })()
+ .unwrap_or_else(|| {
+ (
+ format!("&{}", record_field_info.field_ty),
+ format!("&self.{}", record_field_info.field_name),
+ )
+ })
+ };
+
+ format_to!(
+ buf,
+ " {}fn {}(&{}self) -> {} {{
+ {}
+ }}",
+ vis,
+ record_field_info.fn_name,
+ info.mutable.then(|| "mut ").unwrap_or_default(),
+ ty,
+ body,
+ );
+
+ buf
+}
+
+fn extract_and_parse_record_fields(
+ node: &ast::Struct,
+ selection_range: TextRange,
+ mutable: bool,
+) -> Option<(Vec<RecordFieldInfo>, Vec<String>)> {
+ let mut field_names: Vec<String> = vec![];
+ let field_list = node.field_list()?;
+
+ match field_list {
+ ast::FieldList::RecordFieldList(ele) => {
+ let info_of_record_fields_in_selection = ele
+ .fields()
+ .filter_map(|record_field| {
+ if selection_range.contains_range(record_field.syntax().text_range()) {
+ let record_field_info = parse_record_field(record_field, mutable)?;
+ field_names.push(record_field_info.fn_name.clone());
+ return Some(record_field_info);
+ }
+
+ None
+ })
+ .collect::<Vec<RecordFieldInfo>>();
+
+ if info_of_record_fields_in_selection.len() == 0 {
+ return None;
+ }
+
+ Some((info_of_record_fields_in_selection, field_names))
+ }
+ ast::FieldList::TupleFieldList(_) => {
+ return None;
+ }
+ }
+}
+
+fn parse_record_field(record_field: ast::RecordField, mutable: bool) -> Option<RecordFieldInfo> {
+ let field_name = record_field.name()?;
+ let field_ty = record_field.ty()?;
+
+ let mut fn_name = to_lower_snake_case(&field_name.to_string());
+ if mutable {
+ format_to!(fn_name, "_mut");
+ }
+
+ let target = record_field.syntax().text_range();
+
+ Some(RecordFieldInfo { field_name, field_ty, fn_name, target })
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -489,4 +659,53 @@ impl Context {
"#,
);
}
+
+ #[test]
+ fn test_generate_multiple_getters_from_selection() {
+ check_assist(
+ generate_getter,
+ r#"
+struct Context {
+ $0data: Data,
+ count: usize,$0
+}
+ "#,
+ r#"
+struct Context {
+ data: Data,
+ count: usize,
+}
+
+impl Context {
+ fn data(&self) -> &Data {
+ &self.data
+ }
+
+ fn $0count(&self) -> &usize {
+ &self.count
+ }
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn test_generate_multiple_getters_from_selection_one_already_exists() {
+ // As impl for one of the fields already exist, skip it
+ check_assist_not_applicable(
+ generate_getter,
+ r#"
+struct Context {
+ $0data: Data,
+ count: usize,$0
+}
+
+impl Context {
+ fn data(&self) -> &Data {
+ &self.data
+ }
+}
+ "#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
index 68287a20b..9af26c04e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
@@ -28,7 +28,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
acc.add(
AssistId("generate_impl", AssistKind::Generate),
- format!("Generate impl for `{}`", name),
+ format!("Generate impl for `{name}`"),
target,
|edit| {
let start_offset = nominal.syntax().text_range().end();
@@ -52,6 +52,7 @@ mod tests {
use super::*;
+ // FIXME: break up into separate test fns
#[test]
fn test_add_impl() {
check_assist(
@@ -136,6 +137,18 @@ mod tests {
check_assist(
generate_impl,
+ r#"
+ struct Defaulted<const N: i32 = 0> {}$0"#,
+ r#"
+ struct Defaulted<const N: i32 = 0> {}
+
+ impl<const N: i32> Defaulted<N> {
+ $0
+ }"#,
+ );
+
+ check_assist(
+ generate_impl,
r#"pub trait Trait {}
struct Struct<T>$0
where
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index 6c93875e9..17fadea0e 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -39,7 +39,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
};
// Return early if we've found an existing new fn
- let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
+ let impl_def =
+ find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[String::from("new")])?;
let current_module = ctx.sema.scope(strukt.syntax())?.module();
@@ -51,17 +52,22 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
buf.push('\n');
}
- let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
+ let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} "));
let trivial_constructors = field_list
.fields()
.map(|f| {
+ let name = f.name()?;
+
let ty = ctx.sema.resolve_type(&f.ty()?)?;
let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
- let type_path = current_module
- .find_use_path(ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?)?;
+ let type_path = current_module.find_use_path(
+ ctx.sema.db,
+ item_for_path_search(ctx.sema.db, item_in_ns)?,
+ ctx.config.prefer_no_std,
+ )?;
let expr = use_trivial_constructor(
&ctx.sema.db,
@@ -69,7 +75,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
&ty,
)?;
- Some(format!("{}: {}", f.name()?.syntax(), expr))
+ Some(format!("{name}: {expr}"))
})
.collect::<Vec<_>>();
@@ -78,7 +84,10 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
.enumerate()
.filter_map(|(i, f)| {
if trivial_constructors[i].is_none() {
- Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))
+ let name = f.name()?;
+ let ty = f.ty()?;
+
+ Some(format!("{name}: {ty}"))
} else {
None
}
@@ -98,7 +107,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
})
.format(", ");
- format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
+ format_to!(buf, " {vis}fn new({params}) -> Self {{ Self {{ {fields} }} }}");
let start_offset = impl_def
.and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_setter.rs
index 2a7ad6ce3..62f72df1c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_setter.rs
@@ -36,11 +36,8 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
// Return early if we've found an existing fn
let fn_name = to_lower_snake_case(&field_name.to_string());
- let impl_def = find_struct_impl(
- ctx,
- &ast::Adt::Struct(strukt.clone()),
- format!("set_{}", fn_name).as_str(),
- )?;
+ let impl_def =
+ find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), &[format!("set_{fn_name}")])?;
let target = field.syntax().text_range();
acc.add_group(
@@ -55,18 +52,12 @@ pub(crate) fn generate_setter(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt
buf.push('\n');
}
- let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
+ let vis = strukt.visibility().map_or(String::new(), |v| format!("{v} "));
format_to!(
buf,
- " {}fn set_{}(&mut self, {}: {}) {{
- self.{} = {};
- }}",
- vis,
- fn_name,
- fn_name,
- field_ty,
- fn_name,
- fn_name,
+ " {vis}fn set_{fn_name}(&mut self, {fn_name}: {field_ty}) {{
+ self.{fn_name} = {fn_name};
+ }}"
);
let start_offset = impl_def
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs
new file mode 100644
index 000000000..aa710d2ce
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_format_string_arg.rs
@@ -0,0 +1,296 @@
+use crate::{AssistContext, Assists};
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ syntax_helpers::{
+ format_string::is_format_string,
+ format_string_exprs::{parse_format_exprs, Arg},
+ },
+};
+use itertools::Itertools;
+use stdx::format_to;
+use syntax::{ast, AstNode, AstToken, NodeOrToken, SyntaxKind::COMMA, TextRange};
+
+// Assist: move_format_string_arg
+//
+// 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)*)));
+// }
+//
+// fn main() {
+// print!("{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!("{}"$0, x + 1);
+// }
+// ```
+
+pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let fmt_string = ctx.find_token_at_offset::<ast::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()),
+ )?;
+ if !is_format_string(&expanded_t) {
+ return None;
+ }
+
+ let (new_fmt, extracted_args) = parse_format_exprs(fmt_string.text()).ok()?;
+ if extracted_args.is_empty() {
+ return None;
+ }
+
+ acc.add(
+ AssistId(
+ "move_format_string_arg",
+ // if there aren't any expressions, then make the assist a RefactorExtract
+ if extracted_args.iter().filter(|f| matches!(f, Arg::Expr(_))).count() == 0 {
+ AssistKind::RefactorExtract
+ } else {
+ AssistKind::QuickFix
+ },
+ ),
+ "Extract format args",
+ tt.syntax().text_range(),
+ |edit| {
+ let fmt_range = fmt_string.syntax().text_range();
+
+ // Replace old format string with new format string whose arguments have been extracted
+ edit.replace(fmt_range, new_fmt);
+
+ // Insert cursor at end of format string
+ edit.insert(fmt_range.end(), "$0");
+
+ // Extract existing arguments in macro
+ let tokens =
+ tt.token_trees_and_tokens().collect_vec();
+
+ let mut existing_args: Vec<String> = vec![];
+
+ let mut current_arg = String::new();
+ if let [_opening_bracket, NodeOrToken::Token(format_string), _args_start_comma, tokens @ .., NodeOrToken::Token(end_bracket)] =
+ tokens.as_slice()
+ {
+ for t in tokens {
+ match t {
+ NodeOrToken::Node(n) => {
+ format_to!(current_arg, "{n}");
+ },
+ NodeOrToken::Token(t) if t.kind() == COMMA=> {
+ existing_args.push(current_arg.trim().into());
+ current_arg.clear();
+ },
+ NodeOrToken::Token(t) => {
+ current_arg.push_str(t.text());
+ },
+ }
+ }
+ existing_args.push(current_arg.trim().into());
+
+ // delete everything after the format string till end bracket
+ // we're going to insert the new arguments later
+ edit.delete(TextRange::new(
+ format_string.text_range().end(),
+ end_bracket.text_range().start(),
+ ));
+ }
+
+ // Start building the new args
+ let mut existing_args = existing_args.into_iter();
+ let mut args = String::new();
+
+ let mut placeholder_idx = 1;
+
+ for extracted_args in extracted_args {
+ // remove expr from format string
+ args.push_str(", ");
+
+ match extracted_args {
+ Arg::Ident(s) | Arg::Expr(s) => {
+ // insert arg
+ args.push_str(&s);
+ }
+ Arg::Placeholder => {
+ // try matching with existing argument
+ match existing_args.next() {
+ Some(ea) => {
+ args.push_str(&ea);
+ }
+ None => {
+ // insert placeholder
+ args.push_str(&format!("${placeholder_idx}"));
+ placeholder_idx += 1;
+ }
+ }
+ }
+ }
+ }
+
+ // Insert new args
+ edit.insert(fmt_range.end(), args);
+ },
+ );
+
+ Some(())
+}
+
+#[cfg(test)]
+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(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {x + 1:b} {}$0", y + 2, 2);
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {:b} {}"$0, y + 2, x + 1, 2);
+}
+"#,
+ ),
+ );
+ }
+
+ #[test]
+ fn single_arg() {
+ check_assist(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{obj.value:b}$0",);
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{:b}"$0, obj.value);
+}
+"#,
+ ),
+ );
+ }
+
+ #[test]
+ fn multiple_middle_placeholders_arg() {
+ check_assist(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {x + 1:b} {} {}$0", y + 2, 2);
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {:b} {} {}"$0, y + 2, x + 1, 2, $1);
+}
+"#,
+ ),
+ );
+ }
+
+ #[test]
+ fn multiple_trailing_args() {
+ check_assist(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {x + 1:b} {Struct(1, 2)}$0", 1);
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
+}
+"#,
+ ),
+ );
+ }
+
+ #[test]
+ fn improper_commas() {
+ check_assist(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {x + 1:b} {Struct(1, 2)}$0", 1,);
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2));
+}
+"#,
+ ),
+ );
+ }
+
+ #[test]
+ fn nested_tt() {
+ check_assist(
+ move_format_string_arg,
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("My name is {} {x$0 + x}", stringify!(Paperino))
+}
+"#,
+ ),
+ &add_macro_decl(
+ r#"
+fn main() {
+ print!("My name is {} {}"$0, stringify!(Paperino), x + x)
+}
+"#,
+ ),
+ );
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
index 121f8b4a1..e57d1d065 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_method_call.rs
@@ -44,8 +44,11 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) ->
let current_module = ctx.sema.scope(call.syntax())?.module();
let target_module_def = ModuleDef::from(resolved_call);
let item_in_ns = ItemInNs::from(target_module_def);
- let receiver_path = current_module
- .find_use_path(ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?)?;
+ let receiver_path = current_module.find_use_path(
+ ctx.sema.db,
+ item_for_path_search(ctx.sema.db, item_in_ns)?,
+ ctx.config.prefer_no_std,
+ )?;
let qualify_candidate = QualifyCandidate::ImplMethod(ctx.sema.db, call, resolved_call);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
index 0c2e9da38..4b2af550b 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs
@@ -37,7 +37,8 @@ use crate::{
// ```
pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
let (import_assets, syntax_under_caret) = find_importable_node(ctx)?;
- let mut proposed_imports = import_assets.search_for_relative_paths(&ctx.sema);
+ let mut proposed_imports =
+ import_assets.search_for_relative_paths(&ctx.sema, ctx.config.prefer_no_std);
if proposed_imports.is_empty() {
return None;
}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index d139f78a6..9fd5e1886 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -85,7 +85,7 @@ pub(crate) fn replace_derive_with_manual_impl(
})
.flat_map(|trait_| {
current_module
- .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
+ .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_), ctx.config.prefer_no_std)
.as_ref()
.map(mod_path_to_ast)
.zip(Some(trait_))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
index 2419fa11c..dbbc56958 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs
@@ -67,6 +67,7 @@ pub(crate) fn replace_qualified_name_with_use(
ctx.sema.db,
module,
ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
)
})
.flatten();
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
new file mode 100644
index 000000000..25c58d086
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_tuple.rs
@@ -0,0 +1,159 @@
+use syntax::{
+ ast::{self, edit::AstNodeEdit},
+ AstNode, T,
+};
+
+use crate::{AssistContext, AssistId, AssistKind, Assists};
+
+// Assist: unwrap_tuple
+//
+// Unwrap the tuple to different variables.
+//
+// ```
+// # //- minicore: result
+// fn main() {
+// $0let (foo, bar) = ("Foo", "Bar");
+// }
+// ```
+// ->
+// ```
+// fn main() {
+// let foo = "Foo";
+// let bar = "Bar";
+// }
+// ```
+pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+ let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
+ let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
+ let indent_level = let_stmt.indent_level().0 as usize;
+ let pat = let_stmt.pat()?;
+ let ty = let_stmt.ty();
+ let init = let_stmt.initializer()?;
+
+ // This only applies for tuple patterns, types, and initializers.
+ let tuple_pat = match pat {
+ ast::Pat::TuplePat(pat) => pat,
+ _ => return None,
+ };
+ let tuple_ty = ty.and_then(|it| match it {
+ ast::Type::TupleType(ty) => Some(ty),
+ _ => None,
+ });
+ let tuple_init = match init {
+ ast::Expr::TupleExpr(expr) => expr,
+ _ => return None,
+ };
+
+ if tuple_pat.fields().count() != tuple_init.fields().count() {
+ return None;
+ }
+ if let Some(tys) = &tuple_ty {
+ if tuple_pat.fields().count() != tys.fields().count() {
+ return None;
+ }
+ }
+
+ let parent = let_kw.parent()?;
+
+ acc.add(
+ AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
+ "Unwrap tuple",
+ let_kw.text_range(),
+ |edit| {
+ let indents = " ".repeat(indent_level);
+
+ // If there is an ascribed type, insert that type for each declaration,
+ // otherwise, omit that type.
+ if let Some(tys) = tuple_ty {
+ let mut zipped_decls = String::new();
+ for (pat, ty, expr) in
+ itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
+ {
+ zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
+ }
+ edit.replace(parent.text_range(), zipped_decls.trim());
+ } else {
+ let mut zipped_decls = String::new();
+ for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
+ zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
+ }
+ edit.replace(parent.text_range(), zipped_decls.trim());
+ }
+ },
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_assist;
+
+ use super::*;
+
+ #[test]
+ fn unwrap_tuples() {
+ check_assist(
+ unwrap_tuple,
+ r#"
+fn main() {
+ $0let (foo, bar) = ("Foo", "Bar");
+}
+"#,
+ r#"
+fn main() {
+ let foo = "Foo";
+ let bar = "Bar";
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_tuple,
+ r#"
+fn main() {
+ $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
+}
+"#,
+ r#"
+fn main() {
+ let foo = "Foo";
+ let bar = "Bar";
+ let baz = "Baz";
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn unwrap_tuple_with_types() {
+ check_assist(
+ unwrap_tuple,
+ r#"
+fn main() {
+ $0let (foo, bar): (u8, i32) = (5, 10);
+}
+"#,
+ r#"
+fn main() {
+ let foo: u8 = 5;
+ let bar: i32 = 10;
+}
+"#,
+ );
+
+ check_assist(
+ unwrap_tuple,
+ r#"
+fn main() {
+ $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
+}
+"#,
+ r#"
+fn main() {
+ let foo: u8 = 5;
+ let bar: i32 = 10;
+ let baz: f64 = 17.5;
+}
+"#,
+ );
+ }
+}
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 e52544db5..a07318cef 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -121,6 +121,7 @@ mod handlers {
mod convert_iter_for_each_to_for;
mod convert_let_else_to_match;
mod convert_tuple_struct_to_named_struct;
+ mod convert_named_struct_to_tuple_struct;
mod convert_to_guarded_return;
mod convert_two_arm_bool_match_to_matches_macro;
mod convert_while_to_loop;
@@ -136,6 +137,7 @@ mod handlers {
mod flip_binexpr;
mod flip_comma;
mod flip_trait_bound;
+ mod move_format_string_arg;
mod generate_constant;
mod generate_default_from_enum_variant;
mod generate_default_from_new;
@@ -188,6 +190,7 @@ mod handlers {
mod replace_turbofish_with_explicit_type;
mod split_import;
mod unmerge_match_arm;
+ mod unwrap_tuple;
mod sort_items;
mod toggle_ignore;
mod unmerge_use;
@@ -216,6 +219,7 @@ mod handlers {
convert_iter_for_each_to_for::convert_iter_for_each_to_for,
convert_iter_for_each_to_for::convert_for_loop_with_for_each,
convert_let_else_to_match::convert_let_else_to_match,
+ convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct,
convert_to_guarded_return::convert_to_guarded_return,
convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro,
@@ -254,6 +258,7 @@ mod handlers {
merge_imports::merge_imports,
merge_match_arms::merge_match_arms,
move_bounds::move_bounds_to_where_clause,
+ move_format_string_arg::move_format_string_arg,
move_guard::move_arm_cond_to_match_guard,
move_guard::move_guard_to_arm_body,
move_module_to_file::move_module_to_file,
@@ -289,6 +294,7 @@ mod handlers {
unnecessary_async::unnecessary_async,
unwrap_block::unwrap_block,
unwrap_result_return_type::unwrap_result_return_type,
+ unwrap_tuple::unwrap_tuple,
wrap_return_type_in_result::wrap_return_type_in_result,
// These are manually sorted for better priorities. By default,
// priority is determined by the size of the target range (smaller
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
index 9cd66c6b3..f7f2417d0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests.rs
@@ -29,6 +29,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
group: true,
skip_glob_imports: true,
},
+ prefer_no_std: false,
};
pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
@@ -95,8 +96,10 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) {
});
let actual = {
- let source_change =
- assist.source_change.expect("Assist did not contain any source changes");
+ let source_change = assist
+ .source_change
+ .filter(|it| !it.source_file_edits.is_empty() || !it.file_system_edits.is_empty())
+ .expect("Assist did not contain any source changes");
let mut actual = before;
if let Some(source_file_edit) = source_change.get_source_edit(file_id) {
source_file_edit.apply(&mut actual);
@@ -139,8 +142,10 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult<'_>, assist_la
match (assist, expected) {
(Some(assist), ExpectedResult::After(after)) => {
- let source_change =
- assist.source_change.expect("Assist did not contain any source changes");
+ let source_change = assist
+ .source_change
+ .filter(|it| !it.source_file_edits.is_empty() || !it.file_system_edits.is_empty())
+ .expect("Assist did not contain any source changes");
let skip_header = source_change.source_file_edits.len() == 1
&& source_change.file_system_edits.len() == 0;
@@ -227,6 +232,7 @@ fn assist_order_field_struct() {
assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
+ assert_eq!(assists.next().expect("expected assist").label, "Convert to tuple struct");
assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
}
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 227e2300f..2c4000efe 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
@@ -408,6 +408,47 @@ fn main() {
}
#[test]
+fn doctest_convert_named_struct_to_tuple_struct() {
+ check_doc_test(
+ "convert_named_struct_to_tuple_struct",
+ r#####"
+struct Point$0 { x: f32, y: f32 }
+
+impl Point {
+ pub fn new(x: f32, y: f32) -> Self {
+ Point { x, y }
+ }
+
+ pub fn x(&self) -> f32 {
+ self.x
+ }
+
+ pub fn y(&self) -> f32 {
+ self.y
+ }
+}
+"#####,
+ r#####"
+struct Point(f32, f32);
+
+impl Point {
+ pub fn new(x: f32, y: f32) -> Self {
+ Point(x, y)
+ }
+
+ pub fn x(&self) -> f32 {
+ self.0
+ }
+
+ pub fn y(&self) -> f32 {
+ self.1
+ }
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_convert_to_guarded_return() {
check_doc_test(
"convert_to_guarded_return",
@@ -1592,6 +1633,37 @@ fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
}
#[test]
+fn doctest_move_format_string_arg() {
+ check_doc_test(
+ "move_format_string_arg",
+ r#####"
+macro_rules! format_args {
+ ($lit:literal $(tt:tt)*) => { 0 },
+}
+macro_rules! print {
+ ($($arg:tt)*) => (std::io::_print(format_args!($($arg)*)));
+}
+
+fn main() {
+ print!("{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!("{}"$0, x + 1);
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_move_from_mod_rs() {
check_doc_test(
"move_from_mod_rs",
@@ -2356,6 +2428,25 @@ fn foo() -> i32 { 42i32 }
}
#[test]
+fn doctest_unwrap_tuple() {
+ check_doc_test(
+ "unwrap_tuple",
+ r#####"
+//- minicore: result
+fn main() {
+ $0let (foo, bar) = ("Foo", "Bar");
+}
+"#####,
+ r#####"
+fn main() {
+ let foo = "Foo";
+ let bar = "Bar";
+}
+"#####,
+ )
+}
+
+#[test]
fn doctest_wrap_return_type_in_result() {
check_doc_test(
"wrap_return_type_in_result",
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 4ab6e2627..db32e7182 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -2,8 +2,6 @@
use std::ops;
-use itertools::Itertools;
-
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
use hir::{db::HirDatabase, HirDisplay, Semantics};
use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
@@ -15,7 +13,7 @@ use syntax::{
edit_in_place::{AttrsOwnerEdit, Removable},
make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
},
- ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
+ ted, AstNode, AstToken, Direction, SourceFile,
SyntaxKind::*,
SyntaxNode, TextRange, TextSize, T,
};
@@ -333,10 +331,14 @@ fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
// FIXME: change the new fn checking to a more semantic approach when that's more
// viable (e.g. we process proc macros, etc)
// FIXME: this partially overlaps with `find_impl_block_*`
+
+/// `find_struct_impl` looks for impl of a struct, but this also has additional feature
+/// where it takes a list of function names and check if they exist inside impl_, if
+/// even one match is found, it returns None
pub(crate) fn find_struct_impl(
ctx: &AssistContext<'_>,
adt: &ast::Adt,
- name: &str,
+ names: &[String],
) -> Option<Option<ast::Impl>> {
let db = ctx.db();
let module = adt.syntax().parent()?;
@@ -364,7 +366,7 @@ pub(crate) fn find_struct_impl(
});
if let Some(ref impl_blk) = block {
- if has_fn(impl_blk, name) {
+ if has_any_fn(impl_blk, names) {
return None;
}
}
@@ -372,12 +374,12 @@ pub(crate) fn find_struct_impl(
Some(block)
}
-fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
+fn has_any_fn(imp: &ast::Impl, names: &[String]) -> bool {
if let Some(il) = imp.assoc_item_list() {
for item in il.assoc_items() {
if let ast::AssocItem::Fn(f) = item {
if let Some(name) = f.name() {
- if name.text().eq_ignore_ascii_case(rhs_name) {
+ if names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) {
return true;
}
}
@@ -424,34 +426,44 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
}
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
- let generic_params = adt.generic_param_list();
+ // Ensure lifetime params are before type & const params
+ let generic_params = adt.generic_param_list().map(|generic_params| {
+ let lifetime_params =
+ generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
+ let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+ // remove defaults since they can't be specified in impls
+ match param {
+ ast::TypeOrConstParam::Type(param) => {
+ let param = param.clone_for_update();
+ param.remove_default();
+ Some(ast::GenericParam::TypeParam(param))
+ }
+ ast::TypeOrConstParam::Const(param) => {
+ let param = param.clone_for_update();
+ param.remove_default();
+ Some(ast::GenericParam::ConstParam(param))
+ }
+ }
+ });
+
+ make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
+ });
+
+ // FIXME: use syntax::make & mutable AST apis instead
+ // `trait_text` and `code` can't be opaque blobs of text
let mut buf = String::with_capacity(code.len());
+
+ // Copy any cfg attrs from the original adt
buf.push_str("\n\n");
- adt.attrs()
- .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
- .for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
+ let cfg_attrs = adt
+ .attrs()
+ .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
+ cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
+
+ // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
buf.push_str("impl");
if let Some(generic_params) = &generic_params {
- let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
- let toc_params = generic_params.type_or_const_params().map(|toc_param| {
- let type_param = match toc_param {
- ast::TypeOrConstParam::Type(x) => x,
- ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
- };
- let mut buf = String::new();
- if let Some(it) = type_param.name() {
- format_to!(buf, "{}", it.syntax());
- }
- if let Some(it) = type_param.colon_token() {
- format_to!(buf, "{} ", it);
- }
- if let Some(it) = type_param.type_bound_list() {
- format_to!(buf, "{}", it.syntax());
- }
- buf
- });
- let generics = lifetimes.chain(toc_params).format(", ");
- format_to!(buf, "<{}>", generics);
+ format_to!(buf, "{generic_params}");
}
buf.push(' ');
if let Some(trait_text) = trait_text {
@@ -460,23 +472,15 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
}
buf.push_str(&adt.name().unwrap().text());
if let Some(generic_params) = generic_params {
- let lifetime_params = generic_params
- .lifetime_params()
- .filter_map(|it| it.lifetime())
- .map(|it| SmolStr::from(it.text()));
- let toc_params = generic_params
- .type_or_const_params()
- .filter_map(|it| it.name())
- .map(|it| SmolStr::from(it.text()));
- format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
+ format_to!(buf, "{}", generic_params.to_generic_args());
}
match adt.where_clause() {
Some(where_clause) => {
- format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
+ format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
}
None => {
- format_to!(buf, " {{\n{}\n}}", code);
+ format_to!(buf, " {{\n{code}\n}}");
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
index 8c9d6b228..75835bce9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-completion/Cargo.toml
@@ -11,10 +11,10 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
+itertools = "0.10.5"
-once_cell = "1.12.0"
-smallvec = "1.9.0"
+once_cell = "1.15.0"
+smallvec = "1.10.0"
stdx = { path = "../stdx", version = "0.0.0" }
syntax = { path = "../syntax", version = "0.0.0" }
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 55c3e2839..296dfc142 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs
@@ -19,6 +19,7 @@ pub(crate) mod snippet;
pub(crate) mod r#type;
pub(crate) mod use_;
pub(crate) mod vis;
+pub(crate) mod env_vars;
use std::iter;
@@ -551,7 +552,11 @@ fn enum_variants_with_paths(
}
for variant in variants {
- if let Some(path) = ctx.module.find_use_path(ctx.db, hir::ModuleDef::from(variant)) {
+ if let Some(path) = ctx.module.find_use_path(
+ ctx.db,
+ hir::ModuleDef::from(variant),
+ ctx.config.prefer_no_std,
+ ) {
// Variants with trivial paths are already added by the existing completion logic,
// so we should avoid adding these twice
if path.segments().len() > 1 {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
new file mode 100644
index 000000000..09e95e53d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/env_vars.rs
@@ -0,0 +1,150 @@
+//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
+use hir::Semantics;
+use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
+use syntax::ast::{self, IsString};
+
+use crate::{
+ completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
+};
+
+const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
+ ("CARGO","Path to the cargo binary performing the build"),
+ ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"),
+ ("CARGO_PKG_VERSION","The full version of your package"),
+ ("CARGO_PKG_VERSION_MAJOR","The major version of your package"),
+ ("CARGO_PKG_VERSION_MINOR","The minor version of your package"),
+ ("CARGO_PKG_VERSION_PATCH","The patch version of your package"),
+ ("CARGO_PKG_VERSION_PRE","The pre-release version of your package"),
+ ("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"),
+ ("CARGO_PKG_NAME","The name of your package"),
+ ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"),
+ ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"),
+ ("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"),
+ ("CARGO_PKG_LICENSE","The license from the manifest of your package"),
+ ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"),
+ ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"),
+ ("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"),
+ ("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"),
+ ("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"),
+ ("CARGO_TARGET_TMPDIR","Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code")
+];
+
+pub(crate) fn complete_cargo_env_vars(
+ acc: &mut Completions,
+ ctx: &CompletionContext<'_>,
+ expanded: &ast::String,
+) -> Option<()> {
+ guard_env_macro(expanded, &ctx.sema)?;
+ let range = expanded.text_range_between_quotes()?;
+
+ CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| {
+ let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
+ item.detail(*detail);
+ item.add_to(acc);
+ });
+
+ Some(())
+}
+
+fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> {
+ let call = macro_call_for_string_token(string)?;
+ let name = call.path()?.segment()?.name_ref()?;
+ let makro = semantics.resolve_macro_call(&call)?;
+ let db = semantics.db;
+
+ match name.text().as_str() {
+ "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
+ _ => None,
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::{check_edit, completion_list};
+
+ fn check(macro_name: &str) {
+ check_edit(
+ "CARGO_BIN_NAME",
+ &format!(
+ r#"
+ #[rustc_builtin_macro]
+ macro_rules! {} {{
+ ($var:literal) => {{ 0 }}
+ }}
+
+ fn main() {{
+ let foo = {}!("CAR$0");
+ }}
+ "#,
+ macro_name, macro_name
+ ),
+ &format!(
+ r#"
+ #[rustc_builtin_macro]
+ macro_rules! {} {{
+ ($var:literal) => {{ 0 }}
+ }}
+
+ fn main() {{
+ let foo = {}!("CARGO_BIN_NAME");
+ }}
+ "#,
+ macro_name, macro_name
+ ),
+ );
+ }
+ #[test]
+ fn completes_env_variable_in_env() {
+ check("env")
+ }
+
+ #[test]
+ fn completes_env_variable_in_option_env() {
+ check("option_env");
+ }
+
+ #[test]
+ fn doesnt_complete_in_random_strings() {
+ let fixture = r#"
+ fn main() {
+ let foo = "CA$0";
+ }
+ "#;
+
+ let completions = completion_list(fixture);
+ assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
+ }
+
+ #[test]
+ fn doesnt_complete_in_random_macro() {
+ let fixture = r#"
+ macro_rules! bar {
+ ($($arg:tt)*) => { 0 }
+ }
+
+ fn main() {
+ let foo = bar!("CA$0");
+
+ }
+ "#;
+
+ let completions = completion_list(fixture);
+ assert!(completions.is_empty(), "Completions weren't empty: {}", completions);
+ }
+
+ #[test]
+ fn doesnt_complete_for_shadowed_macro() {
+ let fixture = r#"
+ macro_rules! env {
+ ($var:literal) => { 0 }
+ }
+
+ fn main() {
+ let foo = env!("CA$0");
+ }
+ "#;
+
+ let completions = completion_list(fixture);
+ assert!(completions.is_empty(), "Completions weren't empty: {}", completions)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 588b52cc1..3192b21cf 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -165,7 +165,11 @@ pub(crate) fn complete_expr_path(
hir::Adt::Struct(strukt) => {
let path = ctx
.module
- .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
+ .find_use_path(
+ ctx.db,
+ hir::ModuleDef::from(strukt),
+ ctx.config.prefer_no_std,
+ )
.filter(|it| it.len() > 1);
acc.add_struct_literal(ctx, path_ctx, strukt, path, None);
@@ -183,7 +187,11 @@ pub(crate) fn complete_expr_path(
hir::Adt::Union(un) => {
let path = ctx
.module
- .find_use_path(ctx.db, hir::ModuleDef::from(un))
+ .find_use_path(
+ ctx.db,
+ hir::ModuleDef::from(un),
+ ctx.config.prefer_no_std,
+ )
.filter(|it| it.len() > 1);
acc.add_union_literal(ctx, un, path, None);
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
index f04cc15d7..364969af9 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs
@@ -262,7 +262,11 @@ fn import_on_the_fly(
acc.add_all(
import_assets
- .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+ .search_for_imports(
+ &ctx.sema,
+ ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
+ )
.into_iter()
.filter(ns_filter)
.filter(|import| {
@@ -306,7 +310,11 @@ fn import_on_the_fly_pat_(
acc.add_all(
import_assets
- .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+ .search_for_imports(
+ &ctx.sema,
+ ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
+ )
.into_iter()
.filter(ns_filter)
.filter(|import| {
@@ -344,7 +352,7 @@ fn import_on_the_fly_method(
let user_input_lowercased = potential_import_name.to_lowercase();
import_assets
- .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind)
+ .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
.into_iter()
.filter(|import| {
!ctx.is_item_hidden(&import.item_to_import)
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 785db6fde..e82cbfdcb 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
@@ -38,7 +38,7 @@ use ide_db::{
};
use syntax::{
ast::{self, edit_in_place::AttrsOwnerEdit},
- AstNode, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, T,
+ AstNode, SyntaxElement, SyntaxKind, TextRange, T,
};
use text_edit::TextEdit;
@@ -85,20 +85,36 @@ fn complete_trait_impl_name(
name: &Option<ast::Name>,
kind: ImplCompletionKind,
) -> Option<()> {
- let token = ctx.token.clone();
let item = match name {
Some(name) => name.syntax().parent(),
- None => if token.kind() == SyntaxKind::WHITESPACE { token.prev_token()? } else { token }
- .parent(),
+ None => {
+ let token = &ctx.token;
+ match token.kind() {
+ SyntaxKind::WHITESPACE => token.prev_token()?,
+ _ => token.clone(),
+ }
+ .parent()
+ }
}?;
- complete_trait_impl(
- acc,
- ctx,
- kind,
- replacement_range(ctx, &item),
- // item -> ASSOC_ITEM_LIST -> IMPL
- &ast::Impl::cast(item.parent()?.parent()?)?,
- );
+ let item = ctx.sema.original_syntax_node(&item)?;
+ // item -> ASSOC_ITEM_LIST -> IMPL
+ let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
+ let replacement_range = {
+ // ctx.sema.original_ast_node(item)?;
+ let first_child = item
+ .children_with_tokens()
+ .find(|child| {
+ !matches!(
+ child.kind(),
+ SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
+ )
+ })
+ .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
+
+ TextRange::new(first_child.text_range().start(), ctx.source_range().end())
+ };
+
+ complete_trait_impl(acc, ctx, kind, replacement_range, &impl_def);
Some(())
}
@@ -341,17 +357,6 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String {
syntax.trim_end().to_owned()
}
-fn replacement_range(ctx: &CompletionContext<'_>, item: &SyntaxNode) -> TextRange {
- let first_child = item
- .children_with_tokens()
- .find(|child| {
- !matches!(child.kind(), SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR)
- })
- .unwrap_or_else(|| SyntaxElement::Node(item.clone()));
-
- TextRange::new(first_child.text_range().start(), ctx.source_range().end())
-}
-
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
index 71d2d9d43..58d5bf114 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs
@@ -145,6 +145,7 @@ pub(crate) fn complete_pattern_path(
u.ty(ctx.db)
}
hir::PathResolution::Def(hir::ModuleDef::BuiltinType(ty)) => ty.ty(ctx.db),
+ hir::PathResolution::Def(hir::ModuleDef::TypeAlias(ty)) => ty.ty(ctx.db),
_ => return,
};
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
index b273a4cb5..b43bdb9ab 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix/format_like.rs
@@ -16,8 +16,11 @@
//
// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[]
-use ide_db::SnippetCap;
-use syntax::ast::{self, AstToken};
+use ide_db::{
+ syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders},
+ SnippetCap,
+};
+use syntax::{ast, AstToken};
use crate::{
completions::postfix::build_postfix_snippet_builder, context::CompletionContext, Completions,
@@ -43,250 +46,24 @@ pub(crate) fn add_format_like_completions(
cap: SnippetCap,
receiver_text: &ast::String,
) {
- let input = match string_literal_contents(receiver_text) {
- // It's not a string literal, do not parse input.
- Some(input) => input,
- None => return,
- };
-
let postfix_snippet = match build_postfix_snippet_builder(ctx, cap, dot_receiver) {
Some(it) => it,
None => return,
};
- let mut parser = FormatStrParser::new(input);
- if parser.parse().is_ok() {
+ if let Ok((out, exprs)) = parse_format_exprs(receiver_text.text()) {
+ let exprs = with_placeholders(exprs);
for (label, macro_name) in KINDS {
- let snippet = parser.to_suggestion(macro_name);
+ let snippet = format!(r#"{}({}, {})"#, macro_name, out, exprs.join(", "));
postfix_snippet(label, macro_name, &snippet).add_to(acc);
}
}
}
-/// Checks whether provided item is a string literal.
-fn string_literal_contents(item: &ast::String) -> Option<String> {
- let item = item.text();
- if item.len() >= 2 && item.starts_with('\"') && item.ends_with('\"') {
- return Some(item[1..item.len() - 1].to_owned());
- }
-
- None
-}
-
-/// Parser for a format-like string. It is more allowing in terms of string contents,
-/// as we expect variable placeholders to be filled with expressions.
-#[derive(Debug)]
-pub(crate) struct FormatStrParser {
- input: String,
- output: String,
- extracted_expressions: Vec<String>,
- state: State,
- parsed: bool,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
-enum State {
- NotExpr,
- MaybeExpr,
- Expr,
- MaybeIncorrect,
- FormatOpts,
-}
-
-impl FormatStrParser {
- pub(crate) fn new(input: String) -> Self {
- Self {
- input,
- output: String::new(),
- extracted_expressions: Vec::new(),
- state: State::NotExpr,
- parsed: false,
- }
- }
-
- pub(crate) fn parse(&mut self) -> Result<(), ()> {
- let mut current_expr = String::new();
-
- let mut placeholder_id = 1;
-
- // Count of open braces inside of an expression.
- // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
- // "{MyStruct { val_a: 0, val_b: 1 }}".
- let mut inexpr_open_count = 0;
-
- // We need to escape '\' and '$'. See the comments on `get_receiver_text()` for detail.
- let mut chars = self.input.chars().peekable();
- while let Some(chr) = chars.next() {
- match (self.state, chr) {
- (State::NotExpr, '{') => {
- self.output.push(chr);
- self.state = State::MaybeExpr;
- }
- (State::NotExpr, '}') => {
- self.output.push(chr);
- self.state = State::MaybeIncorrect;
- }
- (State::NotExpr, _) => {
- if matches!(chr, '\\' | '$') {
- self.output.push('\\');
- }
- self.output.push(chr);
- }
- (State::MaybeIncorrect, '}') => {
- // It's okay, we met "}}".
- self.output.push(chr);
- self.state = State::NotExpr;
- }
- (State::MaybeIncorrect, _) => {
- // Error in the string.
- return Err(());
- }
- (State::MaybeExpr, '{') => {
- self.output.push(chr);
- self.state = State::NotExpr;
- }
- (State::MaybeExpr, '}') => {
- // This is an empty sequence '{}'. Replace it with placeholder.
- self.output.push(chr);
- self.extracted_expressions.push(format!("${}", placeholder_id));
- placeholder_id += 1;
- self.state = State::NotExpr;
- }
- (State::MaybeExpr, _) => {
- if matches!(chr, '\\' | '$') {
- current_expr.push('\\');
- }
- current_expr.push(chr);
- self.state = State::Expr;
- }
- (State::Expr, '}') => {
- if inexpr_open_count == 0 {
- self.output.push(chr);
- self.extracted_expressions.push(current_expr.trim().into());
- current_expr = String::new();
- self.state = State::NotExpr;
- } else {
- // We're closing one brace met before inside of the expression.
- current_expr.push(chr);
- inexpr_open_count -= 1;
- }
- }
- (State::Expr, ':') if chars.peek().copied() == Some(':') => {
- // path separator
- current_expr.push_str("::");
- chars.next();
- }
- (State::Expr, ':') => {
- if inexpr_open_count == 0 {
- // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
- self.output.push(chr);
- self.extracted_expressions.push(current_expr.trim().into());
- current_expr = String::new();
- self.state = State::FormatOpts;
- } else {
- // We're inside of braced expression, assume that it's a struct field name/value delimiter.
- current_expr.push(chr);
- }
- }
- (State::Expr, '{') => {
- current_expr.push(chr);
- inexpr_open_count += 1;
- }
- (State::Expr, _) => {
- if matches!(chr, '\\' | '$') {
- current_expr.push('\\');
- }
- current_expr.push(chr);
- }
- (State::FormatOpts, '}') => {
- self.output.push(chr);
- self.state = State::NotExpr;
- }
- (State::FormatOpts, _) => {
- if matches!(chr, '\\' | '$') {
- self.output.push('\\');
- }
- self.output.push(chr);
- }
- }
- }
-
- if self.state != State::NotExpr {
- return Err(());
- }
-
- self.parsed = true;
- Ok(())
- }
-
- pub(crate) fn to_suggestion(&self, macro_name: &str) -> String {
- assert!(self.parsed, "Attempt to get a suggestion from not parsed expression");
-
- let expressions_as_string = self.extracted_expressions.join(", ");
- format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string)
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
- use expect_test::{expect, Expect};
-
- fn check(input: &str, expect: &Expect) {
- let mut parser = FormatStrParser::new((*input).to_owned());
- let outcome_repr = if parser.parse().is_ok() {
- // Parsing should be OK, expected repr is "string; expr_1, expr_2".
- if parser.extracted_expressions.is_empty() {
- parser.output
- } else {
- format!("{}; {}", parser.output, parser.extracted_expressions.join(", "))
- }
- } else {
- // Parsing should fail, expected repr is "-".
- "-".to_owned()
- };
-
- expect.assert_eq(&outcome_repr);
- }
-
- #[test]
- fn format_str_parser() {
- let test_vector = &[
- ("no expressions", expect![["no expressions"]]),
- (r"no expressions with \$0$1", expect![r"no expressions with \\\$0\$1"]),
- ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
- ("{expr:?}", expect![["{:?}; expr"]]),
- ("{expr:1$}", expect![[r"{:1\$}; expr"]]),
- ("{$0}", expect![[r"{}; \$0"]]),
- ("{malformed", expect![["-"]]),
- ("malformed}", expect![["-"]]),
- ("{{correct", expect![["{{correct"]]),
- ("correct}}", expect![["correct}}"]]),
- ("{correct}}}", expect![["{}}}; correct"]]),
- ("{correct}}}}}", expect![["{}}}}}; correct"]]),
- ("{incorrect}}", expect![["-"]]),
- ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
- ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
- (
- "{SomeStruct { val_a: 0, val_b: 1 }}",
- expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
- ),
- ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
- (
- "{SomeStruct { val_a: 0, val_b: 1 }:?}",
- expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
- ),
- ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
- ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
- ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
- ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
- ];
-
- for (input, output) in test_vector {
- check(input, output)
- }
- }
#[test]
fn test_into_suggestion() {
@@ -302,10 +79,10 @@ mod tests {
];
for (kind, input, output) in test_vector {
- let mut parser = FormatStrParser::new((*input).to_owned());
- parser.parse().expect("Parsing must succeed");
-
- assert_eq!(&parser.to_suggestion(*kind), output);
+ let (parsed_string, exprs) = parse_format_exprs(input).unwrap();
+ let exprs = with_placeholders(exprs);
+ let snippet = format!(r#"{}("{}", {})"#, kind, parsed_string, exprs.join(", "));
+ assert_eq!(&snippet, output);
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
index 80d6af281..a0f5e81b4 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
@@ -17,6 +17,7 @@ pub struct CompletionConfig {
pub callable: Option<CallableSnippets>,
pub snippet_cap: Option<SnippetCap>,
pub insert_use: InsertUseConfig,
+ pub prefer_no_std: bool,
pub snippets: Vec<Snippet>,
}
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 a5e854b74..9850813a0 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -1,4 +1,4 @@
-//! See `CompletionContext` structure.
+//! See [`CompletionContext`] structure.
mod analysis;
#[cfg(test)]
@@ -23,7 +23,10 @@ use syntax::{
};
use text_edit::Indel;
-use crate::CompletionConfig;
+use crate::{
+ context::analysis::{expand_and_analyze, AnalysisResult},
+ CompletionConfig,
+};
const COMPLETION_MARKER: &str = "intellijRulezz";
@@ -561,15 +564,27 @@ impl<'a> CompletionContext<'a> {
let edit = Indel::insert(offset, COMPLETION_MARKER.to_string());
parse.reparse(&edit).tree()
};
- let fake_ident_token =
- file_with_fake_ident.syntax().token_at_offset(offset).right_biased()?;
+ // always pick the token to the immediate left of the cursor, as that is what we are actually
+ // completing on
let original_token = original_file.syntax().token_at_offset(offset).left_biased()?;
- let token = sema.descend_into_macros_single(original_token.clone());
+
+ let AnalysisResult {
+ analysis,
+ expected: (expected_type, expected_name),
+ qualifier_ctx,
+ token,
+ offset,
+ } = expand_and_analyze(
+ &sema,
+ original_file.syntax().clone(),
+ file_with_fake_ident.syntax().clone(),
+ offset,
+ &original_token,
+ )?;
// adjust for macro input, this still fails if there is no token written yet
- let scope_offset = if original_token == token { offset } else { token.text_range().end() };
- let scope = sema.scope_at_offset(&token.parent()?, scope_offset)?;
+ let scope = sema.scope_at_offset(&token.parent()?, offset)?;
let krate = scope.krate();
let module = scope.module();
@@ -583,7 +598,7 @@ impl<'a> CompletionContext<'a> {
let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
- let mut ctx = CompletionContext {
+ let ctx = CompletionContext {
sema,
scope,
db,
@@ -593,19 +608,13 @@ impl<'a> CompletionContext<'a> {
token,
krate,
module,
- expected_name: None,
- expected_type: None,
- qualifier_ctx: Default::default(),
+ expected_name,
+ expected_type,
+ qualifier_ctx,
locals,
depth_from_crate_root,
};
- let ident_ctx = ctx.expand_and_analyze(
- original_file.syntax().clone(),
- file_with_fake_ident.syntax().clone(),
- offset,
- fake_ident_token,
- )?;
- Some((ctx, ident_ctx))
+ Some((ctx, analysis))
}
}
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 01dd9a234..04111ec7e 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
@@ -11,1063 +11,1094 @@ use syntax::{
};
use crate::context::{
- AttrCtx, CompletionAnalysis, CompletionContext, DotAccess, DotAccessKind, ExprCtx,
- ItemListKind, LifetimeContext, LifetimeKind, NameContext, NameKind, NameRefContext,
- NameRefKind, ParamContext, ParamKind, PathCompletionCtx, PathKind, PatternContext,
- PatternRefutability, Qualified, QualifierCtx, TypeAscriptionTarget, TypeLocation,
- COMPLETION_MARKER,
+ AttrCtx, CompletionAnalysis, DotAccess, DotAccessKind, ExprCtx, ItemListKind, LifetimeContext,
+ LifetimeKind, NameContext, NameKind, NameRefContext, NameRefKind, ParamContext, ParamKind,
+ PathCompletionCtx, PathKind, PatternContext, PatternRefutability, Qualified, QualifierCtx,
+ TypeAscriptionTarget, TypeLocation, COMPLETION_MARKER,
};
-impl<'a> CompletionContext<'a> {
- /// Expand attributes and macro calls at the current cursor position for both the original file
- /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
- /// and speculative states stay in sync.
- pub(super) fn expand_and_analyze(
- &mut self,
- mut original_file: SyntaxNode,
- mut speculative_file: SyntaxNode,
- mut offset: TextSize,
- mut fake_ident_token: SyntaxToken,
- ) -> Option<CompletionAnalysis> {
- let _p = profile::span("CompletionContext::expand_and_fill");
- let mut derive_ctx = None;
-
- 'expansion: loop {
- let parent_item =
- |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
- let ancestor_items = iter::successors(
- Option::zip(
- find_node_at_offset::<ast::Item>(&original_file, offset),
- find_node_at_offset::<ast::Item>(&speculative_file, offset),
+struct ExpansionResult {
+ original_file: SyntaxNode,
+ speculative_file: SyntaxNode,
+ offset: TextSize,
+ fake_ident_token: SyntaxToken,
+ derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
+}
+
+pub(super) struct AnalysisResult {
+ pub(super) analysis: CompletionAnalysis,
+ pub(super) expected: (Option<Type>, Option<ast::NameOrNameRef>),
+ pub(super) qualifier_ctx: QualifierCtx,
+ pub(super) token: SyntaxToken,
+ pub(super) offset: TextSize,
+}
+
+pub(super) fn expand_and_analyze(
+ sema: &Semantics<'_, RootDatabase>,
+ original_file: SyntaxNode,
+ speculative_file: SyntaxNode,
+ offset: TextSize,
+ original_token: &SyntaxToken,
+) -> Option<AnalysisResult> {
+ // as we insert after the offset, right biased will *always* pick the identifier no matter
+ // if there is an ident already typed or not
+ let fake_ident_token = speculative_file.token_at_offset(offset).right_biased()?;
+ // the relative offset between the cursor and the *identifier* token we are completing on
+ let relative_offset = offset - fake_ident_token.text_range().start();
+ // make the offset point to the start of the original token, as that is what the
+ // intermediate offsets calculated in expansion always points to
+ let offset = offset - relative_offset;
+ let expansion = expand(sema, original_file, speculative_file, offset, fake_ident_token);
+ // add the relative offset back, so that left_biased finds the proper token
+ let offset = expansion.offset + relative_offset;
+ let token = expansion.original_file.token_at_offset(offset).left_biased()?;
+
+ analyze(sema, expansion, original_token, &token).map(|(analysis, expected, qualifier_ctx)| {
+ AnalysisResult { analysis, expected, qualifier_ctx, token, offset }
+ })
+}
+
+/// Expand attributes and macro calls at the current cursor position for both the original file
+/// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original
+/// and speculative states stay in sync.
+fn expand(
+ sema: &Semantics<'_, RootDatabase>,
+ mut original_file: SyntaxNode,
+ mut speculative_file: SyntaxNode,
+ mut offset: TextSize,
+ mut fake_ident_token: SyntaxToken,
+) -> ExpansionResult {
+ let _p = profile::span("CompletionContext::expand");
+ let mut derive_ctx = None;
+
+ 'expansion: loop {
+ let parent_item =
+ |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast);
+ let ancestor_items = iter::successors(
+ Option::zip(
+ find_node_at_offset::<ast::Item>(&original_file, offset),
+ find_node_at_offset::<ast::Item>(&speculative_file, offset),
+ ),
+ |(a, b)| parent_item(a).zip(parent_item(b)),
+ );
+
+ // first try to expand attributes as these are always the outermost macro calls
+ 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
+ match (
+ sema.expand_attr_macro(&actual_item),
+ sema.speculative_expand_attr_macro(
+ &actual_item,
+ &item_with_fake_ident,
+ fake_ident_token.clone(),
),
- |(a, b)| parent_item(a).zip(parent_item(b)),
- );
-
- // first try to expand attributes as these are always the outermost macro calls
- 'ancestors: for (actual_item, item_with_fake_ident) in ancestor_items {
- match (
- self.sema.expand_attr_macro(&actual_item),
- self.sema.speculative_expand_attr_macro(
- &actual_item,
- &item_with_fake_ident,
- fake_ident_token.clone(),
- ),
- ) {
- // maybe parent items have attributes, so continue walking the ancestors
- (None, None) => continue 'ancestors,
- // successful expansions
- (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
- let new_offset = fake_mapped_token.text_range().start();
- if new_offset > actual_expansion.text_range().end() {
- // offset outside of bounds from the original expansion,
- // stop here to prevent problems from happening
- break 'expansion;
- }
- original_file = actual_expansion;
- speculative_file = fake_expansion;
- fake_ident_token = fake_mapped_token;
- offset = new_offset;
- continue 'expansion;
+ ) {
+ // maybe parent items have attributes, so continue walking the ancestors
+ (None, None) => continue 'ancestors,
+ // successful expansions
+ (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
+ let new_offset = fake_mapped_token.text_range().start();
+ if new_offset > actual_expansion.text_range().end() {
+ // offset outside of bounds from the original expansion,
+ // stop here to prevent problems from happening
+ break 'expansion;
}
- // exactly one expansion failed, inconsistent state so stop expanding completely
- _ => break 'expansion,
+ original_file = actual_expansion;
+ speculative_file = fake_expansion;
+ fake_ident_token = fake_mapped_token;
+ offset = new_offset;
+ continue 'expansion;
}
+ // exactly one expansion failed, inconsistent state so stop expanding completely
+ _ => break 'expansion,
}
+ }
- // No attributes have been expanded, so look for macro_call! token trees or derive token trees
- let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
- Some(it) => it,
- None => break 'expansion,
- };
- let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
- Some(it) => it,
- None => break 'expansion,
- };
+ // No attributes have been expanded, so look for macro_call! token trees or derive token trees
+ let orig_tt = match find_node_at_offset::<ast::TokenTree>(&original_file, offset) {
+ Some(it) => it,
+ None => break 'expansion,
+ };
+ let spec_tt = match find_node_at_offset::<ast::TokenTree>(&speculative_file, offset) {
+ Some(it) => it,
+ None => break 'expansion,
+ };
- // Expand pseudo-derive expansion
- if let (Some(orig_attr), Some(spec_attr)) = (
- orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
- spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
+ // Expand pseudo-derive expansion
+ if let (Some(orig_attr), Some(spec_attr)) = (
+ orig_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
+ spec_tt.syntax().parent().and_then(ast::Meta::cast).and_then(|it| it.parent_attr()),
+ ) {
+ if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
+ sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
+ sema.speculative_expand_derive_as_pseudo_attr_macro(
+ &orig_attr,
+ &spec_attr,
+ fake_ident_token.clone(),
+ ),
) {
- if let (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) = (
- self.sema.expand_derive_as_pseudo_attr_macro(&orig_attr),
- self.sema.speculative_expand_derive_as_pseudo_attr_macro(
- &orig_attr,
- &spec_attr,
- fake_ident_token.clone(),
- ),
- ) {
- derive_ctx = Some((
- actual_expansion,
- fake_expansion,
- fake_mapped_token.text_range().start(),
- orig_attr,
- ));
- }
- // at this point we won't have any more successful expansions, so stop
+ derive_ctx = Some((
+ actual_expansion,
+ fake_expansion,
+ fake_mapped_token.text_range().start(),
+ orig_attr,
+ ));
+ }
+ // at this point we won't have any more successful expansions, so stop
+ break 'expansion;
+ }
+
+ // Expand fn-like macro calls
+ if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
+ orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
+ spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
+ ) {
+ let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
+ let mac_call_path1 =
+ macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
+
+ // inconsistent state, stop expanding
+ if mac_call_path0 != mac_call_path1 {
break 'expansion;
}
+ let speculative_args = match macro_call_with_fake_ident.token_tree() {
+ Some(tt) => tt,
+ None => break 'expansion,
+ };
- // Expand fn-like macro calls
- if let (Some(actual_macro_call), Some(macro_call_with_fake_ident)) = (
- orig_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
- spec_tt.syntax().ancestors().find_map(ast::MacroCall::cast),
+ match (
+ sema.expand(&actual_macro_call),
+ sema.speculative_expand(
+ &actual_macro_call,
+ &speculative_args,
+ fake_ident_token.clone(),
+ ),
) {
- let mac_call_path0 = actual_macro_call.path().as_ref().map(|s| s.syntax().text());
- let mac_call_path1 =
- macro_call_with_fake_ident.path().as_ref().map(|s| s.syntax().text());
-
- // inconsistent state, stop expanding
- if mac_call_path0 != mac_call_path1 {
- break 'expansion;
- }
- let speculative_args = match macro_call_with_fake_ident.token_tree() {
- Some(tt) => tt,
- None => break 'expansion,
- };
-
- match (
- self.sema.expand(&actual_macro_call),
- self.sema.speculative_expand(
- &actual_macro_call,
- &speculative_args,
- fake_ident_token.clone(),
- ),
- ) {
- // successful expansions
- (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
- let new_offset = fake_mapped_token.text_range().start();
- if new_offset > actual_expansion.text_range().end() {
- // offset outside of bounds from the original expansion,
- // stop here to prevent problems from happening
- break 'expansion;
- }
- original_file = actual_expansion;
- speculative_file = fake_expansion;
- fake_ident_token = fake_mapped_token;
- offset = new_offset;
- continue 'expansion;
+ // successful expansions
+ (Some(actual_expansion), Some((fake_expansion, fake_mapped_token))) => {
+ let new_offset = fake_mapped_token.text_range().start();
+ if new_offset > actual_expansion.text_range().end() {
+ // offset outside of bounds from the original expansion,
+ // stop here to prevent problems from happening
+ break 'expansion;
}
- // at least on expansion failed, we won't have anything to expand from this point
- // onwards so break out
- _ => break 'expansion,
+ original_file = actual_expansion;
+ speculative_file = fake_expansion;
+ fake_ident_token = fake_mapped_token;
+ offset = new_offset;
+ continue 'expansion;
}
+ // at least on expansion failed, we won't have anything to expand from this point
+ // onwards so break out
+ _ => break 'expansion,
}
-
- // none of our states have changed so stop the loop
- break 'expansion;
}
- self.analyze(&original_file, speculative_file, offset, derive_ctx)
+ // none of our states have changed so stop the loop
+ break 'expansion;
}
+ ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx }
+}
- /// Calculate the expected type and name of the cursor position.
- fn expected_type_and_name(
- &self,
- name_like: &ast::NameLike,
- ) -> (Option<Type>, Option<NameOrNameRef>) {
- let mut node = match self.token.parent() {
- Some(it) => it,
- None => return (None, None),
- };
+/// Fill the completion context, this is what does semantic reasoning about the surrounding context
+/// of the completion location.
+fn analyze(
+ sema: &Semantics<'_, RootDatabase>,
+ expansion_result: ExpansionResult,
+ original_token: &SyntaxToken,
+ self_token: &SyntaxToken,
+) -> Option<(CompletionAnalysis, (Option<Type>, Option<ast::NameOrNameRef>), QualifierCtx)> {
+ let _p = profile::span("CompletionContext::analyze");
+ let ExpansionResult { original_file, speculative_file, offset, fake_ident_token, derive_ctx } =
+ expansion_result;
+ let syntax_element = NodeOrToken::Token(fake_ident_token);
+ if is_in_token_of_for_loop(syntax_element.clone()) {
+ // for pat $0
+ // there is nothing to complete here except `in` keyword
+ // don't bother populating the context
+ // FIXME: the completion calculations should end up good enough
+ // such that this special case becomes unnecessary
+ return None;
+ }
- let strip_refs = |mut ty: Type| match name_like {
- ast::NameLike::NameRef(n) => {
- let p = match n.syntax().parent() {
- Some(it) => it,
- None => return ty,
- };
- let top_syn = match_ast! {
- match p {
- ast::FieldExpr(e) => e
- .syntax()
- .ancestors()
- .map_while(ast::FieldExpr::cast)
- .last()
- .map(|it| it.syntax().clone()),
- ast::PathSegment(e) => e
- .syntax()
- .ancestors()
- .skip(1)
- .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind()))
- .find_map(ast::PathExpr::cast)
- .map(|it| it.syntax().clone()),
- _ => None
- }
+ // Overwrite the path kind for derives
+ if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
+ if let Some(ast::NameLike::NameRef(name_ref)) =
+ find_node_at_offset(&file_with_fake_ident, offset)
+ {
+ let parent = name_ref.syntax().parent()?;
+ let (mut nameref_ctx, _) = classify_name_ref(&sema, &original_file, name_ref, parent)?;
+ if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
+ path_ctx.kind = PathKind::Derive {
+ existing_derives: sema
+ .resolve_derive_macro(&origin_attr)
+ .into_iter()
+ .flatten()
+ .flatten()
+ .collect(),
};
- let top_syn = match top_syn {
- Some(it) => it,
- None => return ty,
- };
- for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) {
- cov_mark::hit!(expected_type_fn_param_ref);
- ty = ty.strip_reference();
- }
- ty
}
- _ => ty,
- };
+ return Some((
+ CompletionAnalysis::NameRef(nameref_ctx),
+ (None, None),
+ QualifierCtx::default(),
+ ));
+ }
+ return None;
+ }
- loop {
- break match_ast! {
- match node {
- ast::LetStmt(it) => {
- cov_mark::hit!(expected_type_let_with_leading_char);
- cov_mark::hit!(expected_type_let_without_leading_char);
- let ty = it.pat()
- .and_then(|pat| self.sema.type_of_pat(&pat))
- .or_else(|| it.initializer().and_then(|it| self.sema.type_of_expr(&it)))
- .map(TypeInfo::original);
- let name = match it.pat() {
- Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
- Some(_) | None => None,
- };
-
- (ty, name)
- },
- ast::LetExpr(it) => {
- cov_mark::hit!(expected_type_if_let_without_leading_char);
- let ty = it.pat()
- .and_then(|pat| self.sema.type_of_pat(&pat))
- .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
- .map(TypeInfo::original);
- (ty, None)
- },
- ast::ArgList(_) => {
- cov_mark::hit!(expected_type_fn_param);
- ActiveParameter::at_token(
- &self.sema,
- self.token.clone(),
- ).map(|ap| {
- let name = ap.ident().map(NameOrNameRef::Name);
-
- let ty = strip_refs(ap.ty);
- (Some(ty), name)
- })
- .unwrap_or((None, None))
- },
- ast::RecordExprFieldList(it) => {
- // wouldn't try {} be nice...
- (|| {
- if self.token.kind() == T![..]
- || self.token.prev_token().map(|t| t.kind()) == Some(T![..])
- {
- cov_mark::hit!(expected_type_struct_func_update);
- let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
- let ty = self.sema.type_of_expr(&record_expr.into())?;
- Some((
- Some(ty.original),
- None
- ))
- } else {
- cov_mark::hit!(expected_type_struct_field_without_leading_char);
- let expr_field = self.token.prev_sibling_or_token()?
- .into_node()
- .and_then(ast::RecordExprField::cast)?;
- let (_, _, ty) = self.sema.resolve_record_field(&expr_field)?;
- Some((
- Some(ty),
- expr_field.field_name().map(NameOrNameRef::NameRef),
- ))
- }
- })().unwrap_or((None, None))
- },
- ast::RecordExprField(it) => {
- if let Some(expr) = it.expr() {
- cov_mark::hit!(expected_type_struct_field_with_leading_char);
- (
- self.sema.type_of_expr(&expr).map(TypeInfo::original),
- it.field_name().map(NameOrNameRef::NameRef),
- )
- } else {
- cov_mark::hit!(expected_type_struct_field_followed_by_comma);
- let ty = self.sema.resolve_record_field(&it)
- .map(|(_, _, ty)| ty);
- (
- ty,
- it.field_name().map(NameOrNameRef::NameRef),
- )
- }
- },
- // match foo { $0 }
- // match foo { ..., pat => $0 }
- ast::MatchExpr(it) => {
- let on_arrow = previous_non_trivia_token(self.token.clone()).map_or(false, |it| T![=>] == it.kind());
-
- let ty = if on_arrow {
- // match foo { ..., pat => $0 }
- cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
- cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
- self.sema.type_of_expr(&it.into())
- } else {
- // match foo { $0 }
- cov_mark::hit!(expected_type_match_arm_without_leading_char);
- it.expr().and_then(|e| self.sema.type_of_expr(&e))
- }.map(TypeInfo::original);
- (ty, None)
- },
- ast::IfExpr(it) => {
- let ty = it.condition()
- .and_then(|e| self.sema.type_of_expr(&e))
- .map(TypeInfo::original);
- (ty, None)
- },
- ast::IdentPat(it) => {
- cov_mark::hit!(expected_type_if_let_with_leading_char);
- cov_mark::hit!(expected_type_match_arm_with_leading_char);
- let ty = self.sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
- (ty, None)
- },
- ast::Fn(it) => {
- cov_mark::hit!(expected_type_fn_ret_with_leading_char);
- cov_mark::hit!(expected_type_fn_ret_without_leading_char);
- let def = self.sema.to_def(&it);
- (def.map(|def| def.ret_type(self.db)), None)
- },
- ast::ClosureExpr(it) => {
- let ty = self.sema.type_of_expr(&it.into());
- ty.and_then(|ty| ty.original.as_callable(self.db))
- .map(|c| (Some(c.return_type()), None))
- .unwrap_or((None, None))
- },
- ast::ParamList(_) => (None, None),
- ast::Stmt(_) => (None, None),
- ast::Item(_) => (None, None),
- _ => {
- match node.parent() {
- Some(n) => {
- node = n;
- continue;
- },
- None => (None, None),
- }
- },
+ let name_like = match find_node_at_offset(&speculative_file, offset) {
+ Some(it) => it,
+ None => {
+ let analysis = if let Some(original) = ast::String::cast(original_token.clone()) {
+ CompletionAnalysis::String {
+ original,
+ expanded: ast::String::cast(self_token.clone()),
+ }
+ } else {
+ // Fix up trailing whitespace problem
+ // #[attr(foo = $0
+ let token = syntax::algo::skip_trivia_token(self_token.clone(), Direction::Prev)?;
+ let p = token.parent()?;
+ if p.kind() == SyntaxKind::TOKEN_TREE
+ && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+ {
+ let colon_prefix = previous_non_trivia_token(self_token.clone())
+ .map_or(false, |it| T![:] == it.kind());
+ CompletionAnalysis::UnexpandedAttrTT {
+ fake_attribute_under_caret: syntax_element
+ .ancestors()
+ .find_map(ast::Attr::cast),
+ colon_prefix,
+ }
+ } else {
+ return None;
}
};
+ return Some((analysis, (None, None), QualifierCtx::default()));
}
- }
-
- /// Fill the completion context, this is what does semantic reasoning about the surrounding context
- /// of the completion location.
- fn analyze(
- &mut self,
- original_file: &SyntaxNode,
- file_with_fake_ident: SyntaxNode,
- offset: TextSize,
- derive_ctx: Option<(SyntaxNode, SyntaxNode, TextSize, ast::Attr)>,
- ) -> Option<CompletionAnalysis> {
- let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased()?;
- let syntax_element = NodeOrToken::Token(fake_ident_token);
- if is_in_token_of_for_loop(syntax_element.clone()) {
- // for pat $0
- // there is nothing to complete here except `in` keyword
- // don't bother populating the context
- // FIXME: the completion calculations should end up good enough
- // such that this special case becomes unnecessary
- return None;
+ };
+ let expected = expected_type_and_name(sema, &self_token, &name_like);
+ let mut qual_ctx = QualifierCtx::default();
+ let analysis = match name_like {
+ ast::NameLike::Lifetime(lifetime) => {
+ CompletionAnalysis::Lifetime(classify_lifetime(sema, &original_file, lifetime)?)
+ }
+ ast::NameLike::NameRef(name_ref) => {
+ let parent = name_ref.syntax().parent()?;
+ let (nameref_ctx, qualifier_ctx) =
+ classify_name_ref(sema, &original_file, name_ref, parent.clone())?;
+ qual_ctx = qualifier_ctx;
+ CompletionAnalysis::NameRef(nameref_ctx)
+ }
+ ast::NameLike::Name(name) => {
+ let name_ctx = classify_name(sema, &original_file, name)?;
+ CompletionAnalysis::Name(name_ctx)
}
+ };
+ Some((analysis, expected, qual_ctx))
+}
- // Overwrite the path kind for derives
- if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx {
- if let Some(ast::NameLike::NameRef(name_ref)) =
- find_node_at_offset(&file_with_fake_ident, offset)
- {
- let parent = name_ref.syntax().parent()?;
- let (mut nameref_ctx, _) =
- Self::classify_name_ref(&self.sema, &original_file, name_ref, parent)?;
- if let NameRefKind::Path(path_ctx) = &mut nameref_ctx.kind {
- path_ctx.kind = PathKind::Derive {
- existing_derives: self
- .sema
- .resolve_derive_macro(&origin_attr)
- .into_iter()
- .flatten()
- .flatten()
- .collect(),
- };
+/// Calculate the expected type and name of the cursor position.
+fn expected_type_and_name(
+ sema: &Semantics<'_, RootDatabase>,
+ token: &SyntaxToken,
+ name_like: &ast::NameLike,
+) -> (Option<Type>, Option<NameOrNameRef>) {
+ let mut node = match token.parent() {
+ Some(it) => it,
+ None => return (None, None),
+ };
+
+ let strip_refs = |mut ty: Type| match name_like {
+ ast::NameLike::NameRef(n) => {
+ let p = match n.syntax().parent() {
+ Some(it) => it,
+ None => return ty,
+ };
+ let top_syn = match_ast! {
+ match p {
+ ast::FieldExpr(e) => e
+ .syntax()
+ .ancestors()
+ .map_while(ast::FieldExpr::cast)
+ .last()
+ .map(|it| it.syntax().clone()),
+ ast::PathSegment(e) => e
+ .syntax()
+ .ancestors()
+ .skip(1)
+ .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind()))
+ .find_map(ast::PathExpr::cast)
+ .map(|it| it.syntax().clone()),
+ _ => None
}
- return Some(CompletionAnalysis::NameRef(nameref_ctx));
+ };
+ let top_syn = match top_syn {
+ Some(it) => it,
+ None => return ty,
+ };
+ for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) {
+ cov_mark::hit!(expected_type_fn_param_ref);
+ ty = ty.strip_reference();
}
- return None;
+ ty
}
+ _ => ty,
+ };
- let name_like = match find_node_at_offset(&file_with_fake_ident, offset) {
- Some(it) => it,
- None => {
- let analysis =
- if let Some(original) = ast::String::cast(self.original_token.clone()) {
- CompletionAnalysis::String {
- original,
- expanded: ast::String::cast(self.token.clone()),
- }
- } else {
- // Fix up trailing whitespace problem
- // #[attr(foo = $0
- let token =
- syntax::algo::skip_trivia_token(self.token.clone(), Direction::Prev)?;
- let p = token.parent()?;
- if p.kind() == SyntaxKind::TOKEN_TREE
- && p.ancestors().any(|it| it.kind() == SyntaxKind::META)
+ loop {
+ break match_ast! {
+ match node {
+ ast::LetStmt(it) => {
+ cov_mark::hit!(expected_type_let_with_leading_char);
+ cov_mark::hit!(expected_type_let_without_leading_char);
+ let ty = it.pat()
+ .and_then(|pat| sema.type_of_pat(&pat))
+ .or_else(|| it.initializer().and_then(|it| sema.type_of_expr(&it)))
+ .map(TypeInfo::original);
+ let name = match it.pat() {
+ Some(ast::Pat::IdentPat(ident)) => ident.name().map(NameOrNameRef::Name),
+ Some(_) | None => None,
+ };
+
+ (ty, name)
+ },
+ ast::LetExpr(it) => {
+ cov_mark::hit!(expected_type_if_let_without_leading_char);
+ let ty = it.pat()
+ .and_then(|pat| sema.type_of_pat(&pat))
+ .or_else(|| it.expr().and_then(|it| sema.type_of_expr(&it)))
+ .map(TypeInfo::original);
+ (ty, None)
+ },
+ ast::ArgList(_) => {
+ cov_mark::hit!(expected_type_fn_param);
+ ActiveParameter::at_token(
+ &sema,
+ token.clone(),
+ ).map(|ap| {
+ let name = ap.ident().map(NameOrNameRef::Name);
+
+ let ty = strip_refs(ap.ty);
+ (Some(ty), name)
+ })
+ .unwrap_or((None, None))
+ },
+ ast::RecordExprFieldList(it) => {
+ // wouldn't try {} be nice...
+ (|| {
+ if token.kind() == T![..]
+ ||token.prev_token().map(|t| t.kind()) == Some(T![..])
{
- let colon_prefix = previous_non_trivia_token(self.token.clone())
- .map_or(false, |it| T![:] == it.kind());
- CompletionAnalysis::UnexpandedAttrTT {
- fake_attribute_under_caret: syntax_element
- .ancestors()
- .find_map(ast::Attr::cast),
- colon_prefix,
- }
+ cov_mark::hit!(expected_type_struct_func_update);
+ let record_expr = it.syntax().parent().and_then(ast::RecordExpr::cast)?;
+ let ty = sema.type_of_expr(&record_expr.into())?;
+ Some((
+ Some(ty.original),
+ None
+ ))
} else {
- return None;
+ cov_mark::hit!(expected_type_struct_field_without_leading_char);
+ let expr_field = token.prev_sibling_or_token()?
+ .into_node()
+ .and_then(ast::RecordExprField::cast)?;
+ let (_, _, ty) = sema.resolve_record_field(&expr_field)?;
+ Some((
+ Some(ty),
+ expr_field.field_name().map(NameOrNameRef::NameRef),
+ ))
}
- };
- return Some(analysis);
+ })().unwrap_or((None, None))
+ },
+ ast::RecordExprField(it) => {
+ if let Some(expr) = it.expr() {
+ cov_mark::hit!(expected_type_struct_field_with_leading_char);
+ (
+ sema.type_of_expr(&expr).map(TypeInfo::original),
+ it.field_name().map(NameOrNameRef::NameRef),
+ )
+ } else {
+ cov_mark::hit!(expected_type_struct_field_followed_by_comma);
+ let ty = sema.resolve_record_field(&it)
+ .map(|(_, _, ty)| ty);
+ (
+ ty,
+ it.field_name().map(NameOrNameRef::NameRef),
+ )
+ }
+ },
+ // match foo { $0 }
+ // match foo { ..., pat => $0 }
+ ast::MatchExpr(it) => {
+ let on_arrow = previous_non_trivia_token(token.clone()).map_or(false, |it| T![=>] == it.kind());
+
+ let ty = if on_arrow {
+ // match foo { ..., pat => $0 }
+ cov_mark::hit!(expected_type_match_arm_body_without_leading_char);
+ cov_mark::hit!(expected_type_match_arm_body_with_leading_char);
+ sema.type_of_expr(&it.into())
+ } else {
+ // match foo { $0 }
+ cov_mark::hit!(expected_type_match_arm_without_leading_char);
+ it.expr().and_then(|e| sema.type_of_expr(&e))
+ }.map(TypeInfo::original);
+ (ty, None)
+ },
+ ast::IfExpr(it) => {
+ let ty = it.condition()
+ .and_then(|e| sema.type_of_expr(&e))
+ .map(TypeInfo::original);
+ (ty, None)
+ },
+ ast::IdentPat(it) => {
+ cov_mark::hit!(expected_type_if_let_with_leading_char);
+ cov_mark::hit!(expected_type_match_arm_with_leading_char);
+ let ty = sema.type_of_pat(&ast::Pat::from(it)).map(TypeInfo::original);
+ (ty, None)
+ },
+ ast::Fn(it) => {
+ cov_mark::hit!(expected_type_fn_ret_with_leading_char);
+ cov_mark::hit!(expected_type_fn_ret_without_leading_char);
+ let def = sema.to_def(&it);
+ (def.map(|def| def.ret_type(sema.db)), None)
+ },
+ ast::ClosureExpr(it) => {
+ let ty = sema.type_of_expr(&it.into());
+ ty.and_then(|ty| ty.original.as_callable(sema.db))
+ .map(|c| (Some(c.return_type()), None))
+ .unwrap_or((None, None))
+ },
+ ast::ParamList(_) => (None, None),
+ ast::Stmt(_) => (None, None),
+ ast::Item(_) => (None, None),
+ _ => {
+ match node.parent() {
+ Some(n) => {
+ node = n;
+ continue;
+ },
+ None => (None, None),
+ }
+ },
}
};
- (self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like);
- let analysis = match name_like {
- ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime(
- Self::classify_lifetime(&self.sema, original_file, lifetime)?,
- ),
- ast::NameLike::NameRef(name_ref) => {
- let parent = name_ref.syntax().parent()?;
- let (nameref_ctx, qualifier_ctx) =
- Self::classify_name_ref(&self.sema, &original_file, name_ref, parent.clone())?;
+ }
+}
- self.qualifier_ctx = qualifier_ctx;
- CompletionAnalysis::NameRef(nameref_ctx)
- }
- ast::NameLike::Name(name) => {
- let name_ctx = Self::classify_name(&self.sema, original_file, name)?;
- CompletionAnalysis::Name(name_ctx)
- }
- };
- Some(analysis)
+fn classify_lifetime(
+ _sema: &Semantics<'_, RootDatabase>,
+ original_file: &SyntaxNode,
+ lifetime: ast::Lifetime,
+) -> Option<LifetimeContext> {
+ let parent = lifetime.syntax().parent()?;
+ if parent.kind() == SyntaxKind::ERROR {
+ return None;
}
- fn classify_lifetime(
- _sema: &Semantics<'_, RootDatabase>,
- original_file: &SyntaxNode,
- lifetime: ast::Lifetime,
- ) -> Option<LifetimeContext> {
- let parent = lifetime.syntax().parent()?;
- if parent.kind() == SyntaxKind::ERROR {
- return None;
+ let kind = match_ast! {
+ match parent {
+ ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
+ is_decl: param.lifetime().as_ref() == Some(&lifetime),
+ param
+ },
+ ast::BreakExpr(_) => LifetimeKind::LabelRef,
+ ast::ContinueExpr(_) => LifetimeKind::LabelRef,
+ ast::Label(_) => LifetimeKind::LabelDef,
+ _ => LifetimeKind::Lifetime,
}
+ };
+ let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start());
- let kind = match_ast! {
- match parent {
- ast::LifetimeParam(param) => LifetimeKind::LifetimeParam {
- is_decl: param.lifetime().as_ref() == Some(&lifetime),
- param
- },
- ast::BreakExpr(_) => LifetimeKind::LabelRef,
- ast::ContinueExpr(_) => LifetimeKind::LabelRef,
- ast::Label(_) => LifetimeKind::LabelDef,
- _ => LifetimeKind::Lifetime,
- }
- };
- let lifetime = find_node_at_offset(&original_file, lifetime.syntax().text_range().start());
+ Some(LifetimeContext { lifetime, kind })
+}
- Some(LifetimeContext { lifetime, kind })
- }
+fn classify_name(
+ sema: &Semantics<'_, RootDatabase>,
+ original_file: &SyntaxNode,
+ name: ast::Name,
+) -> Option<NameContext> {
+ let parent = name.syntax().parent()?;
+ let kind = match_ast! {
+ match parent {
+ ast::Const(_) => NameKind::Const,
+ ast::ConstParam(_) => NameKind::ConstParam,
+ ast::Enum(_) => NameKind::Enum,
+ ast::Fn(_) => NameKind::Function,
+ ast::IdentPat(bind_pat) => {
+ let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into());
+ if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
+ pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat());
+ }
- fn classify_name(
- sema: &Semantics<'_, RootDatabase>,
- original_file: &SyntaxNode,
- name: ast::Name,
- ) -> Option<NameContext> {
- let parent = name.syntax().parent()?;
- let kind = match_ast! {
- match parent {
- ast::Const(_) => NameKind::Const,
- ast::ConstParam(_) => NameKind::ConstParam,
- ast::Enum(_) => NameKind::Enum,
- ast::Fn(_) => NameKind::Function,
- ast::IdentPat(bind_pat) => {
- let mut pat_ctx = pattern_context_for(sema, original_file, bind_pat.into());
- if let Some(record_field) = ast::RecordPatField::for_field_name(&name) {
- pat_ctx.record_pat = find_node_in_file_compensated(sema, original_file, &record_field.parent_record_pat());
- }
+ NameKind::IdentPat(pat_ctx)
+ },
+ ast::MacroDef(_) => NameKind::MacroDef,
+ ast::MacroRules(_) => NameKind::MacroRules,
+ ast::Module(module) => NameKind::Module(module),
+ ast::RecordField(_) => NameKind::RecordField,
+ ast::Rename(_) => NameKind::Rename,
+ ast::SelfParam(_) => NameKind::SelfParam,
+ ast::Static(_) => NameKind::Static,
+ ast::Struct(_) => NameKind::Struct,
+ ast::Trait(_) => NameKind::Trait,
+ ast::TypeAlias(_) => NameKind::TypeAlias,
+ ast::TypeParam(_) => NameKind::TypeParam,
+ ast::Union(_) => NameKind::Union,
+ ast::Variant(_) => NameKind::Variant,
+ _ => return None,
+ }
+ };
+ let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
+ Some(NameContext { name, kind })
+}
- NameKind::IdentPat(pat_ctx)
- },
- ast::MacroDef(_) => NameKind::MacroDef,
- ast::MacroRules(_) => NameKind::MacroRules,
- ast::Module(module) => NameKind::Module(module),
- ast::RecordField(_) => NameKind::RecordField,
- ast::Rename(_) => NameKind::Rename,
- ast::SelfParam(_) => NameKind::SelfParam,
- ast::Static(_) => NameKind::Static,
- ast::Struct(_) => NameKind::Struct,
- ast::Trait(_) => NameKind::Trait,
- ast::TypeAlias(_) => NameKind::TypeAlias,
- ast::TypeParam(_) => NameKind::TypeParam,
- ast::Union(_) => NameKind::Union,
- ast::Variant(_) => NameKind::Variant,
- _ => return None,
- }
- };
- let name = find_node_at_offset(&original_file, name.syntax().text_range().start());
- Some(NameContext { name, kind })
+fn classify_name_ref(
+ sema: &Semantics<'_, RootDatabase>,
+ original_file: &SyntaxNode,
+ name_ref: ast::NameRef,
+ parent: SyntaxNode,
+) -> Option<(NameRefContext, QualifierCtx)> {
+ let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
+
+ let make_res = |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
+
+ if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
+ let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone())
+ .map_or(false, |it| T![.] == it.kind());
+
+ return find_node_in_file_compensated(
+ sema,
+ original_file,
+ &record_field.parent_record_lit(),
+ )
+ .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix })
+ .map(make_res);
}
-
- fn classify_name_ref(
- sema: &Semantics<'_, RootDatabase>,
- original_file: &SyntaxNode,
- name_ref: ast::NameRef,
- parent: SyntaxNode,
- ) -> Option<(NameRefContext, QualifierCtx)> {
- let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
-
- let make_res =
- |kind| (NameRefContext { nameref: nameref.clone(), kind }, Default::default());
-
- if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
- let dot_prefix = previous_non_trivia_token(name_ref.syntax().clone())
- .map_or(false, |it| T![.] == it.kind());
-
- return find_node_in_file_compensated(
+ if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
+ let kind = NameRefKind::Pattern(PatternContext {
+ param_ctx: None,
+ has_type_ascription: false,
+ ref_token: None,
+ mut_token: None,
+ record_pat: find_node_in_file_compensated(
sema,
original_file,
- &record_field.parent_record_lit(),
+ &record_field.parent_record_pat(),
+ ),
+ ..pattern_context_for(
+ sema,
+ original_file,
+ record_field.parent_record_pat().clone().into(),
)
- .map(|expr| NameRefKind::RecordExpr { expr, dot_prefix })
- .map(make_res);
+ });
+ return Some(make_res(kind));
+ }
+
+ let segment = match_ast! {
+ match parent {
+ ast::PathSegment(segment) => segment,
+ ast::FieldExpr(field) => {
+ let receiver = find_opt_node_in_file(original_file, field.expr());
+ let receiver_is_ambiguous_float_literal = match &receiver {
+ Some(ast::Expr::Literal(l)) => matches! {
+ l.kind(),
+ ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.'))
+ },
+ _ => false,
+ };
+ let kind = NameRefKind::DotAccess(DotAccess {
+ receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
+ kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
+ receiver
+ });
+ return Some(make_res(kind));
+ },
+ ast::MethodCallExpr(method) => {
+ let receiver = find_opt_node_in_file(original_file, method.receiver());
+ let kind = NameRefKind::DotAccess(DotAccess {
+ receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
+ kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
+ receiver
+ });
+ return Some(make_res(kind));
+ },
+ _ => return None,
}
- if let Some(record_field) = ast::RecordPatField::for_field_name_ref(&name_ref) {
- let kind = NameRefKind::Pattern(PatternContext {
- param_ctx: None,
- has_type_ascription: false,
- ref_token: None,
- mut_token: None,
- record_pat: find_node_in_file_compensated(
- sema,
- original_file,
- &record_field.parent_record_pat(),
- ),
- ..pattern_context_for(
- sema,
- original_file,
- record_field.parent_record_pat().clone().into(),
- )
- });
- return Some(make_res(kind));
+ };
+
+ let path = segment.parent_path();
+ let original_path = find_node_in_file_compensated(sema, original_file, &path);
+
+ let mut path_ctx = PathCompletionCtx {
+ has_call_parens: false,
+ has_macro_bang: false,
+ qualified: Qualified::No,
+ parent: None,
+ path: path.clone(),
+ original_path,
+ kind: PathKind::Item { kind: ItemListKind::SourceFile },
+ has_type_args: false,
+ use_tree_parent: false,
+ };
+
+ let is_in_block = |it: &SyntaxNode| {
+ it.parent()
+ .map(|node| {
+ ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
+ })
+ .unwrap_or(false)
+ };
+ let func_update_record = |syn: &SyntaxNode| {
+ if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
+ find_node_in_file_compensated(sema, original_file, &record_expr)
+ } else {
+ None
+ }
+ };
+ let after_if_expr = |node: SyntaxNode| {
+ let prev_expr = (|| {
+ let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
+ ast::ExprStmt::cast(prev_sibling)?.expr()
+ })();
+ matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
+ };
+
+ // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
+ // ex. trait Foo $0 {}
+ // in these cases parser recovery usually kicks in for our inserted identifier, causing it
+ // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block
+ // expression or an item list.
+ // The following code checks if the body is missing, if it is we either cut off the body
+ // from the item or it was missing in the first place
+ let inbetween_body_and_decl_check = |node: SyntaxNode| {
+ if let Some(NodeOrToken::Node(n)) =
+ syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev)
+ {
+ if let Some(item) = ast::Item::cast(n) {
+ let is_inbetween = match &item {
+ ast::Item::Const(it) => it.body().is_none(),
+ ast::Item::Enum(it) => it.variant_list().is_none(),
+ ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
+ ast::Item::Fn(it) => it.body().is_none(),
+ ast::Item::Impl(it) => it.assoc_item_list().is_none(),
+ ast::Item::Module(it) => it.item_list().is_none(),
+ ast::Item::Static(it) => it.body().is_none(),
+ ast::Item::Struct(it) => it.field_list().is_none(),
+ ast::Item::Trait(it) => it.assoc_item_list().is_none(),
+ ast::Item::TypeAlias(it) => it.ty().is_none(),
+ ast::Item::Union(it) => it.record_field_list().is_none(),
+ _ => false,
+ };
+ if is_inbetween {
+ return Some(item);
+ }
+ }
}
+ None
+ };
- let segment = match_ast! {
+ let type_location = |node: &SyntaxNode| {
+ let parent = node.parent()?;
+ let res = match_ast! {
match parent {
- ast::PathSegment(segment) => segment,
- ast::FieldExpr(field) => {
- let receiver = find_opt_node_in_file(original_file, field.expr());
- let receiver_is_ambiguous_float_literal = match &receiver {
- Some(ast::Expr::Literal(l)) => matches! {
- l.kind(),
- ast::LiteralKind::FloatNumber { .. } if l.syntax().last_token().map_or(false, |it| it.text().ends_with('.'))
- },
- _ => false,
+ ast::Const(it) => {
+ let name = find_opt_node_in_file(original_file, it.name())?;
+ let original = ast::Const::cast(name.syntax().parent()?)?;
+ TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
+ },
+ ast::RetType(it) => {
+ if it.thin_arrow_token().is_none() {
+ return None;
+ }
+ let parent = match ast::Fn::cast(parent.parent()?) {
+ Some(x) => x.param_list(),
+ None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
};
- let kind = NameRefKind::DotAccess(DotAccess {
- receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
- kind: DotAccessKind::Field { receiver_is_ambiguous_float_literal },
- receiver
- });
- return Some(make_res(kind));
+
+ let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?;
+ TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! {
+ match parent {
+ ast::ClosureExpr(it) => {
+ it.body()
+ },
+ ast::Fn(it) => {
+ it.body().map(ast::Expr::BlockExpr)
+ },
+ _ => return None,
+ }
+ }))
},
- ast::MethodCallExpr(method) => {
- let receiver = find_opt_node_in_file(original_file, method.receiver());
- let kind = NameRefKind::DotAccess(DotAccess {
- receiver_ty: receiver.as_ref().and_then(|it| sema.type_of_expr(it)),
- kind: DotAccessKind::Method { has_parens: method.arg_list().map_or(false, |it| it.l_paren_token().is_some()) },
- receiver
- });
- return Some(make_res(kind));
+ ast::Param(it) => {
+ if it.colon_token().is_none() {
+ return None;
+ }
+ TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat())))
},
+ ast::LetStmt(it) => {
+ if it.colon_token().is_none() {
+ return None;
+ }
+ TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
+ },
+ ast::Impl(it) => {
+ match it.trait_() {
+ Some(t) if t.syntax() == node => TypeLocation::ImplTrait,
+ _ => match it.self_ty() {
+ Some(t) if t.syntax() == node => TypeLocation::ImplTarget,
+ _ => return None,
+ },
+ }
+ },
+ 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))),
+ // is this case needed?
+ ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
+ ast::TupleField(_) => TypeLocation::TupleField,
_ => return None,
}
};
+ Some(res)
+ };
- let path = segment.parent_path();
- let original_path = find_node_in_file_compensated(sema, original_file, &path);
-
- let mut path_ctx = PathCompletionCtx {
- has_call_parens: false,
- has_macro_bang: false,
- qualified: Qualified::No,
- parent: None,
- path: path.clone(),
- original_path,
- kind: PathKind::Item { kind: ItemListKind::SourceFile },
- has_type_args: false,
- use_tree_parent: false,
- };
-
- let is_in_block = |it: &SyntaxNode| {
- it.parent()
- .map(|node| {
- ast::ExprStmt::can_cast(node.kind()) || ast::StmtList::can_cast(node.kind())
- })
- .unwrap_or(false)
- };
- let func_update_record = |syn: &SyntaxNode| {
- if let Some(record_expr) = syn.ancestors().nth(2).and_then(ast::RecordExpr::cast) {
- find_node_in_file_compensated(sema, original_file, &record_expr)
+ let is_in_condition = |it: &ast::Expr| {
+ (|| {
+ let parent = it.syntax().parent()?;
+ if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
+ Some(expr.condition()? == *it)
+ } else if let Some(expr) = ast::IfExpr::cast(parent) {
+ Some(expr.condition()? == *it)
} else {
None
}
- };
- let after_if_expr = |node: SyntaxNode| {
- let prev_expr = (|| {
- let prev_sibling = non_trivia_sibling(node.into(), Direction::Prev)?.into_node()?;
- ast::ExprStmt::cast(prev_sibling)?.expr()
- })();
- matches!(prev_expr, Some(ast::Expr::IfExpr(_)))
- };
+ })()
+ .unwrap_or(false)
+ };
- // We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
- // ex. trait Foo $0 {}
- // in these cases parser recovery usually kicks in for our inserted identifier, causing it
- // to either be parsed as an ExprStmt or a MacroCall, depending on whether it is in a block
- // expression or an item list.
- // The following code checks if the body is missing, if it is we either cut off the body
- // from the item or it was missing in the first place
- let inbetween_body_and_decl_check = |node: SyntaxNode| {
- if let Some(NodeOrToken::Node(n)) =
- syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev)
- {
- if let Some(item) = ast::Item::cast(n) {
- let is_inbetween = match &item {
- ast::Item::Const(it) => it.body().is_none(),
- ast::Item::Enum(it) => it.variant_list().is_none(),
- ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
- ast::Item::Fn(it) => it.body().is_none(),
- ast::Item::Impl(it) => it.assoc_item_list().is_none(),
- ast::Item::Module(it) => it.item_list().is_none(),
- ast::Item::Static(it) => it.body().is_none(),
- ast::Item::Struct(it) => it.field_list().is_none(),
- ast::Item::Trait(it) => it.assoc_item_list().is_none(),
- ast::Item::TypeAlias(it) => it.ty().is_none(),
- ast::Item::Union(it) => it.record_field_list().is_none(),
- _ => false,
- };
- if is_inbetween {
- return Some(item);
+ let make_path_kind_expr = |expr: ast::Expr| {
+ let it = expr.syntax();
+ let in_block_expr = is_in_block(it);
+ let in_loop_body = is_in_loop_body(it);
+ let after_if_expr = after_if_expr(it.clone());
+ let ref_expr_parent =
+ path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
+ let (innermost_ret_ty, self_param) = {
+ let find_ret_ty = |it: SyntaxNode| {
+ if let Some(item) = ast::Item::cast(it.clone()) {
+ match item {
+ ast::Item::Fn(f) => Some(sema.to_def(&f).map(|it| it.ret_type(sema.db))),
+ ast::Item::MacroCall(_) => None,
+ _ => Some(None),
}
- }
- }
- None
- };
-
- let type_location = |node: &SyntaxNode| {
- let parent = node.parent()?;
- let res = match_ast! {
- match parent {
- ast::Const(it) => {
- let name = find_opt_node_in_file(original_file, it.name())?;
- let original = ast::Const::cast(name.syntax().parent()?)?;
- TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
- },
- ast::RetType(it) => {
- if it.thin_arrow_token().is_none() {
- return None;
- }
- let parent = match ast::Fn::cast(parent.parent()?) {
- Some(x) => x.param_list(),
- None => ast::ClosureExpr::cast(parent.parent()?)?.param_list(),
- };
-
- let parent = find_opt_node_in_file(original_file, parent)?.syntax().parent()?;
- TypeLocation::TypeAscription(TypeAscriptionTarget::RetType(match_ast! {
- match parent {
- ast::ClosureExpr(it) => {
- it.body()
- },
- ast::Fn(it) => {
- it.body().map(ast::Expr::BlockExpr)
- },
- _ => return None,
- }
- }))
- },
- ast::Param(it) => {
- if it.colon_token().is_none() {
- return None;
- }
- TypeLocation::TypeAscription(TypeAscriptionTarget::FnParam(find_opt_node_in_file(original_file, it.pat())))
- },
- ast::LetStmt(it) => {
- if it.colon_token().is_none() {
- return None;
- }
- TypeLocation::TypeAscription(TypeAscriptionTarget::Let(find_opt_node_in_file(original_file, it.pat())))
- },
- ast::Impl(it) => {
- match it.trait_() {
- Some(t) if t.syntax() == node => TypeLocation::ImplTrait,
- _ => match it.self_ty() {
- Some(t) if t.syntax() == node => TypeLocation::ImplTarget,
- _ => return None,
- },
- }
- },
- 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))),
- // is this case needed?
- ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))),
- ast::TupleField(_) => TypeLocation::TupleField,
- _ => return None,
- }
- };
- Some(res)
- };
-
- let is_in_condition = |it: &ast::Expr| {
- (|| {
- let parent = it.syntax().parent()?;
- if let Some(expr) = ast::WhileExpr::cast(parent.clone()) {
- Some(expr.condition()? == *it)
- } else if let Some(expr) = ast::IfExpr::cast(parent) {
- Some(expr.condition()? == *it)
} else {
- None
- }
- })()
- .unwrap_or(false)
- };
-
- let make_path_kind_expr = |expr: ast::Expr| {
- let it = expr.syntax();
- let in_block_expr = is_in_block(it);
- let in_loop_body = is_in_loop_body(it);
- let after_if_expr = after_if_expr(it.clone());
- let ref_expr_parent =
- path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
- let (innermost_ret_ty, self_param) = {
- let find_ret_ty = |it: SyntaxNode| {
- if let Some(item) = ast::Item::cast(it.clone()) {
- match item {
- ast::Item::Fn(f) => {
- Some(sema.to_def(&f).map(|it| it.ret_type(sema.db)))
- }
- ast::Item::MacroCall(_) => None,
- _ => Some(None),
- }
- } else {
- let expr = ast::Expr::cast(it)?;
- let callable = match expr {
- // FIXME
- // ast::Expr::BlockExpr(b) if b.async_token().is_some() || b.try_token().is_some() => sema.type_of_expr(b),
- ast::Expr::ClosureExpr(_) => sema.type_of_expr(&expr),
- _ => return None,
- };
- Some(
- callable
- .and_then(|c| c.adjusted().as_callable(sema.db))
- .map(|it| it.return_type()),
- )
- }
- };
- let find_fn_self_param = |it| match it {
- ast::Item::Fn(fn_) => {
- Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db)))
- }
- ast::Item::MacroCall(_) => None,
- _ => Some(None),
- };
-
- match find_node_in_file_compensated(sema, original_file, &expr) {
- Some(it) => {
- let innermost_ret_ty = sema
- .ancestors_with_macros(it.syntax().clone())
- .find_map(find_ret_ty)
- .flatten();
-
- let self_param = sema
- .ancestors_with_macros(it.syntax().clone())
- .filter_map(ast::Item::cast)
- .find_map(find_fn_self_param)
- .flatten();
- (innermost_ret_ty, self_param)
- }
- None => (None, None),
+ let expr = ast::Expr::cast(it)?;
+ let callable = match expr {
+ // FIXME
+ // ast::Expr::BlockExpr(b) if b.async_token().is_some() || b.try_token().is_some() => sema.type_of_expr(b),
+ ast::Expr::ClosureExpr(_) => sema.type_of_expr(&expr),
+ _ => return None,
+ };
+ Some(
+ callable
+ .and_then(|c| c.adjusted().as_callable(sema.db))
+ .map(|it| it.return_type()),
+ )
}
};
- let is_func_update = func_update_record(it);
- let in_condition = is_in_condition(&expr);
- let incomplete_let = it
- .parent()
- .and_then(ast::LetStmt::cast)
- .map_or(false, |it| it.semicolon_token().is_none());
- let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
-
- let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) {
- Some(arm) => arm
- .fat_arrow_token()
- .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()),
- None => false,
+ let find_fn_self_param = |it| match it {
+ ast::Item::Fn(fn_) => Some(sema.to_def(&fn_).and_then(|it| it.self_param(sema.db))),
+ ast::Item::MacroCall(_) => None,
+ _ => Some(None),
};
- PathKind::Expr {
- expr_ctx: ExprCtx {
- in_block_expr,
- in_loop_body,
- after_if_expr,
- in_condition,
- ref_expr_parent,
- is_func_update,
- innermost_ret_ty,
- self_param,
- incomplete_let,
- impl_,
- in_match_guard,
- },
+ match find_node_in_file_compensated(sema, original_file, &expr) {
+ Some(it) => {
+ let innermost_ret_ty = sema
+ .ancestors_with_macros(it.syntax().clone())
+ .find_map(find_ret_ty)
+ .flatten();
+
+ let self_param = sema
+ .ancestors_with_macros(it.syntax().clone())
+ .filter_map(ast::Item::cast)
+ .find_map(find_fn_self_param)
+ .flatten();
+ (innermost_ret_ty, self_param)
+ }
+ None => (None, None),
}
};
- let make_path_kind_type = |ty: ast::Type| {
- let location = type_location(ty.syntax());
- PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
+ let is_func_update = func_update_record(it);
+ let in_condition = is_in_condition(&expr);
+ let incomplete_let = it
+ .parent()
+ .and_then(ast::LetStmt::cast)
+ .map_or(false, |it| it.semicolon_token().is_none());
+ let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
+
+ let in_match_guard = match it.parent().and_then(ast::MatchArm::cast) {
+ Some(arm) => arm
+ .fat_arrow_token()
+ .map_or(true, |arrow| it.text_range().start() < arrow.text_range().start()),
+ None => false,
};
- let mut kind_macro_call = |it: ast::MacroCall| {
- path_ctx.has_macro_bang = it.excl_token().is_some();
- let parent = it.syntax().parent()?;
- // Any path in an item list will be treated as a macro call by the parser
- let kind = match_ast! {
- match parent {
- ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
- ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
- ast::MacroType(ty) => make_path_kind_type(ty.into()),
- ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
- ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
- Some(it) => match_ast! {
- match it {
- ast::Trait(_) => ItemListKind::Trait,
- ast::Impl(it) => if it.trait_().is_some() {
- ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
- } else {
- ItemListKind::Impl
- },
- _ => return None
- }
- },
- None => return None,
- } },
- ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
- ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
- _ => return None,
- }
- };
- Some(kind)
- };
- let make_path_kind_attr = |meta: ast::Meta| {
- let attr = meta.parent_attr()?;
- let kind = attr.kind();
- let attached = attr.syntax().parent()?;
- let is_trailing_outer_attr = kind != AttrKind::Inner
- && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next)
- .is_none();
- let annotated_item_kind =
- if is_trailing_outer_attr { None } else { Some(attached.kind()) };
- Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
- };
+ PathKind::Expr {
+ expr_ctx: ExprCtx {
+ in_block_expr,
+ in_loop_body,
+ after_if_expr,
+ in_condition,
+ ref_expr_parent,
+ is_func_update,
+ innermost_ret_ty,
+ self_param,
+ incomplete_let,
+ impl_,
+ in_match_guard,
+ },
+ }
+ };
+ let make_path_kind_type = |ty: ast::Type| {
+ let location = type_location(ty.syntax());
+ PathKind::Type { location: location.unwrap_or(TypeLocation::Other) }
+ };
- // Infer the path kind
- let parent = path.syntax().parent()?;
+ let mut kind_macro_call = |it: ast::MacroCall| {
+ path_ctx.has_macro_bang = it.excl_token().is_some();
+ let parent = it.syntax().parent()?;
+ // Any path in an item list will be treated as a macro call by the parser
let kind = match_ast! {
match parent {
- ast::PathType(it) => make_path_kind_type(it.into()),
- ast::PathExpr(it) => {
- if let Some(p) = it.syntax().parent() {
- if ast::ExprStmt::can_cast(p.kind()) {
- if let Some(kind) = inbetween_body_and_decl_check(p) {
- return Some(make_res(NameRefKind::Keyword(kind)));
- }
- }
- }
-
- path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
-
- make_path_kind_expr(it.into())
- },
- ast::TupleStructPat(it) => {
- path_ctx.has_call_parens = true;
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
- },
- ast::RecordPat(it) => {
- path_ctx.has_call_parens = true;
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
- },
- ast::PathPat(it) => {
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
- },
- ast::MacroCall(it) => {
- // A macro call in this position is usually a result of parsing recovery, so check that
- if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
- return Some(make_res(NameRefKind::Keyword(kind)));
- }
-
- kind_macro_call(it)?
- },
- ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
- ast::UseTree(_) => PathKind::Use,
- // completing inside a qualifier
- ast::Path(parent) => {
- path_ctx.parent = Some(parent.clone());
- let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
- match_ast! {
- match parent {
- ast::PathType(it) => make_path_kind_type(it.into()),
- ast::PathExpr(it) => {
- path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
-
- make_path_kind_expr(it.into())
- },
- ast::TupleStructPat(it) => {
- path_ctx.has_call_parens = true;
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
- },
- ast::RecordPat(it) => {
- path_ctx.has_call_parens = true;
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
- },
- ast::PathPat(it) => {
- PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
- },
- ast::MacroCall(it) => {
- kind_macro_call(it)?
+ ast::MacroExpr(expr) => make_path_kind_expr(expr.into()),
+ ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())},
+ ast::MacroType(ty) => make_path_kind_type(ty.into()),
+ ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module },
+ ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() {
+ Some(it) => match_ast! {
+ match it {
+ ast::Trait(_) => ItemListKind::Trait,
+ ast::Impl(it) => if it.trait_().is_some() {
+ ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it))
+ } else {
+ ItemListKind::Impl
},
- ast::Meta(meta) => make_path_kind_attr(meta)?,
- ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
- ast::UseTree(_) => PathKind::Use,
- ast::RecordExpr(it) => make_path_kind_expr(it.into()),
- _ => return None,
+ _ => return None
}
- }
- },
- ast::RecordExpr(it) => make_path_kind_expr(it.into()),
+ },
+ None => return None,
+ } },
+ ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
+ ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
_ => return None,
}
};
+ Some(kind)
+ };
+ let make_path_kind_attr = |meta: ast::Meta| {
+ let attr = meta.parent_attr()?;
+ let kind = attr.kind();
+ let attached = attr.syntax().parent()?;
+ let is_trailing_outer_attr = kind != AttrKind::Inner
+ && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none();
+ let annotated_item_kind = if is_trailing_outer_attr { None } else { Some(attached.kind()) };
+ Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } })
+ };
- path_ctx.kind = kind;
- path_ctx.has_type_args = segment.generic_arg_list().is_some();
+ // Infer the path kind
+ let parent = path.syntax().parent()?;
+ let kind = match_ast! {
+ match parent {
+ ast::PathType(it) => make_path_kind_type(it.into()),
+ ast::PathExpr(it) => {
+ if let Some(p) = it.syntax().parent() {
+ if ast::ExprStmt::can_cast(p.kind()) {
+ if let Some(kind) = inbetween_body_and_decl_check(p) {
+ return Some(make_res(NameRefKind::Keyword(kind)));
+ }
+ }
+ }
- // calculate the qualifier context
- if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
- path_ctx.use_tree_parent = use_tree_parent;
- if !use_tree_parent && segment.coloncolon_token().is_some() {
- path_ctx.qualified = Qualified::Absolute;
- } else {
- let qualifier = qualifier
- .segment()
- .and_then(|it| find_node_in_file(original_file, &it))
- .map(|it| it.parent_path());
- if let Some(qualifier) = qualifier {
- let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
- Some(ast::PathSegmentKind::Type {
- type_ref: Some(type_ref),
- trait_ref,
- }) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)),
- _ => None,
- };
+ path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
+
+ make_path_kind_expr(it.into())
+ },
+ ast::TupleStructPat(it) => {
+ path_ctx.has_call_parens = true;
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+ },
+ ast::RecordPat(it) => {
+ path_ctx.has_call_parens = true;
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+ },
+ ast::PathPat(it) => {
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
+ },
+ ast::MacroCall(it) => {
+ // A macro call in this position is usually a result of parsing recovery, so check that
+ if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
+ return Some(make_res(NameRefKind::Keyword(kind)));
+ }
- path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor {
- let ty = match ty {
- ast::Type::InferType(_) => None,
- ty => sema.resolve_type(&ty),
- };
- let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?));
- Qualified::TypeAnchor { ty, trait_ }
- } else {
- let res = sema.resolve_path(&qualifier);
-
- // For understanding how and why super_chain_len is calculated the way it
- // is check the documentation at it's definition
- let mut segment_count = 0;
- let super_count =
- iter::successors(Some(qualifier.clone()), |p| p.qualifier())
- .take_while(|p| {
- p.segment()
- .and_then(|s| {
- segment_count += 1;
- s.super_token()
- })
- .is_some()
- })
- .count();
+ kind_macro_call(it)?
+ },
+ ast::Meta(meta) => make_path_kind_attr(meta)?,
+ ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::UseTree(_) => PathKind::Use,
+ // completing inside a qualifier
+ ast::Path(parent) => {
+ path_ctx.parent = Some(parent.clone());
+ let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?;
+ match_ast! {
+ match parent {
+ ast::PathType(it) => make_path_kind_type(it.into()),
+ ast::PathExpr(it) => {
+ path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind()));
+
+ make_path_kind_expr(it.into())
+ },
+ ast::TupleStructPat(it) => {
+ path_ctx.has_call_parens = true;
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+ },
+ ast::RecordPat(it) => {
+ path_ctx.has_call_parens = true;
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) }
+ },
+ ast::PathPat(it) => {
+ PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}
+ },
+ ast::MacroCall(it) => {
+ kind_macro_call(it)?
+ },
+ ast::Meta(meta) => make_path_kind_attr(meta)?,
+ ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() },
+ ast::UseTree(_) => PathKind::Use,
+ ast::RecordExpr(it) => make_path_kind_expr(it.into()),
+ _ => return None,
+ }
+ }
+ },
+ ast::RecordExpr(it) => make_path_kind_expr(it.into()),
+ _ => return None,
+ }
+ };
- let super_chain_len =
- if segment_count > super_count { None } else { Some(super_count) };
+ path_ctx.kind = kind;
+ path_ctx.has_type_args = segment.generic_arg_list().is_some();
- Qualified::With { path: qualifier, resolution: res, super_chain_len }
+ // calculate the qualifier context
+ if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) {
+ path_ctx.use_tree_parent = use_tree_parent;
+ if !use_tree_parent && segment.coloncolon_token().is_some() {
+ path_ctx.qualified = Qualified::Absolute;
+ } else {
+ let qualifier = qualifier
+ .segment()
+ .and_then(|it| find_node_in_file(original_file, &it))
+ .map(|it| it.parent_path());
+ if let Some(qualifier) = qualifier {
+ let type_anchor = match qualifier.segment().and_then(|it| it.kind()) {
+ Some(ast::PathSegmentKind::Type { type_ref: Some(type_ref), trait_ref })
+ if qualifier.qualifier().is_none() =>
+ {
+ Some((type_ref, trait_ref))
}
+ _ => None,
};
- }
- } else if let Some(segment) = path.segment() {
- if segment.coloncolon_token().is_some() {
- path_ctx.qualified = Qualified::Absolute;
- }
- }
- let mut qualifier_ctx = QualifierCtx::default();
- if path_ctx.is_trivial_path() {
- // fetch the full expression that may have qualifiers attached to it
- let top_node = match path_ctx.kind {
- PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => {
- parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
- let parent = p.parent()?;
- if ast::StmtList::can_cast(parent.kind()) {
- Some(p)
- } else if ast::ExprStmt::can_cast(parent.kind()) {
- Some(parent)
- } else {
- None
- }
- })
- }
- PathKind::Item { .. } => {
- parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
+ path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor {
+ let ty = match ty {
+ ast::Type::InferType(_) => None,
+ ty => sema.resolve_type(&ty),
+ };
+ let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?));
+ Qualified::TypeAnchor { ty, trait_ }
+ } else {
+ let res = sema.resolve_path(&qualifier);
+
+ // For understanding how and why super_chain_len is calculated the way it
+ // is check the documentation at it's definition
+ let mut segment_count = 0;
+ let super_count = iter::successors(Some(qualifier.clone()), |p| p.qualifier())
+ .take_while(|p| {
+ p.segment()
+ .and_then(|s| {
+ segment_count += 1;
+ s.super_token()
+ })
+ .is_some()
+ })
+ .count();
+
+ let super_chain_len =
+ if segment_count > super_count { None } else { Some(super_count) };
+
+ Qualified::With { path: qualifier, resolution: res, super_chain_len }
}
- _ => None,
};
- if let Some(top) = top_node {
- if let Some(NodeOrToken::Node(error_node)) =
- syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
- {
- if error_node.kind() == SyntaxKind::ERROR {
- qualifier_ctx.unsafe_tok = error_node
- .children_with_tokens()
- .filter_map(NodeOrToken::into_token)
- .find(|it| it.kind() == T![unsafe]);
- qualifier_ctx.vis_node =
- error_node.children().find_map(ast::Visibility::cast);
+ }
+ } else if let Some(segment) = path.segment() {
+ if segment.coloncolon_token().is_some() {
+ path_ctx.qualified = Qualified::Absolute;
+ }
+ }
+
+ let mut qualifier_ctx = QualifierCtx::default();
+ if path_ctx.is_trivial_path() {
+ // fetch the full expression that may have qualifiers attached to it
+ let top_node = match path_ctx.kind {
+ PathKind::Expr { expr_ctx: ExprCtx { in_block_expr: true, .. } } => {
+ parent.ancestors().find(|it| ast::PathExpr::can_cast(it.kind())).and_then(|p| {
+ let parent = p.parent()?;
+ if ast::StmtList::can_cast(parent.kind()) {
+ Some(p)
+ } else if ast::ExprStmt::can_cast(parent.kind()) {
+ Some(parent)
+ } else {
+ None
}
+ })
+ }
+ PathKind::Item { .. } => {
+ parent.ancestors().find(|it| ast::MacroCall::can_cast(it.kind()))
+ }
+ _ => None,
+ };
+ if let Some(top) = top_node {
+ if let Some(NodeOrToken::Node(error_node)) =
+ syntax::algo::non_trivia_sibling(top.clone().into(), syntax::Direction::Prev)
+ {
+ if error_node.kind() == SyntaxKind::ERROR {
+ qualifier_ctx.unsafe_tok = error_node
+ .children_with_tokens()
+ .filter_map(NodeOrToken::into_token)
+ .find(|it| it.kind() == T![unsafe]);
+ qualifier_ctx.vis_node = error_node.children().find_map(ast::Visibility::cast);
}
+ }
- if let PathKind::Item { .. } = path_ctx.kind {
- if qualifier_ctx.none() {
- if let Some(t) = top.first_token() {
- if let Some(prev) = t
- .prev_token()
- .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
- {
- if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
- // This was inferred to be an item position path, but it seems
- // to be part of some other broken node which leaked into an item
- // list
- return None;
- }
+ if let PathKind::Item { .. } = path_ctx.kind {
+ if qualifier_ctx.none() {
+ if let Some(t) = top.first_token() {
+ if let Some(prev) = t
+ .prev_token()
+ .and_then(|t| syntax::algo::skip_trivia_token(t, Direction::Prev))
+ {
+ if ![T![;], T!['}'], T!['{']].contains(&prev.kind()) {
+ // This was inferred to be an item position path, but it seems
+ // to be part of some other broken node which leaked into an item
+ // list
+ return None;
}
}
}
}
}
}
- Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
}
+ Some((NameRefContext { nameref, kind: NameRefKind::Path(path_ctx) }, qualifier_ctx))
}
fn pattern_context_for(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index ae1a440d0..9d0044e55 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -183,6 +183,7 @@ pub fn completions(
CompletionAnalysis::String { original, expanded: Some(expanded) } => {
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
completions::format_string::format_string(acc, ctx, original, expanded);
+ completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded);
}
CompletionAnalysis::UnexpandedAttrTT {
colon_prefix,
@@ -234,7 +235,12 @@ pub fn resolve_completion_edits(
);
let import = items_with_name
.filter_map(|candidate| {
- current_module.find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind)
+ current_module.find_use_path_prefixed(
+ db,
+ candidate,
+ config.insert_use.prefix_kind,
+ config.prefer_no_std,
+ )
})
.find(|mod_path| mod_path.to_string() == full_import_path);
if let Some(import_path) = import {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
index dc1039fa6..f3b8eae4f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs
@@ -174,8 +174,12 @@ fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<V
hir::PathResolution::Def(def) => def.into(),
_ => return None,
};
- let path =
- ctx.module.find_use_path_prefixed(ctx.db, item, ctx.config.insert_use.prefix_kind)?;
+ let path = ctx.module.find_use_path_prefixed(
+ ctx.db,
+ item,
+ ctx.config.insert_use.prefix_kind,
+ ctx.config.prefer_no_std,
+ )?;
Some((path.len() > 1).then(|| LocatedImport::new(path.clone(), item, item, None)))
};
let mut res = Vec::with_capacity(requires.len());
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index cf826648d..9e2beb9ee 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -66,6 +66,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
enable_private_editable: false,
callable: Some(CallableSnippets::FillArguments),
snippet_cap: SnippetCap::new(true),
+ prefer_no_std: false,
insert_use: InsertUseConfig {
granularity: ImportGranularity::Crate,
prefix_kind: PrefixKind::Plain,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
index 85c4dbd66..db8bef664 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs
@@ -714,3 +714,30 @@ impl Ty {
"#]],
);
}
+
+#[test]
+fn through_alias() {
+ check_empty(
+ r#"
+enum Enum<T> {
+ Unit,
+ Tuple(T),
+}
+
+type EnumAlias<T> = Enum<T>;
+
+fn f(x: EnumAlias<u8>) {
+ match x {
+ EnumAlias::$0 => (),
+ _ => (),
+ }
+
+}
+
+"#,
+ expect![[r#"
+ bn Tuple(…) Tuple($1)$0
+ bn Unit Unit$0
+ "#]],
+ );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index a1b0bd6cb..cf0bcd5c9 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -15,11 +15,12 @@ tracing = "0.1.35"
rayon = "1.5.3"
fst = { version = "0.4.7", default-features = false }
rustc-hash = "1.1.0"
-once_cell = "1.12.0"
+once_cell = "1.15.0"
either = "1.7.0"
-itertools = "0.10.3"
+itertools = "0.10.5"
arrayvec = "0.7.2"
indexmap = "1.9.1"
+memchr = "2.5.0"
stdx = { path = "../stdx", version = "0.0.0" }
parser = { path = "../parser", version = "0.0.0" }
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 26ef86155..40a6a3e89 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
@@ -212,18 +212,20 @@ impl ImportAssets {
&self,
sema: &Semantics<'_, RootDatabase>,
prefix_kind: PrefixKind,
+ prefer_no_std: bool,
) -> Vec<LocatedImport> {
let _p = profile::span("import_assets::search_for_imports");
- self.search_for(sema, Some(prefix_kind))
+ self.search_for(sema, Some(prefix_kind), prefer_no_std)
}
/// This may return non-absolute paths if a part of the returned path is already imported into scope.
pub fn search_for_relative_paths(
&self,
sema: &Semantics<'_, RootDatabase>,
+ prefer_no_std: bool,
) -> Vec<LocatedImport> {
let _p = profile::span("import_assets::search_for_relative_paths");
- self.search_for(sema, None)
+ self.search_for(sema, None, prefer_no_std)
}
pub fn path_fuzzy_name_to_exact(&mut self, case_sensitive: bool) {
@@ -242,6 +244,7 @@ impl ImportAssets {
&self,
sema: &Semantics<'_, RootDatabase>,
prefixed: Option<PrefixKind>,
+ prefer_no_std: bool,
) -> Vec<LocatedImport> {
let _p = profile::span("import_assets::search_for");
@@ -252,6 +255,7 @@ impl ImportAssets {
item_for_path_search(sema.db, item)?,
&self.module_with_candidate,
prefixed,
+ prefer_no_std,
)
};
@@ -564,11 +568,12 @@ fn get_mod_path(
item_to_search: ItemInNs,
module_with_candidate: &Module,
prefixed: Option<PrefixKind>,
+ prefer_no_std: bool,
) -> Option<ModPath> {
if let Some(prefix_kind) = prefixed {
- module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind)
+ module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind, prefer_no_std)
} else {
- module_with_candidate.find_use_path(db, item_to_search)
+ module_with_candidate.find_use_path(db, item_to_search, prefer_no_std)
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
index 7fb4b90e6..371d642c1 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
@@ -225,7 +225,7 @@ fn path_cmp_short(a: &ast::Path, b: &ast::Path) -> Ordering {
}
/// Compares two paths, if one ends earlier than the other the has_tl parameters decide which is
-/// greater as a a path that has a tree list should be greater, while one that just ends without
+/// greater as a path that has a tree list should be greater, while one that just ends without
/// a tree list should be considered less.
pub(super) fn use_tree_path_cmp(
a: &ast::Path,
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 1ec62a842..e0bc0f89f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -38,6 +38,7 @@ pub mod syntax_helpers {
pub mod node_ext;
pub mod insert_whitespace_into_node;
pub mod format_string;
+ pub mod format_string_exprs;
pub use parser::LexedStr;
}
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 40af9e6fe..12d873b4a 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
@@ -173,6 +173,7 @@ impl<'a> Ctx<'a> {
let found_path = self.target_module.find_use_path(
self.source_scope.db.upcast(),
hir::ModuleDef::Trait(trait_ref),
+ false,
)?;
match ast::make::ty_path(mod_path_to_ast(&found_path)) {
ast::Type::PathType(path_ty) => Some(path_ty),
@@ -209,7 +210,7 @@ impl<'a> Ctx<'a> {
}
let found_path =
- self.target_module.find_use_path(self.source_scope.db.upcast(), def)?;
+ self.target_module.find_use_path(self.source_scope.db.upcast(), def, false)?;
let res = mod_path_to_ast(&found_path).clone_for_update();
if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) {
if let Some(segment) = res.segment() {
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 7deffe8e0..82b85f2fa 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -8,7 +8,9 @@ use std::{mem, sync::Arc};
use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt};
use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility};
+use memchr::memmem::Finder;
use once_cell::unsync::Lazy;
+use parser::SyntaxKind;
use stdx::hash::NoHashHashMap;
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
@@ -67,6 +69,7 @@ pub enum ReferenceCategory {
// Create
Write,
Read,
+ Import,
// FIXME: Some day should be able to search in doc comments. Would probably
// need to switch from enum to bitflags then?
// DocComment
@@ -236,6 +239,7 @@ impl Definition {
DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
+ DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
};
return match def {
Some(def) => SearchScope::file_range(def.as_ref().original_file_range(db)),
@@ -409,14 +413,17 @@ impl<'a> FindUsages<'a> {
Some(s) => s.as_str(),
None => return,
};
+ let finder = &Finder::new(name);
+ let include_self_kw_refs =
+ self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
- // these can't be closures because rust infers the lifetimes wrong ...
+ // for<'a> |text: &'a str, name: &'a str, search_range: TextRange| -> impl Iterator<Item = TextSize> + 'a { ... }
fn match_indices<'a>(
text: &'a str,
- name: &'a str,
+ finder: &'a Finder<'a>,
search_range: TextRange,
) -> impl Iterator<Item = TextSize> + 'a {
- text.match_indices(name).filter_map(move |(idx, _)| {
+ finder.find_iter(text.as_bytes()).filter_map(move |idx| {
let offset: TextSize = idx.try_into().unwrap();
if !search_range.contains_inclusive(offset) {
return None;
@@ -425,6 +432,7 @@ impl<'a> FindUsages<'a> {
})
}
+ // for<'a> |scope: &'a SearchScope| -> impl Iterator<Item = (Arc<String>, FileId, TextRange)> + 'a { ... }
fn scope_files<'a>(
sema: &'a Semantics<'_, RootDatabase>,
scope: &'a SearchScope,
@@ -448,7 +456,7 @@ impl<'a> FindUsages<'a> {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
// Search for occurrences of the items name
- for offset in match_indices(&text, name, search_range) {
+ for offset in match_indices(&text, finder, search_range) {
for name in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if match name {
ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
@@ -460,8 +468,8 @@ impl<'a> FindUsages<'a> {
}
}
// Search for occurrences of the `Self` referring to our type
- if let Some(self_ty) = &self.include_self_kw_refs {
- for offset in match_indices(&text, "Self", search_range) {
+ if let Some((self_ty, finder)) = &include_self_kw_refs {
+ for offset in match_indices(&text, finder, search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_self_ty_name_ref(self_ty, &name_ref, sink) {
return;
@@ -477,20 +485,22 @@ impl<'a> FindUsages<'a> {
let scope = search_scope
.intersection(&SearchScope::module_and_children(self.sema.db, module));
- let is_crate_root = module.is_crate_root(self.sema.db);
+ let is_crate_root =
+ module.is_crate_root(self.sema.db).then(|| Finder::new("crate"));
+ let finder = &Finder::new("super");
for (text, file_id, search_range) in scope_files(sema, &scope) {
let tree = Lazy::new(move || sema.parse(file_id).syntax().clone());
- for offset in match_indices(&text, "super", search_range) {
+ for offset in match_indices(&text, finder, search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_name_ref(&name_ref, sink) {
return;
}
}
}
- if is_crate_root {
- for offset in match_indices(&text, "crate", search_range) {
+ if let Some(finder) = &is_crate_root {
+ for offset in match_indices(&text, finder, search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_name_ref(&name_ref, sink) {
return;
@@ -531,8 +541,9 @@ impl<'a> FindUsages<'a> {
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(text.as_str())));
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
+ let finder = &Finder::new("self");
- for offset in match_indices(&text, "self", search_range) {
+ for offset in match_indices(&text, finder, search_range) {
for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) {
if self.found_self_module_name_ref(&name_ref, sink) {
return;
@@ -577,7 +588,7 @@ impl<'a> FindUsages<'a> {
let reference = FileReference {
range,
name: ast::NameLike::NameRef(name_ref.clone()),
- category: None,
+ category: is_name_ref_in_import(name_ref).then(|| ReferenceCategory::Import),
};
sink(file_id, reference)
}
@@ -756,7 +767,7 @@ impl ReferenceCategory {
fn new(def: &Definition, r: &ast::NameRef) -> Option<ReferenceCategory> {
// Only Locals and Fields have accesses for now.
if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
- return None;
+ return is_name_ref_in_import(r).then(|| ReferenceCategory::Import);
}
let mode = r.syntax().ancestors().find_map(|node| {
@@ -783,3 +794,12 @@ impl ReferenceCategory {
mode.or(Some(ReferenceCategory::Read))
}
}
+
+fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool {
+ name_ref
+ .syntax()
+ .parent()
+ .and_then(ast::PathSegment::cast)
+ .and_then(|it| it.parent_path().top_path().syntax().parent())
+ .map_or(false, |it| it.kind() == SyntaxKind::USE_TREE)
+}
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 f48a57008..2d6927cee 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,7 +1,8 @@
//! 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},
- AstNode, AstToken, TextRange, TextSize,
+ TextRange, TextSize,
};
pub fn is_format_string(string: &ast::String) -> bool {
@@ -14,8 +15,7 @@ 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 macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
- let name = macro_call.path()?.segment()?.name_ref()?;
+ let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?;
if !matches!(
name.text().as_str(),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
new file mode 100644
index 000000000..ac6c6e8fe
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/format_string_exprs.rs
@@ -0,0 +1,267 @@
+//! Tools to work with expressions present in format string literals for the `format_args!` family of macros.
+//! Primarily meant for assists and completions.
+
+/// Enum for represenging extraced format string args.
+/// Can either be extracted expressions (which includes identifiers),
+/// or placeholders `{}`.
+#[derive(Debug, PartialEq, Eq)]
+pub enum Arg {
+ Placeholder,
+ Ident(String),
+ Expr(String),
+}
+
+/**
+ Add placeholders like `$1` and `$2` in place of [`Arg::Placeholder`],
+ and unwraps the [`Arg::Ident`] and [`Arg::Expr`] enums.
+ ```rust
+ # use ide_db::syntax_helpers::format_string_exprs::*;
+ assert_eq!(with_placeholders(vec![Arg::Ident("ident".to_owned()), Arg::Placeholder, Arg::Expr("expr + 2".to_owned())]), vec!["ident".to_owned(), "$1".to_owned(), "expr + 2".to_owned()])
+ ```
+*/
+
+pub fn with_placeholders(args: Vec<Arg>) -> Vec<String> {
+ let mut placeholder_id = 1;
+ args.into_iter()
+ .map(move |a| match a {
+ Arg::Expr(s) | Arg::Ident(s) => s,
+ Arg::Placeholder => {
+ let s = format!("${placeholder_id}");
+ placeholder_id += 1;
+ s
+ }
+ })
+ .collect()
+}
+
+/**
+ Parser for a format-like string. It is more allowing in terms of string contents,
+ as we expect variable placeholders to be filled with expressions.
+
+ Built for completions and assists, and escapes `\` and `$` in output.
+ (See the comments on `get_receiver_text()` for detail.)
+ Splits a format string that may contain expressions
+ like
+ ```rust
+ assert_eq!(parse("{ident} {} {expr + 42} ").unwrap(), ("{} {} {}", vec![Arg::Ident("ident"), Arg::Placeholder, Arg::Expr("expr + 42")]));
+ ```
+*/
+pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
+ #[derive(Debug, Clone, Copy, PartialEq)]
+ enum State {
+ NotArg,
+ MaybeArg,
+ Expr,
+ Ident,
+ MaybeIncorrect,
+ FormatOpts,
+ }
+
+ let mut state = State::NotArg;
+ let mut current_expr = String::new();
+ let mut extracted_expressions = Vec::new();
+ let mut output = String::new();
+
+ // Count of open braces inside of an expression.
+ // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g.
+ // "{MyStruct { val_a: 0, val_b: 1 }}".
+ let mut inexpr_open_count = 0;
+
+ let mut chars = input.chars().peekable();
+ while let Some(chr) = chars.next() {
+ match (state, chr) {
+ (State::NotArg, '{') => {
+ output.push(chr);
+ state = State::MaybeArg;
+ }
+ (State::NotArg, '}') => {
+ output.push(chr);
+ state = State::MaybeIncorrect;
+ }
+ (State::NotArg, _) => {
+ if matches!(chr, '\\' | '$') {
+ output.push('\\');
+ }
+ output.push(chr);
+ }
+ (State::MaybeIncorrect, '}') => {
+ // It's okay, we met "}}".
+ output.push(chr);
+ state = State::NotArg;
+ }
+ (State::MaybeIncorrect, _) => {
+ // Error in the string.
+ return Err(());
+ }
+ // Escaped braces `{{`
+ (State::MaybeArg, '{') => {
+ output.push(chr);
+ state = State::NotArg;
+ }
+ (State::MaybeArg, '}') => {
+ // This is an empty sequence '{}'.
+ output.push(chr);
+ extracted_expressions.push(Arg::Placeholder);
+ state = State::NotArg;
+ }
+ (State::MaybeArg, _) => {
+ if matches!(chr, '\\' | '$') {
+ current_expr.push('\\');
+ }
+ current_expr.push(chr);
+
+ // While Rust uses the unicode sets of XID_start and XID_continue for Identifiers
+ // this is probably the best we can do to avoid a false positive
+ if chr.is_alphabetic() || chr == '_' {
+ state = State::Ident;
+ } else {
+ state = State::Expr;
+ }
+ }
+ (State::Ident | State::Expr, '}') => {
+ if inexpr_open_count == 0 {
+ output.push(chr);
+
+ if matches!(state, State::Expr) {
+ extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
+ } else {
+ extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
+ }
+
+ current_expr = String::new();
+ state = State::NotArg;
+ } else {
+ // We're closing one brace met before inside of the expression.
+ current_expr.push(chr);
+ inexpr_open_count -= 1;
+ }
+ }
+ (State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => {
+ // path separator
+ state = State::Expr;
+ current_expr.push_str("::");
+ chars.next();
+ }
+ (State::Ident | State::Expr, ':') => {
+ if inexpr_open_count == 0 {
+ // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
+ output.push(chr);
+
+ if matches!(state, State::Expr) {
+ extracted_expressions.push(Arg::Expr(current_expr.trim().into()));
+ } else {
+ extracted_expressions.push(Arg::Ident(current_expr.trim().into()));
+ }
+
+ current_expr = String::new();
+ state = State::FormatOpts;
+ } else {
+ // We're inside of braced expression, assume that it's a struct field name/value delimiter.
+ current_expr.push(chr);
+ }
+ }
+ (State::Ident | State::Expr, '{') => {
+ state = State::Expr;
+ current_expr.push(chr);
+ inexpr_open_count += 1;
+ }
+ (State::Ident | State::Expr, _) => {
+ if !(chr.is_alphanumeric() || chr == '_' || chr == '#') {
+ state = State::Expr;
+ }
+
+ if matches!(chr, '\\' | '$') {
+ current_expr.push('\\');
+ }
+ current_expr.push(chr);
+ }
+ (State::FormatOpts, '}') => {
+ output.push(chr);
+ state = State::NotArg;
+ }
+ (State::FormatOpts, _) => {
+ if matches!(chr, '\\' | '$') {
+ output.push('\\');
+ }
+ output.push(chr);
+ }
+ }
+ }
+
+ if state != State::NotArg {
+ return Err(());
+ }
+
+ Ok((output, extracted_expressions))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use expect_test::{expect, Expect};
+
+ fn check(input: &str, expect: &Expect) {
+ let (output, exprs) = parse_format_exprs(input).unwrap_or(("-".to_string(), vec![]));
+ let outcome_repr = if !exprs.is_empty() {
+ format!("{}; {}", output, with_placeholders(exprs).join(", "))
+ } else {
+ output
+ };
+
+ expect.assert_eq(&outcome_repr);
+ }
+
+ #[test]
+ fn format_str_parser() {
+ let test_vector = &[
+ ("no expressions", expect![["no expressions"]]),
+ (r"no expressions with \$0$1", expect![r"no expressions with \\\$0\$1"]),
+ ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]),
+ ("{expr:?}", expect![["{:?}; expr"]]),
+ ("{expr:1$}", expect![[r"{:1\$}; expr"]]),
+ ("{$0}", expect![[r"{}; \$0"]]),
+ ("{malformed", expect![["-"]]),
+ ("malformed}", expect![["-"]]),
+ ("{{correct", expect![["{{correct"]]),
+ ("correct}}", expect![["correct}}"]]),
+ ("{correct}}}", expect![["{}}}; correct"]]),
+ ("{correct}}}}}", expect![["{}}}}}; correct"]]),
+ ("{incorrect}}", expect![["-"]]),
+ ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]),
+ ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]),
+ (
+ "{SomeStruct { val_a: 0, val_b: 1 }}",
+ expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]],
+ ),
+ ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]),
+ (
+ "{SomeStruct { val_a: 0, val_b: 1 }:?}",
+ expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
+ ),
+ ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
+ ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
+ ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
+ ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
+ ];
+
+ for (input, output) in test_vector {
+ check(input, output)
+ }
+ }
+
+ #[test]
+ fn arg_type() {
+ assert_eq!(
+ parse_format_exprs("{_ident} {r#raw_ident} {expr.obj} {name {thing: 42} } {}")
+ .unwrap()
+ .1,
+ vec![
+ Arg::Ident("_ident".to_owned()),
+ Arg::Ident("r#raw_ident".to_owned()),
+ Arg::Expr("expr.obj".to_owned()),
+ Arg::Expr("name {thing: 42}".to_owned()),
+ Arg::Placeholder
+ ]
+ );
+ }
+}
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 b890e2b58..39710b8f1 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
@@ -2,8 +2,8 @@
use itertools::Itertools;
use parser::T;
use syntax::{
- ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind},
- AstNode, Preorder, RustLanguage, WalkEvent,
+ ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind},
+ AstNode, AstToken, Preorder, RustLanguage, WalkEvent,
};
pub fn expr_as_name_ref(expr: &ast::Expr) -> Option<ast::NameRef> {
@@ -457,3 +457,8 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Pat
.collect();
Some(paths)
}
+
+pub fn macro_call_for_string_token(string: &ast::String) -> Option<MacroCall> {
+ let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?;
+ Some(macro_call)
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
index 9b9e21a4d..e1d146f4e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/Cargo.toml
@@ -11,11 +11,9 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
-
-
either = "1.7.0"
-serde_json = "1.0.82"
+itertools = "0.10.5"
+serde_json = "1.0.86"
profile = { path = "../profile", version = "0.0.0" }
stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
index 04918891b..f558b7256 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/inactive_code.rs
@@ -140,4 +140,35 @@ trait Bar {
"#,
);
}
+
+ #[test]
+ fn inactive_fields_and_variants() {
+ check(
+ r#"
+enum Foo {
+ #[cfg(a)] Bar,
+//^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+ Baz {
+ #[cfg(a)] baz: String,
+ //^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+ },
+ Qux(#[cfg(a)] String),
+ //^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+}
+
+struct Baz {
+ #[cfg(a)] baz: String,
+//^^^^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+}
+
+struct Qux(#[cfg(a)] String);
+ //^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+
+union FooBar {
+ #[cfg(a)] baz: u32,
+//^^^^^^^^^^^^^^^^^^ weak: code is inactive due to #[cfg] directives: a is disabled
+}
+"#,
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
index a21db5b2c..303429519 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs
@@ -137,6 +137,7 @@ pub(crate) fn json_in_items(
sema.db,
it,
config.insert_use.prefix_kind,
+ config.prefer_no_std,
) {
insert_use(
&scope,
@@ -152,6 +153,7 @@ pub(crate) fn json_in_items(
sema.db,
it,
config.insert_use.prefix_kind,
+ config.prefer_no_std,
) {
insert_use(
&scope,
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index edb1fc091..7f140eb6a 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -124,6 +124,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
let type_path = current_module?.find_use_path(
ctx.sema.db,
item_for_path_search(ctx.sema.db, item_in_ns)?,
+ ctx.config.prefer_no_std,
)?;
use_trivial_constructor(
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 6bf90e645..62c69f90b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -59,9 +59,6 @@ fn add_reference(
d: &hir::TypeMismatch,
acc: &mut Vec<Assist>,
) -> Option<()> {
- let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
- let expr_node = d.expr.value.to_node(&root);
-
let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
let (_, mutability) = d.expected.as_reference()?;
@@ -72,7 +69,7 @@ fn add_reference(
let ampersands = format!("&{}", mutability.as_keyword_for_ref());
- let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
+ let edit = TextEdit::insert(range.start(), ampersands);
let source_change =
SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
acc.push(fix("add_reference_here", "Add reference here", source_change, range));
@@ -315,6 +312,34 @@ fn main() {
}
#[test]
+ fn test_add_reference_to_macro_call() {
+ check_fix(
+ r#"
+macro_rules! thousand {
+ () => {
+ 1000_u64
+ };
+}
+fn test(foo: &u64) {}
+fn main() {
+ test($0thousand!());
+}
+ "#,
+ r#"
+macro_rules! thousand {
+ () => {
+ 1000_u64
+ };
+}
+fn test(foo: &u64) {}
+fn main() {
+ test(&thousand!());
+}
+ "#,
+ );
+ }
+
+ #[test]
fn test_add_mutable_reference_to_let_stmt() {
check_fix(
r#"
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 61e63ea7a..ae299f058 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -150,6 +150,7 @@ pub struct DiagnosticsConfig {
pub expr_fill_default: ExprFillDefaultMode,
// FIXME: We may want to include a whole `AssistConfig` here
pub insert_use: InsertUseConfig,
+ pub prefer_no_std: bool,
}
impl DiagnosticsConfig {
@@ -170,6 +171,7 @@ impl DiagnosticsConfig {
group: false,
skip_glob_imports: false,
},
+ prefer_no_std: false,
}
}
}
diff --git a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
index 73314e0f3..4baf786c4 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-ssr/Cargo.toml
@@ -12,8 +12,7 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-
-itertools = "0.10.3"
+itertools = "0.10.5"
text-edit = { path = "../text-edit", version = "0.0.0" }
parser = { path = "../parser", version = "0.0.0" }
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 e3a837ddc..57b5ab6ab 100644
--- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
+++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs
@@ -648,9 +648,10 @@ impl Match {
.module();
for (path, resolved_path) in &template.resolved_paths {
if let hir::PathResolution::Def(module_def) = resolved_path.resolution {
- let mod_path = module.find_use_path(sema.db, module_def).ok_or_else(|| {
- match_error!("Failed to render template path `{}` at match location")
- })?;
+ let mod_path =
+ module.find_use_path(sema.db, module_def, false).ok_or_else(|| {
+ match_error!("Failed to render template path `{}` at match location")
+ })?;
self.rendered_template_paths.insert(path.clone(), mod_path);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/Cargo.toml b/src/tools/rust-analyzer/crates/ide/Cargo.toml
index 0e9771cd2..712459a7e 100644
--- a/src/tools/rust-analyzer/crates/ide/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide/Cargo.toml
@@ -13,12 +13,12 @@ doctest = false
cov-mark = "2.0.0-pre.1"
crossbeam-channel = "0.5.5"
either = "1.7.0"
-itertools = "0.10.3"
+itertools = "0.10.5"
tracing = "0.1.35"
oorandom = "11.1.3"
-pulldown-cmark-to-cmark = "10.0.1"
+pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.1", default-features = false }
-url = "2.2.2"
+url = "2.3.1"
dot = "0.1.4"
stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
index 210c5c7fa..f994c284c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs
@@ -8,13 +8,15 @@ use ide_db::{
use syntax::{ast::HasName, AstNode, TextRange};
use crate::{
- fn_references::find_all_methods,
+ annotations::fn_references::find_all_methods,
goto_implementation::goto_implementation,
references::find_all_refs,
runnables::{runnables, Runnable},
NavigationTarget, RunnableKind,
};
+mod fn_references;
+
// Feature: Annotations
//
// Provides user with annotations above items for looking up references or impl blocks
@@ -30,8 +32,8 @@ pub struct Annotation {
#[derive(Debug)]
pub enum AnnotationKind {
Runnable(Runnable),
- HasImpls { file_id: FileId, data: Option<Vec<NavigationTarget>> },
- HasReferences { file_id: FileId, data: Option<Vec<FileRange>> },
+ HasImpls { pos: FilePosition, data: Option<Vec<NavigationTarget>> },
+ HasReferences { pos: FilePosition, data: Option<Vec<FileRange>> },
}
pub struct AnnotationConfig {
@@ -41,6 +43,12 @@ pub struct AnnotationConfig {
pub annotate_references: bool,
pub annotate_method_references: bool,
pub annotate_enum_variant_references: bool,
+ pub location: AnnotationLocation,
+}
+
+pub enum AnnotationLocation {
+ AboveName,
+ AboveWholeItem,
}
pub(crate) fn annotations(
@@ -62,6 +70,16 @@ pub(crate) fn annotations(
}
}
+ let mk_ranges = |(range, focus): (_, Option<_>)| {
+ let cmd_target: TextRange = focus.unwrap_or(range);
+ let annotation_range = match config.location {
+ AnnotationLocation::AboveName => cmd_target,
+ AnnotationLocation::AboveWholeItem => range,
+ };
+ let target_pos = FilePosition { file_id, offset: cmd_target.start() };
+ (annotation_range, target_pos)
+ };
+
visit_file_defs(&Semantics::new(db), file_id, &mut |def| {
let range = match def {
Definition::Const(konst) if config.annotate_references => {
@@ -81,9 +99,13 @@ pub(crate) fn annotations(
})
.flatten()
.for_each(|range| {
+ let (annotation_range, target_position) = mk_ranges(range);
annotations.push(Annotation {
- range,
- kind: AnnotationKind::HasReferences { file_id, data: None },
+ range: annotation_range,
+ kind: AnnotationKind::HasReferences {
+ pos: target_position,
+ data: None,
+ },
})
})
}
@@ -108,15 +130,18 @@ pub(crate) fn annotations(
Some(range) => range,
None => return,
};
-
+ let (annotation_range, target_pos) = mk_ranges(range);
if config.annotate_impls && !matches!(def, Definition::Const(_)) {
- annotations
- .push(Annotation { range, kind: AnnotationKind::HasImpls { file_id, data: None } });
+ annotations.push(Annotation {
+ range: annotation_range,
+ kind: AnnotationKind::HasImpls { pos: target_pos, data: None },
+ });
}
+
if config.annotate_references {
annotations.push(Annotation {
- range,
- kind: AnnotationKind::HasReferences { file_id, data: None },
+ range: annotation_range,
+ kind: AnnotationKind::HasReferences { pos: target_pos, data: None },
});
}
@@ -124,10 +149,13 @@ pub(crate) fn annotations(
db: &RootDatabase,
node: InFile<T>,
source_file_id: FileId,
- ) -> Option<TextRange> {
+ ) -> Option<(TextRange, Option<TextRange>)> {
if let Some(InFile { file_id, value }) = node.original_ast_node(db) {
if file_id == source_file_id.into() {
- return value.name().map(|it| it.syntax().text_range());
+ return Some((
+ value.syntax().text_range(),
+ value.name().map(|name| name.syntax().text_range()),
+ ));
}
}
None
@@ -135,12 +163,13 @@ pub(crate) fn annotations(
});
if config.annotate_method_references {
- annotations.extend(find_all_methods(db, file_id).into_iter().map(
- |FileRange { file_id, range }| Annotation {
- range,
- kind: AnnotationKind::HasReferences { file_id, data: None },
- },
- ));
+ annotations.extend(find_all_methods(db, file_id).into_iter().map(|range| {
+ let (annotation_range, target_range) = mk_ranges(range);
+ Annotation {
+ range: annotation_range,
+ kind: AnnotationKind::HasReferences { pos: target_range, data: None },
+ }
+ }));
}
annotations
@@ -148,18 +177,11 @@ pub(crate) fn annotations(
pub(crate) fn resolve_annotation(db: &RootDatabase, mut annotation: Annotation) -> Annotation {
match annotation.kind {
- AnnotationKind::HasImpls { file_id, ref mut data } => {
- *data =
- goto_implementation(db, FilePosition { file_id, offset: annotation.range.start() })
- .map(|range| range.info);
+ AnnotationKind::HasImpls { pos, ref mut data } => {
+ *data = goto_implementation(db, pos).map(|range| range.info);
}
- AnnotationKind::HasReferences { file_id, ref mut data } => {
- *data = find_all_refs(
- &Semantics::new(db),
- FilePosition { file_id, offset: annotation.range.start() },
- None,
- )
- .map(|result| {
+ AnnotationKind::HasReferences { pos, ref mut data } => {
+ *data = find_all_refs(&Semantics::new(db), pos, None).map(|result| {
result
.into_iter()
.flat_map(|res| res.references)
@@ -188,21 +210,23 @@ mod tests {
use crate::{fixture, Annotation, AnnotationConfig};
- fn check(ra_fixture: &str, expect: Expect) {
+ use super::AnnotationLocation;
+
+ const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig {
+ binary_target: true,
+ annotate_runnables: true,
+ annotate_impls: true,
+ annotate_references: true,
+ annotate_method_references: true,
+ annotate_enum_variant_references: true,
+ location: AnnotationLocation::AboveName,
+ };
+
+ fn check_with_config(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) {
let (analysis, file_id) = fixture::file(ra_fixture);
let annotations: Vec<Annotation> = analysis
- .annotations(
- &AnnotationConfig {
- binary_target: true,
- annotate_runnables: true,
- annotate_impls: true,
- annotate_references: true,
- annotate_method_references: true,
- annotate_enum_variant_references: true,
- },
- file_id,
- )
+ .annotations(config, file_id)
.unwrap()
.into_iter()
.map(|annotation| analysis.resolve_annotation(annotation).unwrap())
@@ -211,6 +235,10 @@ mod tests {
expect.assert_debug_eq(&annotations);
}
+ fn check(ra_fixture: &str, expect: Expect) {
+ check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
+ }
+
#[test]
fn const_annotations() {
check(
@@ -247,9 +275,12 @@ fn main() {
Annotation {
range: 6..10,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 6,
+ },
data: Some(
[
FileRange {
@@ -265,9 +296,12 @@ fn main() {
Annotation {
range: 30..36,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 30,
+ },
data: Some(
[],
),
@@ -276,9 +310,12 @@ fn main() {
Annotation {
range: 53..57,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 53,
+ },
data: Some(
[],
),
@@ -323,9 +360,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasImpls {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[],
),
@@ -334,9 +374,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[
FileRange {
@@ -352,9 +395,12 @@ fn main() {
Annotation {
range: 17..21,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 17,
+ },
data: Some(
[],
),
@@ -403,9 +449,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasImpls {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[
NavigationTarget {
@@ -424,9 +473,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[
FileRange {
@@ -448,9 +500,12 @@ fn main() {
Annotation {
range: 20..31,
kind: HasImpls {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 20,
+ },
data: Some(
[
NavigationTarget {
@@ -469,9 +524,12 @@ fn main() {
Annotation {
range: 20..31,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 20,
+ },
data: Some(
[
FileRange {
@@ -487,9 +545,12 @@ fn main() {
Annotation {
range: 69..73,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 69,
+ },
data: Some(
[],
),
@@ -530,9 +591,12 @@ fn main() {}
Annotation {
range: 3..7,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 3,
+ },
data: Some(
[],
),
@@ -581,9 +645,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasImpls {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[
NavigationTarget {
@@ -602,9 +669,12 @@ fn main() {
Annotation {
range: 7..11,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 7,
+ },
data: Some(
[
FileRange {
@@ -626,9 +696,12 @@ fn main() {
Annotation {
range: 33..44,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 33,
+ },
data: Some(
[
FileRange {
@@ -644,9 +717,12 @@ fn main() {
Annotation {
range: 61..65,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 61,
+ },
data: Some(
[],
),
@@ -740,9 +816,12 @@ mod tests {
Annotation {
range: 3..7,
kind: HasReferences {
- file_id: FileId(
- 0,
- ),
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 3,
+ },
data: Some(
[],
),
@@ -786,4 +865,48 @@ m!();
"#]],
);
}
+
+ #[test]
+ fn test_annotations_appear_above_whole_item_when_configured_to_do_so() {
+ check_with_config(
+ r#"
+/// This is a struct named Foo, obviously.
+#[derive(Clone)]
+struct Foo;
+"#,
+ expect![[r#"
+ [
+ Annotation {
+ range: 0..71,
+ kind: HasImpls {
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 67,
+ },
+ data: Some(
+ [],
+ ),
+ },
+ },
+ Annotation {
+ range: 0..71,
+ kind: HasReferences {
+ pos: FilePosition {
+ file_id: FileId(
+ 0,
+ ),
+ offset: 67,
+ },
+ data: Some(
+ [],
+ ),
+ },
+ },
+ ]
+ "#]],
+ &AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG },
+ );
+ }
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/fn_references.rs b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs
index 63fb322ce..0cadf125f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/fn_references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/annotations/fn_references.rs
@@ -4,30 +4,38 @@
use hir::Semantics;
use ide_assists::utils::test_related_attribute;
use ide_db::RootDatabase;
-use syntax::{ast, ast::HasName, AstNode, SyntaxNode};
+use syntax::{ast, ast::HasName, AstNode, SyntaxNode, TextRange};
-use crate::{FileId, FileRange};
+use crate::FileId;
-pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> {
+pub(super) fn find_all_methods(
+ db: &RootDatabase,
+ file_id: FileId,
+) -> Vec<(TextRange, Option<TextRange>)> {
let sema = Semantics::new(db);
let source_file = sema.parse(file_id);
- source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect()
+ source_file.syntax().descendants().filter_map(|it| method_range(it)).collect()
}
-fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> {
+fn method_range(item: SyntaxNode) -> Option<(TextRange, Option<TextRange>)> {
ast::Fn::cast(item).and_then(|fn_def| {
if test_related_attribute(&fn_def).is_some() {
None
} else {
- fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() })
+ Some((
+ fn_def.syntax().text_range(),
+ fn_def.name().map(|name| name.syntax().text_range()),
+ ))
}
})
}
#[cfg(test)]
mod tests {
+ use syntax::TextRange;
+
use crate::fixture;
- use crate::{FileRange, TextSize};
+ use crate::TextSize;
use std::ops::RangeInclusive;
#[test]
@@ -42,7 +50,7 @@ mod tests {
"#,
);
- let refs = analysis.find_all_methods(pos.file_id).unwrap();
+ let refs = super::find_all_methods(&analysis.db, pos.file_id);
check_result(&refs, &[3..=13, 27..=33, 47..=57]);
}
@@ -57,7 +65,7 @@ mod tests {
"#,
);
- let refs = analysis.find_all_methods(pos.file_id).unwrap();
+ let refs = super::find_all_methods(&analysis.db, pos.file_id);
check_result(&refs, &[19..=22, 35..=38]);
}
@@ -78,17 +86,18 @@ mod tests {
"#,
);
- let refs = analysis.find_all_methods(pos.file_id).unwrap();
+ let refs = super::find_all_methods(&analysis.db, pos.file_id);
check_result(&refs, &[28..=34]);
}
- fn check_result(refs: &[FileRange], expected: &[RangeInclusive<u32>]) {
+ fn check_result(refs: &[(TextRange, Option<TextRange>)], expected: &[RangeInclusive<u32>]) {
assert_eq!(refs.len(), expected.len());
- for (i, item) in refs.iter().enumerate() {
+ for (i, &(full, focus)) in refs.iter().enumerate() {
let range = &expected[i];
- assert_eq!(TextSize::from(*range.start()), item.range.start());
- assert_eq!(TextSize::from(*range.end()), item.range.end());
+ let item = focus.unwrap_or(full);
+ assert_eq!(TextSize::from(*range.start()), item.start());
+ assert_eq!(TextSize::from(*range.end()), item.end());
}
}
}
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 92ce26b42..d96827326 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -232,8 +232,13 @@ pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option<DocComment
(match_ast! {
match doc_token {
ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(),
- ast::String(string) => doc_token.parent_ancestors().find_map(ast::Attr::cast)
- .filter(|attr| attr.simple_name().as_deref() == Some("doc")).and_then(|_| string.open_quote_text_range().map(|it| it.len())),
+ ast::String(string) => {
+ doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?;
+ if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() {
+ return None;
+ }
+ string.open_quote_text_range().map(|it| it.len())
+ },
_ => None,
}
}).map(|prefix_len| DocCommentToken { prefix_len, doc_token: doc_token.clone() })
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 36a648fe4..d0be1b3f4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -48,10 +48,14 @@ pub(crate) fn goto_definition(
_ => 1,
})?;
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
- return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| {
- let nav = def.try_to_nav(db)?;
- Some(RangeInfo::new(original_token.text_range(), vec![nav]))
- });
+ 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]))
+ },
+ );
}
let navs = sema
.descend_into_macros(original_token.clone())
@@ -95,6 +99,14 @@ fn try_lookup_include_path(
if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
return None;
}
+
+ // Ignore non-built-in macros to account for shadowing
+ if let Some(it) = sema.resolve_macro_call(&macro_call) {
+ if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) {
+ return None;
+ }
+ }
+
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
let size = sema.db.file_text(file_id).len().try_into().ok()?;
Some(NavigationTarget {
@@ -156,9 +168,6 @@ mod tests {
fn check(ra_fixture: &str) {
let (analysis, position, expected) = fixture::annotations(ra_fixture);
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
- if navs.is_empty() {
- panic!("unresolved reference")
- }
let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
let navs = navs
@@ -1348,6 +1357,10 @@ fn f(e: Enum) {
check(
r#"
//- /main.rs
+
+#[rustc_builtin_macro]
+macro_rules! include_str {}
+
fn main() {
let str = include_str!("foo.txt$0");
}
@@ -1357,6 +1370,42 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn goto_doc_include_str() {
+ check(
+ r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include_str {}
+
+#[doc = include_str!("docs.md$0")]
+struct Item;
+
+//- /docs.md
+// docs
+//^file
+"#,
+ );
+ }
+
+ #[test]
+ fn goto_shadow_include() {
+ check(
+ r#"
+//- /main.rs
+macro_rules! include {
+ ("included.rs") => {}
+}
+
+include!("included.rs$0");
+
+//- /included.rs
+// empty
+"#,
+ );
+ }
+
#[cfg(test)]
mod goto_impl_of_trait_fn {
use super::check;
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 f190da326..540a11583 100644
--- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs
@@ -377,6 +377,7 @@ mod tests {
match it {
ReferenceCategory::Read => "read",
ReferenceCategory::Write => "write",
+ ReferenceCategory::Import => "import",
}
.to_string()
}),
@@ -423,12 +424,12 @@ struct Foo;
check(
r#"
use crate$0;
- //^^^^^
+ //^^^^^ import
use self;
- //^^^^
+ //^^^^ import
mod __ {
use super;
- //^^^^^
+ //^^^^^ import
}
"#,
);
@@ -436,7 +437,7 @@ mod __ {
r#"
//- /main.rs crate:main deps:lib
use lib$0;
- //^^^
+ //^^^ import
//- /lib.rs crate:lib
"#,
);
@@ -450,7 +451,7 @@ use lib$0;
mod foo;
//- /foo.rs
use self$0;
- // ^^^^
+ // ^^^^ import
"#,
);
}
@@ -1375,4 +1376,20 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn test_assoc_type_highlighting() {
+ check(
+ r#"
+trait Trait {
+ type Output;
+ // ^^^^^^
+}
+impl Trait for () {
+ type Output$0 = ();
+ // ^^^^^^
+}
+"#,
+ );
+ }
}
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 c5c50d88d..d109c0769 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -346,7 +346,16 @@ pub(super) fn definition(
Definition::Module(it) => label_and_docs(db, it),
Definition::Function(it) => label_and_docs(db, it),
Definition::Adt(it) => label_and_docs(db, it),
- Definition::Variant(it) => label_and_docs(db, it),
+ Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
+ if !it.parent_enum(db).is_data_carrying(db) {
+ match it.eval(db) {
+ Ok(x) => Some(format!("{}", x)),
+ Err(_) => it.value(db).map(|x| format!("{:?}", x)),
+ }
+ } else {
+ None
+ }
+ }),
Definition::Const(it) => label_value_and_docs(db, it, |it| {
let body = it.eval(db);
match body {
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 4b8b47783..eb997e6fe 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -698,6 +698,7 @@ fn hover_enum_variant() {
check(
r#"
enum Option<T> {
+ Some(T)
/// The None variant
Non$0e
}
@@ -3528,6 +3529,112 @@ impl<const LEN: usize> Foo<LEN$0> {}
}
#[test]
+fn hover_const_eval_variant() {
+ // show hex for <10
+ check(
+ r#"
+#[repr(u8)]
+enum E {
+ /// This is a doc
+ A$0 = 1 << 3,
+}
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test::E
+ ```
+
+ ```rust
+ A = 8
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
+ // show hex for >10
+ check(
+ r#"
+#[repr(u8)]
+enum E {
+ /// This is a doc
+ A$0 = (1 << 3) + (1 << 2),
+}
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test::E
+ ```
+
+ ```rust
+ A = 12 (0xC)
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
+ // enums in const eval
+ check(
+ r#"
+#[repr(u8)]
+enum E {
+ A = 1,
+ /// This is a doc
+ B$0 = E::A as u8 + 1,
+}
+"#,
+ expect![[r#"
+ *B*
+
+ ```rust
+ test::E
+ ```
+
+ ```rust
+ B = 2
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
+ // unspecified variant should increment by one
+ check(
+ r#"
+#[repr(u8)]
+enum E {
+ A = 4,
+ /// This is a doc
+ B$0,
+}
+"#,
+ expect![[r#"
+ *B*
+
+ ```rust
+ test::E
+ ```
+
+ ```rust
+ B = 5
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
+}
+
+#[test]
fn hover_const_eval() {
// show hex for <10
check(
@@ -3823,6 +3930,35 @@ fn foo() {
This is a doc
"#]],
);
+ check(
+ r#"
+enum E {
+ /// This is a doc
+ A = 3,
+}
+fn foo(e: E) {
+ match e {
+ E::A$0 => (),
+ _ => ()
+ }
+}
+"#,
+ expect![[r#"
+ *A*
+
+ ```rust
+ test::E
+ ```
+
+ ```rust
+ A = 3
+ ```
+
+ ---
+
+ This is a doc
+ "#]],
+ );
}
#[test]
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 d1b1d2c33..34d8bf67a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -176,12 +176,6 @@ impl fmt::Debug for InlayHintLabelPart {
// * elided lifetimes
// * compiler inserted reborrows
//
-// |===
-// | Editor | Action Name
-//
-// | VS Code | **rust-analyzer: Toggle inlay hints*
-// |===
-//
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
pub(crate) fn inlay_hints(
db: &RootDatabase,
@@ -1688,6 +1682,74 @@ fn main() {
}
#[test]
+ fn iterator_hint_regression_issue_12674() {
+ // Ensure we don't crash while solving the projection type of iterators.
+ check_expect(
+ InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: iterators
+struct S<T>(T);
+impl<T> S<T> {
+ fn iter(&self) -> Iter<'_, T> { loop {} }
+}
+struct Iter<'a, T: 'a>(&'a T);
+impl<'a, T> Iterator for Iter<'a, T> {
+ type Item = &'a T;
+ fn next(&mut self) -> Option<Self::Item> { loop {} }
+}
+struct Container<'a> {
+ elements: S<&'a str>,
+}
+struct SliceIter<'a, T>(&'a T);
+impl<'a, T> Iterator for SliceIter<'a, T> {
+ type Item = &'a T;
+ fn next(&mut self) -> Option<Self::Item> { loop {} }
+}
+
+fn main(a: SliceIter<'_, Container>) {
+ a
+ .filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
+ .map(|e| e);
+}
+ "#,
+ expect![[r#"
+ [
+ InlayHint {
+ range: 484..554,
+ kind: ChainingHint,
+ label: [
+ "impl Iterator<Item = impl Iterator<Item = &&str>>",
+ ],
+ tooltip: Some(
+ HoverRanged(
+ FileId(
+ 0,
+ ),
+ 484..554,
+ ),
+ ),
+ },
+ InlayHint {
+ range: 484..485,
+ kind: ChainingHint,
+ label: [
+ "SliceIter<Container>",
+ ],
+ tooltip: Some(
+ HoverRanged(
+ FileId(
+ 0,
+ ),
+ 484..485,
+ ),
+ ),
+ },
+ ]
+ "#]],
+ );
+ }
+
+ #[test]
fn infer_call_method_return_associated_types_with_generic() {
check_types(
r#"
@@ -1962,7 +2024,14 @@ impl<T> Vec<T> {
}
impl<T> IntoIterator for Vec<T> {
- type Item=T;
+ type Item = T;
+ type IntoIter = IntoIter<T>;
+}
+
+struct IntoIter<T> {}
+
+impl<T> Iterator for IntoIter<T> {
+ type Item = T;
}
fn main() {
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 055233081..416817ca0 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -31,7 +31,6 @@ mod highlight_related;
mod expand_macro;
mod extend_selection;
mod file_structure;
-mod fn_references;
mod folding_ranges;
mod goto_declaration;
mod goto_definition;
@@ -74,7 +73,7 @@ use syntax::SourceFile;
use crate::navigation_target::{ToNav, TryToNav};
pub use crate::{
- annotations::{Annotation, AnnotationConfig, AnnotationKind},
+ annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
call_hierarchy::CallItem,
expand_macro::ExpandedMacro,
file_structure::{StructureNode, StructureNodeKind},
@@ -236,7 +235,7 @@ impl Analysis {
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo { repo: None, name: None },
);
change.change_file(file_id, Some(Arc::new(text)));
change.set_crate_graph(crate_graph);
@@ -429,11 +428,6 @@ impl Analysis {
self.with_db(|db| references::find_all_refs(&Semantics::new(db), position, search_scope))
}
- /// Finds all methods and free functions for the file. Does not return tests!
- pub fn find_all_methods(&self, file_id: FileId) -> Cancellable<Vec<FileRange>> {
- self.with_db(|db| fn_references::find_all_methods(db, file_id))
- }
-
/// Returns a short text describing element at position.
pub fn hover(
&self,
@@ -488,8 +482,18 @@ impl Analysis {
}
/// Returns crates this file belongs too.
- pub fn crate_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
- self.with_db(|db| parent_module::crate_for(db, file_id))
+ pub fn crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
+ self.with_db(|db| parent_module::crates_for(db, file_id))
+ }
+
+ /// Returns crates this file belongs too.
+ pub fn transitive_rev_deps(&self, crate_id: CrateId) -> Cancellable<Vec<CrateId>> {
+ self.with_db(|db| db.crate_graph().transitive_rev_deps(crate_id).collect())
+ }
+
+ /// Returns crates this file *might* belong too.
+ pub fn relevant_crates_for(&self, file_id: FileId) -> Cancellable<Vec<CrateId>> {
+ self.with_db(|db| db.relevant_crates(file_id).iter().copied().collect())
}
/// Returns the edition of the given crate.
diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
index 600a52630..852a8fd83 100644
--- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs
@@ -253,10 +253,14 @@ pub(crate) fn def_to_moniker(
},
kind: if krate == from_crate { MonikerKind::Export } else { MonikerKind::Import },
package_information: {
- let name = krate.display_name(db)?.to_string();
- let (repo, version) = match krate.origin(db) {
- CrateOrigin::CratesIo { repo } => (repo?, krate.version(db)?),
+ let (name, repo, version) = match krate.origin(db) {
+ CrateOrigin::CratesIo { repo, name } => (
+ name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()),
+ repo?,
+ krate.version(db)?,
+ ),
CrateOrigin::Lang(lang) => (
+ krate.display_name(db)?.canonical_name().to_string(),
"https://github.com/rust-lang/rust/".to_string(),
match lang {
LangCrateOrigin::Other => {
diff --git a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
index 8f3cc8687..506f9452c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs
@@ -1,6 +1,6 @@
-use hir::Semantics;
+use hir::{db::DefDatabase, Semantics};
use ide_db::{
- base_db::{CrateId, FileId, FilePosition},
+ base_db::{CrateId, FileId, FileLoader, FilePosition},
RootDatabase,
};
use itertools::Itertools;
@@ -55,9 +55,13 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
}
/// Returns `Vec` for the same reason as `parent_module`
-pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
- let sema = Semantics::new(db);
- sema.to_module_defs(file_id).map(|module| module.krate().into()).unique().collect()
+pub(crate) fn crates_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
+ db.relevant_crates(file_id)
+ .iter()
+ .copied()
+ .filter(|&crate_id| db.crate_def_map(crate_id).modules_for_file(file_id).next().is_some())
+ .sorted()
+ .collect()
}
#[cfg(test)]
@@ -147,7 +151,7 @@ $0
mod foo;
"#,
);
- assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1);
+ assert_eq!(analysis.crates_for(file_id).unwrap().len(), 1);
}
#[test]
@@ -162,6 +166,6 @@ mod baz;
mod baz;
"#,
);
- assert_eq!(analysis.crate_for(file_id).unwrap().len(), 2);
+ assert_eq!(analysis.crates_for(file_id).unwrap().len(), 2);
}
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs
index 99614b645..e942413c1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/references.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/references.rs
@@ -742,7 +742,7 @@ pub struct Foo {
expect![[r#"
foo Module FileId(0) 0..8 4..7
- FileId(0) 14..17
+ FileId(0) 14..17 Import
"#]],
);
}
@@ -760,7 +760,7 @@ use self$0;
expect![[r#"
foo Module FileId(0) 0..8 4..7
- FileId(1) 4..8
+ FileId(1) 4..8 Import
"#]],
);
}
@@ -775,7 +775,7 @@ use self$0;
expect![[r#"
Module FileId(0) 0..10
- FileId(0) 4..8
+ FileId(0) 4..8 Import
"#]],
);
}
@@ -803,7 +803,7 @@ pub(super) struct Foo$0 {
expect![[r#"
Foo Struct FileId(2) 0..41 18..21
- FileId(1) 20..23
+ FileId(1) 20..23 Import
FileId(1) 47..50
"#]],
);
@@ -966,7 +966,7 @@ fn g() { f(); }
expect![[r#"
f Function FileId(0) 22..31 25..26
- FileId(1) 11..12
+ FileId(1) 11..12 Import
FileId(1) 24..25
"#]],
);
@@ -1424,9 +1424,9 @@ pub use level1::Foo;
expect![[r#"
Foo Struct FileId(0) 0..15 11..14
- FileId(1) 16..19
- FileId(2) 16..19
- FileId(3) 16..19
+ FileId(1) 16..19 Import
+ FileId(2) 16..19 Import
+ FileId(3) 16..19 Import
"#]],
);
}
@@ -1454,7 +1454,7 @@ lib::foo!();
expect![[r#"
foo Macro FileId(1) 0..61 29..32
- FileId(0) 46..49
+ FileId(0) 46..49 Import
FileId(2) 0..3
FileId(3) 5..8
"#]],
@@ -1617,7 +1617,7 @@ struct Foo;
expect![[r#"
derive_identity Derive FileId(2) 1..107 45..60
- FileId(0) 17..31
+ FileId(0) 17..31 Import
FileId(0) 56..70
"#]],
);
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 9e5eb9095..27ad1a948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -210,9 +210,7 @@ fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Opt
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
if let Some(&[x]) = def.as_deref() {
return Some(x);
- } else {
- continue;
- };
+ }
}
None
}
diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs
index f4d038744..20810c25b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/status.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/status.rs
@@ -45,7 +45,7 @@ pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
if let Some(file_id) = file_id {
format_to!(buf, "\nFile info:\n");
- let crates = crate::parent_module::crate_for(db, file_id);
+ let crates = crate::parent_module::crates_for(db, file_id);
if crates.is_empty() {
format_to!(buf, "Does not belong to any crate");
}
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 9395e914c..e7d0a8be7 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
@@ -87,9 +87,9 @@ fn punctuation(
let parent = token.parent();
let parent_kind = parent.as_ref().map_or(EOF, SyntaxNode::kind);
match (kind, parent_kind) {
- (T![?], _) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
+ (T![?], TRY_EXPR) => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow,
(T![&], BIN_EXPR) => HlOperator::Bitwise.into(),
- (T![&], _) => {
+ (T![&], REF_EXPR) => {
let h = HlTag::Operator(HlOperator::Other).into();
let is_unsafe = parent
.and_then(ast::RefExpr::cast)
@@ -100,7 +100,9 @@ fn punctuation(
h
}
}
- (T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.], _) => HlOperator::Other.into(),
+ (T![::] | T![->] | T![=>] | T![..] | T![..=] | T![=] | T![@] | T![.], _) => {
+ HlOperator::Other.into()
+ }
(T![!], MACRO_CALL | MACRO_RULES) => HlPunct::MacroBang.into(),
(T![!], NEVER_TYPE) => HlTag::BuiltinType.into(),
(T![!], PREFIX_EXPR) => HlOperator::Logical.into(),
@@ -129,7 +131,7 @@ fn punctuation(
(T![+=] | T![-=] | T![*=] | T![/=] | T![%=], BIN_EXPR) => {
Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
}
- (T![|] | T![&] | T![!] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(),
+ (T![|] | T![&] | T![^] | T![>>] | T![<<], BIN_EXPR) => HlOperator::Bitwise.into(),
(T![|=] | T![&=] | T![^=] | T![>>=] | T![<<=], BIN_EXPR) => {
Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
}
@@ -137,7 +139,6 @@ fn punctuation(
(T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=], BIN_EXPR) => {
HlOperator::Comparison.into()
}
- (_, PREFIX_EXPR | BIN_EXPR | RANGE_EXPR | RANGE_PAT | REST_PAT) => HlOperator::Other.into(),
(_, ATTR) => HlTag::AttributeBracket.into(),
(kind, _) => match kind {
T!['['] | T![']'] => HlPunct::Bracket,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
index e07fd3925..9ed65fbc8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_assoc_functions.html
@@ -48,15 +48,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">impl</span> <span class="struct">foo</span> <span class="brace">{</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public static">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
- <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference">is_not_static</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">trait</span> <span class="trait declaration">t</span> <span class="brace">{</span>
<span class="keyword">fn</span> <span class="function associated declaration static trait">t_is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference trait">t_is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference trait">t_is_not_static</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">impl</span> <span class="trait">t</span> <span class="keyword">for</span> <span class="struct">foo</span> <span class="brace">{</span>
<span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public static trait">is_static</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
- <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference trait">is_not_static</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference trait">is_not_static</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index eef5baea9..18045f1f5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -125,7 +125,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="comment documentation">/// ```sh</span>
<span class="comment documentation">/// echo 1</span>
<span class="comment documentation">/// ```</span>
- <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference">foo</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
+ <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function associated declaration public reference">foo</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">bool</span> <span class="brace">{</span>
<span class="bool_literal">true</span>
<span class="brace">}</span>
<span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index a97802cbb..9f2b1926b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -61,11 +61,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">trait</span> <span class="trait declaration">Bar</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference trait">bar</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference trait">bar</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference trait">bar</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
<span class="self_keyword reference">self</span><span class="operator">.</span><span class="field">x</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -75,11 +75,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="value_param">f</span><span class="operator">.</span><span class="function associated consuming">baz</span><span class="parenthesis">(</span><span class="self_keyword consuming mutable">self</span><span class="parenthesis">)</span>
<span class="brace">}</span>
- <span class="keyword">fn</span> <span class="function associated declaration mutable reference">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
+ <span class="keyword">fn</span> <span class="function associated declaration mutable reference">qux</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="self_keyword mutable reference">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
<span class="brace">}</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference">quop</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">i32</span> <span class="brace">{</span>
<span class="self_keyword reference">self</span><span class="operator">.</span><span class="field">x</span>
<span class="brace">}</span>
<span class="brace">}</span>
@@ -96,11 +96,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="value_param">f</span><span class="operator">.</span><span class="function associated">baz</span><span class="parenthesis">(</span><span class="self_keyword">self</span><span class="parenthesis">)</span>
<span class="brace">}</span>
- <span class="keyword">fn</span> <span class="function associated declaration mutable reference">qux</span><span class="parenthesis">(</span><span class="operator">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
+ <span class="keyword">fn</span> <span class="function associated declaration mutable reference">qux</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="keyword">mut</span> <span class="self_keyword declaration mutable reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="self_keyword mutable reference">self</span><span class="operator">.</span><span class="field">x</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
<span class="brace">}</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference">quop</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference">quop</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">u32</span> <span class="brace">{</span>
<span class="self_keyword reference">self</span><span class="operator">.</span><span class="field">x</span>
<span class="brace">}</span>
<span class="brace">}</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
index ced7d22f0..abcd80c28 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection.html
@@ -42,7 +42,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
-<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="operator">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+<pre><code><span class="keyword">fn</span> <span class="function declaration">fixture</span><span class="parenthesis">(</span><span class="value_param declaration reference">ra_fixture</span><span class="colon">:</span> <span class="punctuation">&</span><span class="builtin_type">str</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="function">fixture</span><span class="parenthesis">(</span><span class="string_literal">r#"</span>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
index 2d85fc8c9..f98e0b1cd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_lifetimes.html
@@ -45,8 +45,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<pre><code>
<span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="attribute attribute default_library library">derive</span><span class="parenthesis attribute">(</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
<span class="keyword">struct</span> <span class="struct declaration">Foo</span><span class="angle">&lt;</span><span class="lifetime declaration">'a</span><span class="comma">,</span> <span class="lifetime declaration">'b</span><span class="comma">,</span> <span class="lifetime declaration">'c</span><span class="angle">&gt;</span> <span class="keyword">where</span> <span class="lifetime">'a</span><span class="colon">:</span> <span class="lifetime">'a</span><span class="comma">,</span> <span class="lifetime">'static</span><span class="colon">:</span> <span class="lifetime">'static</span> <span class="brace">{</span>
- <span class="field declaration">field</span><span class="colon">:</span> <span class="operator">&</span><span class="lifetime">'a</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
- <span class="field declaration">field2</span><span class="colon">:</span> <span class="operator">&</span><span class="lifetime">'static</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
+ <span class="field declaration">field</span><span class="colon">:</span> <span class="punctuation">&</span><span class="lifetime">'a</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
+ <span class="field declaration">field2</span><span class="colon">:</span> <span class="punctuation">&</span><span class="lifetime">'static</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="comma">,</span>
<span class="brace">}</span>
<span class="keyword">impl</span><span class="angle">&lt;</span><span class="lifetime declaration">'a</span><span class="angle">&gt;</span> <span class="struct">Foo</span><span class="angle">&lt;</span><span class="lifetime">'_</span><span class="comma">,</span> <span class="lifetime">'a</span><span class="comma">,</span> <span class="lifetime">'static</span><span class="angle">&gt;</span>
<span class="keyword">where</span>
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 c627bc9b0..a626cda3f 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
@@ -62,16 +62,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<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>panicking<span class="colon">:</span><span class="colon">:</span>panic<span class="parenthesis">(</span><span class="string_literal">"explicit panic"</span><span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>literal <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>literal <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><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<span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="comment">// Use `panic_str` instead of `panic_display::&lt;&str&gt;` for non_fmt_panic lint.</span>
- <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="parenthesis">(</span>
+ <span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><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_str<span class="parenthesis">(</span><span class="punctuation">$</span>msg<span class="parenthesis">)</span>
<span class="parenthesis">)</span><span class="comma">,</span>
<span class="comment">// Special-case the single-argument case for const_panic.</span>
- <span class="parenthesis">(</span><span class="string_literal">"{}"</span><span class="comma">,</span> <span class="punctuation">$</span>arg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><span class="parenthesis">)</span><span class="operator control">?</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_display<span class="parenthesis">(</span><span class="operator">&</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span>
+ <span class="parenthesis">(</span><span class="string_literal">"{}"</span><span class="comma">,</span> <span class="punctuation">$</span>arg<span class="colon">:</span>expr <span class="punctuation">$</span><span class="parenthesis">(</span><span class="comma">,</span><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_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>
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 0716bae75..1992bdc6a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -49,7 +49,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span>
<span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="angle">&gt;</span> <span class="brace">{</span>
- <span class="punctuation">*</span><span class="parenthesis">(</span><span class="operator">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
+ <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span>
<span class="brace">}</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable unsafe">MUT_GLOBAL</span><span class="colon">:</span> <span class="struct">Struct</span> <span class="operator">=</span> <span class="struct">Struct</span> <span class="brace">{</span> <span class="field">field</span><span class="colon">:</span> <span class="numeric_literal">0</span> <span class="brace">}</span><span class="semicolon">;</span>
@@ -63,7 +63,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">struct</span> <span class="struct declaration">Struct</span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="builtin_type">i32</span> <span class="brace">}</span>
<span class="keyword">impl</span> <span class="struct">Struct</span> <span class="brace">{</span>
- <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="keyword unsafe">unsafe</span> <span class="keyword">fn</span> <span class="function associated declaration reference unsafe">unsafe_method</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</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">repr</span><span class="parenthesis attribute">(</span><span class="none attribute">packed</span><span class="parenthesis attribute">)</span><span class="attribute_bracket attribute">]</span>
@@ -78,11 +78,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="keyword">fn</span> <span class="function declaration">unsafe_trait_bound</span><span class="angle">&lt;</span><span class="type_param declaration">T</span><span class="colon">:</span> <span class="trait">UnsafeTrait</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="type_param">T</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="keyword">trait</span> <span class="trait declaration">DoTheAutoref</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">impl</span> <span class="trait">DoTheAutoref</span> <span class="keyword">for</span> <span class="builtin_type">u16</span> <span class="brace">{</span>
- <span class="keyword">fn</span> <span class="function associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="operator">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
+ <span class="keyword">fn</span> <span class="function associated declaration reference trait">calls_autoref</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="self_keyword declaration reference">self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
index 5ff3448a1..13cd89010 100644
--- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
rustc-hash = "1.1.0"
-smallvec = "1.9.0"
+smallvec = "1.10.0"
tracing = "0.1.35"
syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
index ac691578d..9c92bae6a 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs
@@ -8,7 +8,7 @@ use syntax::{
use test_utils::{bench, bench_fixture, skip_slow_tests};
use crate::{
- parser::{Op, RepeatKind, Separator},
+ parser::{MetaVarKind, Op, RepeatKind, Separator},
syntax_node_to_token_tree, DeclarativeMacro,
};
@@ -111,35 +111,35 @@ fn invocation_fixtures(rules: &FxHashMap<String, DeclarativeMacro>) -> Vec<(Stri
fn collect_from_op(op: &Op, parent: &mut tt::Subtree, seed: &mut usize) {
return match op {
- Op::Var { kind, .. } => match kind.as_ref().map(|it| it.as_str()) {
- Some("ident") => parent.token_trees.push(make_ident("foo")),
- Some("ty") => parent.token_trees.push(make_ident("Foo")),
- Some("tt") => parent.token_trees.push(make_ident("foo")),
- Some("vis") => parent.token_trees.push(make_ident("pub")),
- Some("pat") => parent.token_trees.push(make_ident("foo")),
- Some("path") => parent.token_trees.push(make_ident("foo")),
- Some("literal") => parent.token_trees.push(make_literal("1")),
- Some("expr") => parent.token_trees.push(make_ident("foo")),
- Some("lifetime") => {
+ Op::Var { kind, .. } => match kind.as_ref() {
+ Some(MetaVarKind::Ident) => parent.token_trees.push(make_ident("foo")),
+ Some(MetaVarKind::Ty) => parent.token_trees.push(make_ident("Foo")),
+ Some(MetaVarKind::Tt) => parent.token_trees.push(make_ident("foo")),
+ Some(MetaVarKind::Vis) => parent.token_trees.push(make_ident("pub")),
+ Some(MetaVarKind::Pat) => parent.token_trees.push(make_ident("foo")),
+ Some(MetaVarKind::Path) => parent.token_trees.push(make_ident("foo")),
+ Some(MetaVarKind::Literal) => parent.token_trees.push(make_literal("1")),
+ Some(MetaVarKind::Expr) => parent.token_trees.push(make_ident("foo")),
+ Some(MetaVarKind::Lifetime) => {
parent.token_trees.push(make_punct('\''));
parent.token_trees.push(make_ident("a"));
}
- Some("block") => {
+ Some(MetaVarKind::Block) => {
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None))
}
- Some("item") => {
+ Some(MetaVarKind::Item) => {
parent.token_trees.push(make_ident("fn"));
parent.token_trees.push(make_ident("foo"));
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
parent.token_trees.push(make_subtree(tt::DelimiterKind::Brace, None));
}
- Some("meta") => {
+ Some(MetaVarKind::Meta) => {
parent.token_trees.push(make_ident("foo"));
parent.token_trees.push(make_subtree(tt::DelimiterKind::Parenthesis, None));
}
None => (),
- Some(kind) => panic!("Unhandled kind {}", kind),
+ Some(kind) => panic!("Unhandled kind {:?}", kind),
},
Op::Leaf(leaf) => parent.token_trees.push(leaf.clone().into()),
Op::Repeat { tokens, kind, separator } => {
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander.rs b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
index 1e1bfa550..100ec6bfb 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander.rs
@@ -8,7 +8,7 @@ mod transcriber;
use rustc_hash::FxHashMap;
use syntax::SmolStr;
-use crate::{ExpandError, ExpandResult};
+use crate::{parser::MetaVarKind, ExpandError, ExpandResult};
pub(crate) fn expand_rules(
rules: &[crate::Rule],
@@ -104,6 +104,7 @@ enum Binding {
Fragment(Fragment),
Nested(Vec<Binding>),
Empty,
+ Missing(MetaVarKind),
}
#[derive(Debug, Clone, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
index c1aa14d6b..3f656df25 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs
@@ -66,7 +66,7 @@ use syntax::SmolStr;
use crate::{
expander::{Binding, Bindings, ExpandResult, Fragment},
- parser::{Op, RepeatKind, Separator},
+ parser::{MetaVarKind, Op, RepeatKind, Separator},
tt_iter::TtIter,
ExpandError, MetaTemplate,
};
@@ -119,6 +119,7 @@ pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match {
.map(|it| match it {
Binding::Fragment(_) => 1,
Binding::Empty => 1,
+ Binding::Missing(_) => 1,
Binding::Nested(it) => count(it.iter()),
})
.sum()
@@ -130,6 +131,7 @@ enum BindingKind {
Empty(SmolStr),
Optional(SmolStr),
Fragment(SmolStr, Fragment),
+ Missing(SmolStr, MetaVarKind),
Nested(usize, usize),
}
@@ -190,6 +192,10 @@ impl BindingsBuilder {
.push(LinkNode::Node(Rc::new(BindingKind::Fragment(var.clone(), fragment))));
}
+ fn push_missing(&mut self, idx: &mut BindingsIdx, var: &SmolStr, kind: MetaVarKind) {
+ self.nodes[idx.0].push(LinkNode::Node(Rc::new(BindingKind::Missing(var.clone(), kind))));
+ }
+
fn push_nested(&mut self, parent: &mut BindingsIdx, child: &BindingsIdx) {
let BindingsIdx(idx, nidx) = self.copy(child);
self.nodes[parent.0].push(LinkNode::Node(Rc::new(BindingKind::Nested(idx, nidx))));
@@ -203,17 +209,16 @@ impl BindingsBuilder {
}
fn build(self, idx: &BindingsIdx) -> Bindings {
- let mut bindings = Bindings::default();
- self.build_inner(&mut bindings, &self.nodes[idx.0]);
- bindings
+ self.build_inner(&self.nodes[idx.0])
}
- fn build_inner(&self, bindings: &mut Bindings, link_nodes: &[LinkNode<Rc<BindingKind>>]) {
+ fn build_inner(&self, link_nodes: &[LinkNode<Rc<BindingKind>>]) -> Bindings {
+ let mut bindings = Bindings::default();
let mut nodes = Vec::new();
self.collect_nodes(link_nodes, &mut nodes);
for cmd in nodes {
- match &**cmd {
+ match cmd {
BindingKind::Empty(name) => {
bindings.push_empty(name);
}
@@ -223,6 +228,9 @@ impl BindingsBuilder {
BindingKind::Fragment(name, fragment) => {
bindings.inner.insert(name.clone(), Binding::Fragment(fragment.clone()));
}
+ BindingKind::Missing(name, kind) => {
+ bindings.inner.insert(name.clone(), Binding::Missing(*kind));
+ }
BindingKind::Nested(idx, nested_idx) => {
let mut nested_nodes = Vec::new();
self.collect_nested(*idx, *nested_idx, &mut nested_nodes);
@@ -246,13 +254,15 @@ impl BindingsBuilder {
}
}
}
+
+ bindings
}
fn collect_nested_ref<'a>(
&'a self,
id: usize,
len: usize,
- nested_refs: &mut Vec<&'a Vec<LinkNode<Rc<BindingKind>>>>,
+ nested_refs: &mut Vec<&'a [LinkNode<Rc<BindingKind>>]>,
) {
self.nested[id].iter().take(len).for_each(|it| match it {
LinkNode::Node(id) => nested_refs.push(&self.nodes[*id]),
@@ -262,26 +272,16 @@ impl BindingsBuilder {
fn collect_nested(&self, idx: usize, nested_idx: usize, nested: &mut Vec<Bindings>) {
let last = &self.nodes[idx];
- let mut nested_refs = Vec::new();
+ let mut nested_refs: Vec<&[_]> = Vec::new();
self.nested[nested_idx].iter().for_each(|it| match *it {
LinkNode::Node(idx) => nested_refs.push(&self.nodes[idx]),
LinkNode::Parent { idx, len } => self.collect_nested_ref(idx, len, &mut nested_refs),
});
nested_refs.push(last);
-
- nested_refs.into_iter().for_each(|iter| {
- let mut child_bindings = Bindings::default();
- self.build_inner(&mut child_bindings, iter);
- nested.push(child_bindings)
- })
+ nested.extend(nested_refs.into_iter().map(|iter| self.build_inner(iter)));
}
- fn collect_nodes_ref<'a>(
- &'a self,
- id: usize,
- len: usize,
- nodes: &mut Vec<&'a Rc<BindingKind>>,
- ) {
+ fn collect_nodes_ref<'a>(&'a self, id: usize, len: usize, nodes: &mut Vec<&'a BindingKind>) {
self.nodes[id].iter().take(len).for_each(|it| match it {
LinkNode::Node(it) => nodes.push(it),
LinkNode::Parent { idx, len } => self.collect_nodes_ref(*idx, *len, nodes),
@@ -291,7 +291,7 @@ impl BindingsBuilder {
fn collect_nodes<'a>(
&'a self,
link_nodes: &'a [LinkNode<Rc<BindingKind>>],
- nodes: &mut Vec<&'a Rc<BindingKind>>,
+ nodes: &mut Vec<&'a BindingKind>,
) {
link_nodes.iter().for_each(|it| match it {
LinkNode::Node(it) => nodes.push(it),
@@ -386,10 +386,10 @@ fn match_loop_inner<'t>(
let op = match item.dot.peek() {
None => {
// We are at or past the end of the matcher of `item`.
- if item.up.is_some() {
+ if let Some(up) = &item.up {
if item.sep_parsed.is_none() {
// Get the `up` matcher
- let mut new_pos = *item.up.clone().unwrap();
+ let mut new_pos = (**up).clone();
new_pos.bindings = bindings_builder.copy(&new_pos.bindings);
// Add matches from this repetition to the `matches` of `up`
bindings_builder.push_nested(&mut new_pos.bindings, &item.bindings);
@@ -402,7 +402,7 @@ fn match_loop_inner<'t>(
// Check if we need a separator.
// We check the separator one by one
- let sep_idx = *item.sep_parsed.as_ref().unwrap_or(&0);
+ let sep_idx = item.sep_parsed.unwrap_or(0);
let sep_len = item.sep.as_ref().map_or(0, Separator::tt_count);
if item.sep.is_some() && sep_idx != sep_len {
let sep = item.sep.as_ref().unwrap();
@@ -467,9 +467,9 @@ fn match_loop_inner<'t>(
}
}
OpDelimited::Op(Op::Var { kind, name, .. }) => {
- if let Some(kind) = kind {
+ if let &Some(kind) = kind {
let mut fork = src.clone();
- let match_res = match_meta_var(kind.as_str(), &mut fork);
+ let match_res = match_meta_var(kind, &mut fork);
match match_res.err {
None => {
// Some meta variables are optional (e.g. vis)
@@ -484,8 +484,15 @@ fn match_loop_inner<'t>(
}
Some(err) => {
res.add_err(err);
- if let Some(fragment) = match_res.value {
- bindings_builder.push_fragment(&mut item.bindings, name, fragment);
+ match match_res.value {
+ Some(fragment) => bindings_builder.push_fragment(
+ &mut item.bindings,
+ name,
+ fragment,
+ ),
+ None => {
+ bindings_builder.push_missing(&mut item.bindings, name, kind)
+ }
}
item.is_error = true;
error_items.push(item);
@@ -677,20 +684,20 @@ fn match_leaf(lhs: &tt::Leaf, src: &mut TtIter<'_>) -> Result<(), ExpandError> {
}
}
-fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
+fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult<Option<Fragment>> {
let fragment = match kind {
- "path" => parser::PrefixEntryPoint::Path,
- "ty" => parser::PrefixEntryPoint::Ty,
+ MetaVarKind::Path => parser::PrefixEntryPoint::Path,
+ MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
// FIXME: These two should actually behave differently depending on the edition.
//
// https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html
- "pat" | "pat_param" => parser::PrefixEntryPoint::Pat,
- "stmt" => parser::PrefixEntryPoint::Stmt,
- "block" => parser::PrefixEntryPoint::Block,
- "meta" => parser::PrefixEntryPoint::MetaItem,
- "item" => parser::PrefixEntryPoint::Item,
- "vis" => parser::PrefixEntryPoint::Vis,
- "expr" => {
+ MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
+ MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
+ MetaVarKind::Block => parser::PrefixEntryPoint::Block,
+ MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem,
+ MetaVarKind::Item => parser::PrefixEntryPoint::Item,
+ MetaVarKind::Vis => parser::PrefixEntryPoint::Vis,
+ MetaVarKind::Expr => {
// `expr` should not match underscores.
// HACK: Macro expansion should not be done using "rollback and try another alternative".
// rustc [explicitly checks the next token][0].
@@ -707,17 +714,17 @@ fn match_meta_var(kind: &str, input: &mut TtIter<'_>) -> ExpandResult<Option<Fra
}
_ => {
let tt_result = match kind {
- "ident" => input
+ MetaVarKind::Ident => input
.expect_ident()
.map(|ident| tt::Leaf::from(ident.clone()).into())
.map_err(|()| ExpandError::binding_error("expected ident")),
- "tt" => input
+ MetaVarKind::Tt => input
.expect_tt()
.map_err(|()| ExpandError::binding_error("expected token tree")),
- "lifetime" => input
+ MetaVarKind::Lifetime => input
.expect_lifetime()
.map_err(|()| ExpandError::binding_error("expected lifetime")),
- "literal" => {
+ MetaVarKind::Literal => {
let neg = input.eat_char('-');
input
.expect_literal()
diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
index 7bcc84740..cbb59ab8e 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs
@@ -6,7 +6,7 @@ use tt::{Delimiter, Subtree};
use crate::{
expander::{Binding, Bindings, Fragment},
- parser::{Op, RepeatKind, Separator},
+ parser::{MetaVarKind, Op, RepeatKind, Separator},
ExpandError, ExpandResult, MetaTemplate,
};
@@ -15,7 +15,7 @@ impl Bindings {
self.inner.contains_key(name)
}
- fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<&Fragment, ExpandError> {
+ fn get(&self, name: &str, nesting: &mut [NestingState]) -> Result<Fragment, ExpandError> {
macro_rules! binding_err {
($($arg:tt)*) => { ExpandError::binding_error(format!($($arg)*)) };
}
@@ -26,6 +26,7 @@ impl Bindings {
nesting_state.hit = true;
b = match b {
Binding::Fragment(_) => break,
+ Binding::Missing(_) => break,
Binding::Nested(bs) => bs.get(nesting_state.idx).ok_or_else(|| {
nesting_state.at_end = true;
binding_err!("could not find nested binding `{name}`")
@@ -37,7 +38,55 @@ impl Bindings {
};
}
match b {
- Binding::Fragment(it) => Ok(it),
+ Binding::Fragment(it) => Ok(it.clone()),
+ // emit some reasonable default expansion for missing bindings,
+ // this gives better recovery than emitting the `$fragment-name` verbatim
+ Binding::Missing(it) => Ok(match it {
+ MetaVarKind::Stmt => {
+ Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
+ id: tt::TokenId::unspecified(),
+ char: ';',
+ spacing: tt::Spacing::Alone,
+ })))
+ }
+ MetaVarKind::Block => Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
+ delimiter: Some(tt::Delimiter {
+ id: tt::TokenId::unspecified(),
+ kind: tt::DelimiterKind::Brace,
+ }),
+ token_trees: vec![],
+ })),
+ // FIXME: Meta and Item should get proper defaults
+ MetaVarKind::Meta | MetaVarKind::Item | MetaVarKind::Tt | MetaVarKind::Vis => {
+ Fragment::Tokens(tt::TokenTree::Subtree(tt::Subtree {
+ delimiter: None,
+ token_trees: vec![],
+ }))
+ }
+ MetaVarKind::Path
+ | MetaVarKind::Ty
+ | MetaVarKind::Pat
+ | MetaVarKind::PatParam
+ | MetaVarKind::Expr
+ | MetaVarKind::Ident => {
+ Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+ text: SmolStr::new_inline("missing"),
+ id: tt::TokenId::unspecified(),
+ })))
+ }
+ MetaVarKind::Lifetime => {
+ Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+ text: SmolStr::new_inline("'missing"),
+ id: tt::TokenId::unspecified(),
+ })))
+ }
+ MetaVarKind::Literal => {
+ Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
+ text: SmolStr::new_inline("\"missing\""),
+ id: tt::TokenId::unspecified(),
+ })))
+ }
+ }),
Binding::Nested(_) => {
Err(binding_err!("expected simple binding, found nested binding `{name}`"))
}
@@ -157,7 +206,7 @@ fn expand_var(ctx: &mut ExpandCtx<'_>, v: &SmolStr, id: tt::TokenId) -> ExpandRe
} else {
ctx.bindings.get(v, &mut ctx.nesting).map_or_else(
|e| ExpandResult { value: Fragment::Tokens(tt::TokenTree::empty()), err: Some(e) },
- |b| ExpandResult::ok(b.clone()),
+ |it| ExpandResult::ok(it),
)
}
}
diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
index 79da84f4a..c4f0fa20d 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs
@@ -21,7 +21,7 @@ mod token_map;
use std::fmt;
use crate::{
- parser::{MetaTemplate, Op},
+ parser::{MetaTemplate, MetaVarKind, Op},
tt_iter::TtIter,
};
@@ -291,9 +291,9 @@ fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
// Checks that no repetition which could match an empty token
// https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
let lsh_is_empty_seq = separator.is_none() && subtree.iter().all(|child_op| {
- match child_op {
+ match *child_op {
// vis is optional
- Op::Var { kind: Some(kind), .. } => kind == "vis",
+ Op::Var { kind: Some(kind), .. } => kind == MetaVarKind::Vis,
Op::Repeat {
kind: parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne,
..
diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
index acb4be584..351c359b7 100644
--- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs
@@ -50,7 +50,7 @@ impl MetaTemplate {
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Op {
- Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
+ Var { name: SmolStr, kind: Option<MetaVarKind>, id: tt::TokenId },
Ignore { name: SmolStr, id: tt::TokenId },
Index { depth: u32 },
Repeat { tokens: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
@@ -65,6 +65,24 @@ pub(crate) enum RepeatKind {
ZeroOrOne,
}
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub(crate) enum MetaVarKind {
+ Path,
+ Ty,
+ Pat,
+ PatParam,
+ Stmt,
+ Block,
+ Meta,
+ Item,
+ Vis,
+ Expr,
+ Ident,
+ Tt,
+ Lifetime,
+ Literal,
+}
+
#[derive(Clone, Debug, Eq)]
pub(crate) enum Separator {
Literal(tt::Literal),
@@ -179,13 +197,30 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul
Ok(res)
}
-fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<SmolStr>, ParseError> {
+fn eat_fragment_kind(src: &mut TtIter<'_>, mode: Mode) -> Result<Option<MetaVarKind>, ParseError> {
if let Mode::Pattern = mode {
src.expect_char(':').map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
let ident = src
.expect_ident()
.map_err(|()| ParseError::unexpected("missing fragment specifier"))?;
- return Ok(Some(ident.text.clone()));
+ let kind = match ident.text.as_str() {
+ "path" => MetaVarKind::Path,
+ "ty" => MetaVarKind::Ty,
+ "pat" => MetaVarKind::Pat,
+ "pat_param" => MetaVarKind::PatParam,
+ "stmt" => MetaVarKind::Stmt,
+ "block" => MetaVarKind::Block,
+ "meta" => MetaVarKind::Meta,
+ "item" => MetaVarKind::Item,
+ "vis" => MetaVarKind::Vis,
+ "expr" => MetaVarKind::Expr,
+ "ident" => MetaVarKind::Ident,
+ "tt" => MetaVarKind::Tt,
+ "lifetime" => MetaVarKind::Lifetime,
+ "literal" => MetaVarKind::Literal,
+ _ => return Ok(None),
+ };
+ return Ok(Some(kind));
};
Ok(None)
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
index 85a1c13fe..54879c187 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/Cargo.toml
@@ -19,7 +19,7 @@ object = { version = "0.29.0", default-features = false, features = [
] }
serde = { version = "1.0.137", features = ["derive"] }
serde_json = { version = "1.0.81", features = ["unbounded_depth"] }
-tracing = "0.1.35"
+tracing = "0.1.37"
memmap2 = "0.5.4"
snap = "1.0.5"
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
index 44712f419..243972b04 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs
@@ -11,6 +11,8 @@ use proc_macro_api::ProcMacroKind;
use super::PanicMessage;
+pub use ra_server::TokenStream;
+
pub(crate) struct Abi {
exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
index f7d3a3091..2f854bc15 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
@@ -32,8 +32,8 @@ mod abi_sysroot;
include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
// Used by `test/utils.rs`
-#[cfg(test)]
-pub(crate) use abi_1_63::TokenStream as TestTokenStream;
+#[cfg(all(test, feature = "sysroot-abi"))]
+pub(crate) use abi_sysroot::TokenStream as TestTokenStream;
use super::dylib::LoadProcMacroDylibError;
pub(crate) use abi_1_58::Abi as Abi_1_58;
@@ -144,3 +144,10 @@ impl Abi {
}
}
}
+
+#[test]
+fn test_version_check() {
+ let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
+ let info = proc_macro_api::read_dylib_info(&path).unwrap();
+ assert!(info.version.1 >= 50);
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index 3679bfc43..72a2dfe72 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -20,6 +20,8 @@
mod dylib;
mod abis;
+pub mod cli;
+
use std::{
collections::{hash_map::Entry, HashMap},
env,
@@ -149,7 +151,10 @@ impl EnvSnapshot {
}
}
-pub mod cli;
+#[cfg(all(feature = "sysroot-abi", test))]
+mod tests;
#[cfg(test)]
-mod tests;
+pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
+ proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
index 6339d56d0..b46cdddcf 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs
@@ -2,10 +2,10 @@
#[macro_use]
mod utils;
-use expect_test::expect;
-use paths::AbsPathBuf;
use utils::*;
+use expect_test::expect;
+
#[test]
fn test_derive_empty() {
assert_expand("DeriveEmpty", r#"struct S;"#, expect![[r#"SUBTREE $"#]]);
@@ -157,10 +157,3 @@ fn list_test_macros() {
DeriveError [CustomDerive]"#]]
.assert_eq(&res);
}
-
-#[test]
-fn test_version_check() {
- let path = AbsPathBuf::assert(fixtures::proc_macro_test_dylib_path());
- let info = proc_macro_api::read_dylib_info(&path).unwrap();
- assert!(info.version.1 >= 50);
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
index f881fe868..44b1b6588 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs
@@ -1,15 +1,9 @@
//! utils used in proc-macro tests
-use crate::dylib;
-use crate::ProcMacroSrv;
use expect_test::Expect;
use std::str::FromStr;
-pub mod fixtures {
- pub fn proc_macro_test_dylib_path() -> std::path::PathBuf {
- proc_macro_test::PROC_MACRO_TEST_LOCATION.into()
- }
-}
+use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv};
fn parse_string(code: &str) -> Option<crate::abis::TestTokenStream> {
// This is a bit strange. We need to parse a string into a token stream into
@@ -30,7 +24,7 @@ pub fn assert_expand_attr(macro_name: &str, ra_fixture: &str, attr_args: &str, e
}
fn assert_expand_impl(macro_name: &str, input: &str, attr: Option<&str>, expect: Expect) {
- let path = fixtures::proc_macro_test_dylib_path();
+ let path = proc_macro_test_dylib_path();
let expander = dylib::Expander::new(&path).unwrap();
let fixture = parse_string(input).unwrap();
let attr = attr.map(|attr| parse_string(attr).unwrap().into_subtree());
@@ -40,7 +34,7 @@ fn assert_expand_impl(macro_name: &str, input: &str, attr: Option<&str>, expect:
}
pub(crate) fn list() -> Vec<String> {
- let dylib_path = fixtures::proc_macro_test_dylib_path();
+ let dylib_path = proc_macro_test_dylib_path();
let mut srv = ProcMacroSrv::default();
let res = srv.list_macros(&dylib_path).unwrap();
res.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
diff --git a/src/tools/rust-analyzer/crates/profile/Cargo.toml b/src/tools/rust-analyzer/crates/profile/Cargo.toml
index 0b78a45a2..5697aea96 100644
--- a/src/tools/rust-analyzer/crates/profile/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/profile/Cargo.toml
@@ -10,9 +10,9 @@ rust-version = "1.57"
doctest = false
[dependencies]
-once_cell = "1.12.0"
+once_cell = "1.15.0"
cfg-if = "1.0.0"
-libc = "0.2.126"
+libc = "0.2.135"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
countme = { version = "3.0.1", features = ["enable"] }
jemalloc-ctl = { version = "0.5.0", package = "tikv-jemalloc-ctl", optional = true }
diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
index bc75d6faa..cf9868740 100644
--- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml
@@ -13,11 +13,10 @@ doctest = false
tracing = "0.1.35"
rustc-hash = "1.1.0"
cargo_metadata = "0.15.0"
-semver = "1.0.10"
+semver = "1.0.14"
serde = { version = "1.0.137", features = ["derive"] }
-serde_json = "1.0.81"
-anyhow = "1.0.57"
-expect-test = "1.4.0"
+serde_json = "1.0.86"
+anyhow = "1.0.62"
la-arena = { version = "0.3.0", path = "../../lib/la-arena" }
cfg = { path = "../cfg", version = "0.0.0" }
@@ -26,3 +25,6 @@ toolchain = { path = "../toolchain", version = "0.0.0" }
paths = { path = "../paths", version = "0.0.0" }
stdx = { path = "../stdx", version = "0.0.0" }
profile = { path = "../profile", version = "0.0.0" }
+
+[dev-dependencies]
+expect-test = "1.4.0"
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
index 84e772d16..a26a7c57a 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_scripts.rs
@@ -6,7 +6,12 @@
//! This module implements this second part. We use "build script" terminology
//! here, but it covers procedural macros as well.
-use std::{cell::RefCell, io, path::PathBuf, process::Command};
+use std::{
+ cell::RefCell,
+ io, mem,
+ path::{self, PathBuf},
+ process::Command,
+};
use cargo_metadata::{camino::Utf8Path, Message};
use la_arena::ArenaMap;
@@ -15,11 +20,14 @@ use rustc_hash::FxHashMap;
use semver::Version;
use serde::Deserialize;
-use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package};
+use crate::{
+ cfg_flag::CfgFlag, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
+ InvocationStrategy, Package,
+};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct WorkspaceBuildScripts {
- outputs: ArenaMap<Package, Option<BuildScriptOutput>>,
+ outputs: ArenaMap<Package, BuildScriptOutput>,
error: Option<String>,
}
@@ -38,43 +46,71 @@ pub(crate) struct BuildScriptOutput {
pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}
+impl BuildScriptOutput {
+ fn is_unchanged(&self) -> bool {
+ self.cfgs.is_empty()
+ && self.envs.is_empty()
+ && self.out_dir.is_none()
+ && self.proc_macro_dylib_path.is_none()
+ }
+}
+
impl WorkspaceBuildScripts {
- fn build_command(config: &CargoConfig) -> Command {
- if let Some([program, args @ ..]) = config.run_build_script_command.as_deref() {
- let mut cmd = Command::new(program);
- cmd.args(args);
- return cmd;
- }
+ fn build_command(config: &CargoConfig) -> io::Result<Command> {
+ let mut cmd = match config.run_build_script_command.as_deref() {
+ Some([program, args @ ..]) => {
+ let mut cmd = Command::new(program);
+ cmd.args(args);
+ cmd
+ }
+ _ => {
+ let mut cmd = Command::new(toolchain::cargo());
- let mut cmd = Command::new(toolchain::cargo());
+ cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
- cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]);
+ // --all-targets includes tests, benches and examples in addition to the
+ // default lib and bins. This is an independent concept from the --targets
+ // flag below.
+ cmd.arg("--all-targets");
- // --all-targets includes tests, benches and examples in addition to the
- // default lib and bins. This is an independent concept from the --targets
- // flag below.
- cmd.arg("--all-targets");
+ if let Some(target) = &config.target {
+ cmd.args(&["--target", target]);
+ }
- if let Some(target) = &config.target {
- cmd.args(&["--target", target]);
- }
+ match &config.features {
+ CargoFeatures::All => {
+ cmd.arg("--all-features");
+ }
+ CargoFeatures::Selected { features, no_default_features } => {
+ if *no_default_features {
+ cmd.arg("--no-default-features");
+ }
+ if !features.is_empty() {
+ cmd.arg("--features");
+ cmd.arg(features.join(" "));
+ }
+ }
+ }
- if config.all_features {
- cmd.arg("--all-features");
- } else {
- if config.no_default_features {
- cmd.arg("--no-default-features");
- }
- if !config.features.is_empty() {
- cmd.arg("--features");
- cmd.arg(config.features.join(" "));
+ cmd
}
+ };
+
+ cmd.envs(&config.extra_env);
+ if config.wrap_rustc_in_build_scripts {
+ // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
+ // that to compile only proc macros and build scripts during the initial
+ // `cargo check`.
+ let myself = std::env::current_exe()?;
+ cmd.env("RUSTC_WRAPPER", myself);
+ cmd.env("RA_RUSTC_WRAPPER", "1");
}
- cmd
+ Ok(cmd)
}
- pub(crate) fn run(
+ /// Runs the build scripts for the given workspace
+ pub(crate) fn run_for_workspace(
config: &CargoConfig,
workspace: &CargoWorkspace,
progress: &dyn Fn(String),
@@ -82,15 +118,23 @@ impl WorkspaceBuildScripts {
) -> io::Result<WorkspaceBuildScripts> {
const RUST_1_62: Version = Version::new(1, 62, 0);
- match Self::run_(Self::build_command(config), config, workspace, progress) {
+ let current_dir = match &config.invocation_location {
+ InvocationLocation::Root(root) if config.run_build_script_command.is_some() => {
+ root.as_path()
+ }
+ _ => &workspace.workspace_root(),
+ }
+ .as_ref();
+
+ match Self::run_per_ws(Self::build_command(config)?, workspace, current_dir, progress) {
Ok(WorkspaceBuildScripts { error: Some(error), .. })
if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) =>
{
// building build scripts failed, attempt to build with --keep-going so
// that we potentially get more build data
- let mut cmd = Self::build_command(config);
+ let mut cmd = Self::build_command(config)?;
cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1");
- let mut res = Self::run_(cmd, config, workspace, progress)?;
+ let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
res.error = Some(error);
Ok(res)
}
@@ -98,23 +142,90 @@ impl WorkspaceBuildScripts {
}
}
- fn run_(
- mut cmd: Command,
+ /// Runs the build scripts by invoking the configured command *once*.
+ /// This populates the outputs for all passed in workspaces.
+ pub(crate) fn run_once(
config: &CargoConfig,
- workspace: &CargoWorkspace,
+ workspaces: &[&CargoWorkspace],
progress: &dyn Fn(String),
- ) -> io::Result<WorkspaceBuildScripts> {
- if config.wrap_rustc_in_build_scripts {
- // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
- // that to compile only proc macros and build scripts during the initial
- // `cargo check`.
- let myself = std::env::current_exe()?;
- cmd.env("RUSTC_WRAPPER", myself);
- cmd.env("RA_RUSTC_WRAPPER", "1");
+ ) -> io::Result<Vec<WorkspaceBuildScripts>> {
+ assert_eq!(config.invocation_strategy, InvocationStrategy::Once);
+
+ let current_dir = match &config.invocation_location {
+ InvocationLocation::Root(root) => root,
+ InvocationLocation::Workspace => {
+ return Err(io::Error::new(
+ io::ErrorKind::Other,
+ "Cannot run build scripts from workspace with invocation strategy `once`",
+ ))
+ }
+ };
+ let cmd = Self::build_command(config)?;
+ // NB: Cargo.toml could have been modified between `cargo metadata` and
+ // `cargo check`. We shouldn't assume that package ids we see here are
+ // exactly those from `config`.
+ let mut by_id = FxHashMap::default();
+ // some workspaces might depend on the same crates, so we need to duplicate the outputs
+ // to those collisions
+ let mut collisions = Vec::new();
+ let mut res: Vec<_> = workspaces
+ .iter()
+ .enumerate()
+ .map(|(idx, workspace)| {
+ let mut res = WorkspaceBuildScripts::default();
+ for package in workspace.packages() {
+ res.outputs.insert(package, BuildScriptOutput::default());
+ if by_id.contains_key(&workspace[package].id) {
+ collisions.push((&workspace[package].id, idx, package));
+ } else {
+ by_id.insert(workspace[package].id.clone(), (package, idx));
+ }
+ }
+ res
+ })
+ .collect();
+
+ let errors = Self::run_command(
+ cmd,
+ current_dir.as_path().as_ref(),
+ |package, cb| {
+ if let Some(&(package, workspace)) = by_id.get(package) {
+ cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]);
+ }
+ },
+ progress,
+ )?;
+ res.iter_mut().for_each(|it| it.error = errors.clone());
+ collisions.into_iter().for_each(|(id, workspace, package)| {
+ if let Some(&(p, w)) = by_id.get(id) {
+ res[workspace].outputs[package] = res[w].outputs[p].clone();
+ }
+ });
+
+ if tracing::enabled!(tracing::Level::INFO) {
+ for (idx, workspace) in workspaces.iter().enumerate() {
+ for package in workspace.packages() {
+ let package_build_data = &mut res[idx].outputs[package];
+ if !package_build_data.is_unchanged() {
+ tracing::info!(
+ "{}: {:?}",
+ workspace[package].manifest.parent().display(),
+ package_build_data,
+ );
+ }
+ }
+ }
}
- cmd.current_dir(workspace.workspace_root());
+ Ok(res)
+ }
+ fn run_per_ws(
+ cmd: Command,
+ workspace: &CargoWorkspace,
+ current_dir: &path::Path,
+ progress: &dyn Fn(String),
+ ) -> io::Result<WorkspaceBuildScripts> {
let mut res = WorkspaceBuildScripts::default();
let outputs = &mut res.outputs;
// NB: Cargo.toml could have been modified between `cargo metadata` and
@@ -122,10 +233,46 @@ impl WorkspaceBuildScripts {
// exactly those from `config`.
let mut by_id: FxHashMap<String, Package> = FxHashMap::default();
for package in workspace.packages() {
- outputs.insert(package, None);
+ outputs.insert(package, BuildScriptOutput::default());
by_id.insert(workspace[package].id.clone(), package);
}
+ res.error = Self::run_command(
+ cmd,
+ current_dir,
+ |package, cb| {
+ if let Some(&package) = by_id.get(package) {
+ cb(&workspace[package].name, &mut outputs[package]);
+ }
+ },
+ progress,
+ )?;
+
+ if tracing::enabled!(tracing::Level::INFO) {
+ for package in workspace.packages() {
+ let package_build_data = &mut outputs[package];
+ if !package_build_data.is_unchanged() {
+ tracing::info!(
+ "{}: {:?}",
+ workspace[package].manifest.parent().display(),
+ package_build_data,
+ );
+ }
+ }
+ }
+
+ Ok(res)
+ }
+
+ fn run_command(
+ mut cmd: Command,
+ current_dir: &path::Path,
+ // ideally this would be something like:
+ // with_output_for: impl FnMut(&str, dyn FnOnce(&mut BuildScriptOutput)),
+ // but owned trait objects aren't a thing
+ mut with_output_for: impl FnMut(&str, &mut dyn FnMut(&str, &mut BuildScriptOutput)),
+ progress: &dyn Fn(String),
+ ) -> io::Result<Option<String>> {
let errors = RefCell::new(String::new());
let push_err = |err: &str| {
let mut e = errors.borrow_mut();
@@ -133,7 +280,8 @@ impl WorkspaceBuildScripts {
e.push('\n');
};
- tracing::info!("Running build scripts: {:?}", cmd);
+ tracing::info!("Running build scripts in {}: {:?}", current_dir.display(), cmd);
+ cmd.current_dir(current_dir);
let output = stdx::process::spawn_with_streaming_output(
cmd,
&mut |line| {
@@ -145,59 +293,58 @@ impl WorkspaceBuildScripts {
.unwrap_or_else(|_| Message::TextLine(line.to_string()));
match message {
- Message::BuildScriptExecuted(message) => {
- let package = match by_id.get(&message.package_id.repr) {
- Some(&it) => it,
- None => return,
- };
- let cfgs = {
- let mut acc = Vec::new();
- for cfg in message.cfgs {
- match cfg.parse::<CfgFlag>() {
- Ok(it) => acc.push(it),
- Err(err) => {
- push_err(&format!(
- "invalid cfg from cargo-metadata: {}",
- err
- ));
- return;
- }
- };
+ Message::BuildScriptExecuted(mut message) => {
+ with_output_for(&message.package_id.repr, &mut |name, data| {
+ progress(format!("running build-script: {}", name));
+ let cfgs = {
+ let mut acc = Vec::new();
+ for cfg in &message.cfgs {
+ match cfg.parse::<CfgFlag>() {
+ Ok(it) => acc.push(it),
+ Err(err) => {
+ push_err(&format!(
+ "invalid cfg from cargo-metadata: {}",
+ err
+ ));
+ return;
+ }
+ };
+ }
+ acc
+ };
+ if !message.env.is_empty() {
+ data.envs = mem::take(&mut message.env);
}
- acc
- };
- // cargo_metadata crate returns default (empty) path for
- // older cargos, which is not absolute, so work around that.
- let out_dir = message.out_dir.into_os_string();
- if !out_dir.is_empty() {
- let data = outputs[package].get_or_insert_with(Default::default);
- data.out_dir = Some(AbsPathBuf::assert(PathBuf::from(out_dir)));
- data.cfgs = cfgs;
- }
- if !message.env.is_empty() {
- outputs[package].get_or_insert_with(Default::default).envs =
- message.env;
- }
+ // cargo_metadata crate returns default (empty) path for
+ // older cargos, which is not absolute, so work around that.
+ let out_dir = mem::take(&mut message.out_dir).into_os_string();
+ if !out_dir.is_empty() {
+ let out_dir = AbsPathBuf::assert(PathBuf::from(out_dir));
+ // inject_cargo_env(package, package_build_data);
+ // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
+ if let Some(out_dir) =
+ out_dir.as_os_str().to_str().map(|s| s.to_owned())
+ {
+ data.envs.push(("OUT_DIR".to_string(), out_dir));
+ }
+ data.out_dir = Some(out_dir);
+ data.cfgs = cfgs;
+ }
+ });
}
Message::CompilerArtifact(message) => {
- let package = match by_id.get(&message.package_id.repr) {
- Some(it) => *it,
- None => return,
- };
-
- progress(format!("metadata {}", message.target.name));
-
- if message.target.kind.iter().any(|k| k == "proc-macro") {
- // Skip rmeta file
- if let Some(filename) =
- message.filenames.iter().find(|name| is_dylib(name))
- {
- let filename = AbsPathBuf::assert(PathBuf::from(&filename));
- outputs[package]
- .get_or_insert_with(Default::default)
- .proc_macro_dylib_path = Some(filename);
+ with_output_for(&message.package_id.repr, &mut |name, data| {
+ progress(format!("building proc-macros: {}", name));
+ if message.target.kind.iter().any(|k| k == "proc-macro") {
+ // Skip rmeta file
+ if let Some(filename) =
+ message.filenames.iter().find(|name| is_dylib(name))
+ {
+ let filename = AbsPathBuf::assert(PathBuf::from(&filename));
+ data.proc_macro_dylib_path = Some(filename);
+ }
}
- }
+ });
}
Message::CompilerMessage(message) => {
progress(message.target.name);
@@ -216,32 +363,13 @@ impl WorkspaceBuildScripts {
},
)?;
- for package in workspace.packages() {
- if let Some(package_build_data) = &mut outputs[package] {
- tracing::info!(
- "{}: {:?}",
- workspace[package].manifest.parent().display(),
- package_build_data,
- );
- // inject_cargo_env(package, package_build_data);
- if let Some(out_dir) = &package_build_data.out_dir {
- // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
- if let Some(out_dir) = out_dir.as_os_str().to_str().map(|s| s.to_owned()) {
- package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
- }
- }
- }
- }
-
- let mut errors = errors.into_inner();
- if !output.status.success() {
- if errors.is_empty() {
- errors = "cargo check failed".to_string();
- }
- res.error = Some(errors);
- }
-
- Ok(res)
+ let errors = if !output.status.success() {
+ let errors = errors.into_inner();
+ Some(if errors.is_empty() { "cargo check failed".to_string() } else { errors })
+ } else {
+ None
+ };
+ Ok(errors)
}
pub fn error(&self) -> Option<&str> {
@@ -249,11 +377,11 @@ impl WorkspaceBuildScripts {
}
pub(crate) fn get_output(&self, idx: Package) -> Option<&BuildScriptOutput> {
- self.outputs.get(idx)?.as_ref()
+ self.outputs.get(idx)
}
}
-// FIXME: File a better way to know if it is a dylib.
+// FIXME: Find a better way to know if it is a dylib.
fn is_dylib(path: &Utf8Path) -> bool {
match path.extension().map(|e| e.to_string().to_lowercase()) {
None => false,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index eed955b42..b4c2ba436 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -2,6 +2,7 @@
use std::iter;
use std::path::PathBuf;
+use std::str::from_utf8;
use std::{ops, process::Command};
use anyhow::{Context, Result};
@@ -13,8 +14,8 @@ use rustc_hash::FxHashMap;
use serde::Deserialize;
use serde_json::from_value;
-use crate::CfgOverrides;
-use crate::{utf8_stdout, ManifestPath};
+use crate::{utf8_stdout, InvocationLocation, ManifestPath};
+use crate::{CfgOverrides, InvocationStrategy};
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
@@ -70,34 +71,43 @@ impl Default for UnsetTestCrates {
}
}
-#[derive(Default, Clone, Debug, PartialEq, Eq)]
-pub struct CargoConfig {
- /// Do not activate the `default` feature.
- pub no_default_features: bool,
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum CargoFeatures {
+ All,
+ Selected {
+ /// List of features to activate.
+ features: Vec<String>,
+ /// Do not activate the `default` feature.
+ no_default_features: bool,
+ },
+}
- /// Activate all available features
- pub all_features: bool,
+impl Default for CargoFeatures {
+ fn default() -> Self {
+ CargoFeatures::Selected { features: vec![], no_default_features: false }
+ }
+}
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
+pub struct CargoConfig {
/// List of features to activate.
- /// This will be ignored if `cargo_all_features` is true.
- pub features: Vec<String>,
-
+ pub features: CargoFeatures,
/// rustc target
pub target: Option<String>,
-
- /// Don't load sysroot crates (`std`, `core` & friends). Might be useful
- /// when debugging isolated issues.
- pub no_sysroot: bool,
-
+ /// Sysroot loading behavior
+ pub sysroot: Option<RustcSource>,
/// rustc private crate source
pub rustc_source: Option<RustcSource>,
-
/// crates to disable `#[cfg(test)]` on
pub unset_test_crates: UnsetTestCrates,
-
+ /// Invoke `cargo check` through the RUSTC_WRAPPER.
pub wrap_rustc_in_build_scripts: bool,
-
+ /// The command to run instead of `cargo check` for building build scripts.
pub run_build_script_command: Option<Vec<String>>,
+ /// Extra env vars to set when invoking the cargo command
+ pub extra_env: FxHashMap<String, String>,
+ pub invocation_strategy: InvocationStrategy,
+ pub invocation_location: InvocationLocation,
}
impl CargoConfig {
@@ -140,7 +150,7 @@ pub struct PackageData {
pub targets: Vec<Target>,
/// Does this package come from the local filesystem (and is editable)?
pub is_local: bool,
- // Whether this package is a member of the workspace
+ /// Whether this package is a member of the workspace
pub is_member: bool,
/// List of packages this package depends on
pub dependencies: Vec<PackageDependency>,
@@ -246,8 +256,8 @@ impl TargetKind {
}
}
+// Deserialize helper for the cargo metadata
#[derive(Deserialize, Default)]
-// Deserialise helper for the cargo metadata
struct PackageMetadata {
#[serde(rename = "rust-analyzer")]
rust_analyzer: Option<RustAnalyzerPackageMetaData>,
@@ -263,22 +273,23 @@ impl CargoWorkspace {
let target = config
.target
.clone()
- .or_else(|| cargo_config_build_target(cargo_toml))
- .or_else(|| rustc_discover_host_triple(cargo_toml));
+ .or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env))
+ .or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env));
let mut meta = MetadataCommand::new();
meta.cargo_path(toolchain::cargo());
meta.manifest_path(cargo_toml.to_path_buf());
- if config.all_features {
- meta.features(CargoOpt::AllFeatures);
- } else {
- if config.no_default_features {
- // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
- // https://github.com/oli-obk/cargo_metadata/issues/79
- meta.features(CargoOpt::NoDefaultFeatures);
+ match &config.features {
+ CargoFeatures::All => {
+ meta.features(CargoOpt::AllFeatures);
}
- if !config.features.is_empty() {
- meta.features(CargoOpt::SomeFeatures(config.features.clone()));
+ CargoFeatures::Selected { features, no_default_features } => {
+ if *no_default_features {
+ meta.features(CargoOpt::NoDefaultFeatures);
+ }
+ if !features.is_empty() {
+ meta.features(CargoOpt::SomeFeatures(features.clone()));
+ }
}
}
meta.current_dir(current_dir.as_os_str());
@@ -292,10 +303,22 @@ impl CargoWorkspace {
// unclear whether cargo itself supports it.
progress("metadata".to_string());
- let meta =
- meta.exec().with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))?;
-
- Ok(meta)
+ (|| -> Result<cargo_metadata::Metadata, cargo_metadata::Error> {
+ let mut command = meta.cargo_command();
+ command.envs(&config.extra_env);
+ let output = command.output()?;
+ if !output.status.success() {
+ return Err(cargo_metadata::Error::CargoMetadata {
+ stderr: String::from_utf8(output.stderr)?,
+ });
+ }
+ let stdout = from_utf8(&output.stdout)?
+ .lines()
+ .find(|line| line.starts_with('{'))
+ .ok_or(cargo_metadata::Error::NoJson)?;
+ cargo_metadata::MetadataCommand::parse(stdout)
+ })()
+ .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
}
pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
@@ -306,18 +329,21 @@ impl CargoWorkspace {
let ws_members = &meta.workspace_members;
meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
- for meta_pkg in &meta.packages {
+ for meta_pkg in meta.packages {
let cargo_metadata::Package {
- id,
- edition,
name,
- manifest_path,
version,
- metadata,
+ id,
+ source,
+ targets: meta_targets,
+ features,
+ manifest_path,
repository,
+ edition,
+ metadata,
..
} = meta_pkg;
- let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default();
+ let meta = from_value::<PackageMetadata>(metadata).unwrap_or_default();
let edition = match edition {
cargo_metadata::Edition::E2015 => Edition::Edition2015,
cargo_metadata::Edition::E2018 => Edition::Edition2018,
@@ -329,67 +355,50 @@ impl CargoWorkspace {
};
// We treat packages without source as "local" packages. That includes all members of
// the current workspace, as well as any path dependency outside the workspace.
- let is_local = meta_pkg.source.is_none();
- let is_member = ws_members.contains(id);
+ let is_local = source.is_none();
+ let is_member = ws_members.contains(&id);
let pkg = packages.alloc(PackageData {
id: id.repr.clone(),
- name: name.clone(),
- version: version.clone(),
- manifest: AbsPathBuf::assert(PathBuf::from(&manifest_path)).try_into().unwrap(),
+ name,
+ version,
+ manifest: AbsPathBuf::assert(manifest_path.into()).try_into().unwrap(),
targets: Vec::new(),
is_local,
is_member,
edition,
- repository: repository.clone(),
+ repository,
dependencies: Vec::new(),
- features: meta_pkg.features.clone().into_iter().collect(),
+ features: features.into_iter().collect(),
active_features: Vec::new(),
metadata: meta.rust_analyzer.unwrap_or_default(),
});
let pkg_data = &mut packages[pkg];
pkg_by_id.insert(id, pkg);
- for meta_tgt in &meta_pkg.targets {
- let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
+ for meta_tgt in meta_targets {
+ let cargo_metadata::Target { name, kind, required_features, src_path, .. } =
+ meta_tgt;
let tgt = targets.alloc(TargetData {
package: pkg,
- name: meta_tgt.name.clone(),
- root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
- kind: TargetKind::new(meta_tgt.kind.as_slice()),
- is_proc_macro,
- required_features: meta_tgt.required_features.clone(),
+ name,
+ root: AbsPathBuf::assert(src_path.into()),
+ kind: TargetKind::new(&kind),
+ is_proc_macro: &*kind == ["proc-macro"],
+ required_features,
});
pkg_data.targets.push(tgt);
}
}
let resolve = meta.resolve.expect("metadata executed with deps");
for mut node in resolve.nodes {
- let source = match pkg_by_id.get(&node.id) {
- Some(&src) => src,
- // FIXME: replace this and a similar branch below with `.unwrap`, once
- // https://github.com/rust-lang/cargo/issues/7841
- // is fixed and hits stable (around 1.43-is probably?).
- None => {
- tracing::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
- continue;
- }
- };
+ let &source = pkg_by_id.get(&node.id).unwrap();
node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
- for (dep_node, kind) in node
+ let dependencies = node
.deps
.iter()
- .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)))
- {
- let pkg = match pkg_by_id.get(&dep_node.pkg) {
- Some(&pkg) => pkg,
- None => {
- tracing::error!(
- "Dep node id do not match in cargo metadata, ignoring {}",
- dep_node.pkg
- );
- continue;
- }
- };
+ .flat_map(|dep| DepKind::iter(&dep.dep_kinds).map(move |kind| (dep, kind)));
+ for (dep_node, kind) in dependencies {
+ let &pkg = pkg_by_id.get(&dep_node.pkg).unwrap();
let dep = PackageDependency { name: dep_node.name.clone(), pkg, kind };
packages[source].dependencies.push(dep);
}
@@ -434,10 +443,7 @@ impl CargoWorkspace {
found = true
}
self[pkg].dependencies.iter().find_map(|dep| {
- if &self[dep.pkg].manifest == manifest_path {
- return Some(self[pkg].manifest.clone());
- }
- None
+ (&self[dep.pkg].manifest == manifest_path).then(|| self[pkg].manifest.clone())
})
})
.collect::<Vec<ManifestPath>>();
@@ -463,8 +469,12 @@ impl CargoWorkspace {
}
}
-fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
+fn rustc_discover_host_triple(
+ cargo_toml: &ManifestPath,
+ extra_env: &FxHashMap<String, String>,
+) -> Option<String> {
let mut rustc = Command::new(toolchain::rustc());
+ rustc.envs(extra_env);
rustc.current_dir(cargo_toml.parent()).arg("-vV");
tracing::debug!("Discovering host platform by {:?}", rustc);
match utf8_stdout(rustc) {
@@ -486,8 +496,12 @@ fn rustc_discover_host_triple(cargo_toml: &ManifestPath) -> Option<String> {
}
}
-fn cargo_config_build_target(cargo_toml: &ManifestPath) -> Option<String> {
+fn cargo_config_build_target(
+ cargo_toml: &ManifestPath,
+ extra_env: &FxHashMap<String, String>,
+) -> Option<String> {
let mut cargo_config = Command::new(toolchain::cargo());
+ cargo_config.envs(extra_env);
cargo_config
.current_dir(cargo_toml.parent())
.args(&["-Z", "unstable-options", "config", "get", "build.target"])
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index b81b7432f..575581fa5 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -42,8 +42,8 @@ use rustc_hash::FxHashSet;
pub use crate::{
build_scripts::WorkspaceBuildScripts,
cargo_workspace::{
- CargoConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustcSource, Target,
- TargetData, TargetKind, UnsetTestCrates,
+ CargoConfig, CargoFeatures, CargoWorkspace, Package, PackageData, PackageDependency,
+ RustcSource, Target, TargetData, TargetKind, UnsetTestCrates,
},
manifest_path::ManifestPath,
project_json::{ProjectJson, ProjectJsonData},
@@ -67,7 +67,7 @@ impl ProjectManifest {
if path.file_name().unwrap_or_default() == "Cargo.toml" {
return Ok(ProjectManifest::CargoToml(path));
}
- bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
+ bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display());
}
pub fn discover_single(path: &AbsPath) -> Result<ProjectManifest> {
@@ -78,7 +78,7 @@ impl ProjectManifest {
};
if !candidates.is_empty() {
- bail!("more than one project")
+ bail!("more than one project");
}
Ok(res)
}
@@ -157,3 +157,17 @@ fn utf8_stdout(mut cmd: Command) -> Result<String> {
let stdout = String::from_utf8(output.stdout)?;
Ok(stdout.trim().to_string())
}
+
+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
+pub enum InvocationStrategy {
+ Once,
+ #[default]
+ PerWorkspace,
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub enum InvocationLocation {
+ Root(AbsPathBuf),
+ #[default]
+ Workspace,
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
index 63d1d0ace..5133a14d5 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs
@@ -110,14 +110,17 @@ impl ProjectJson {
.collect::<Vec<_>>(),
}
}
+
/// Returns the number of crates in the project.
pub fn n_crates(&self) -> usize {
self.crates.len()
}
+
/// Returns an iterator over the crates in the project.
pub fn crates(&self) -> impl Iterator<Item = (CrateId, &Crate)> + '_ {
self.crates.iter().enumerate().map(|(idx, krate)| (CrateId(idx as u32), krate))
}
+
/// Returns the path to the project's root folder.
pub fn path(&self) -> &AbsPath {
&self.project_root
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 17e244d06..323136183 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
@@ -3,10 +3,15 @@
use std::process::Command;
use anyhow::Result;
+use rustc_hash::FxHashMap;
use crate::{cfg_flag::CfgFlag, utf8_stdout, ManifestPath};
-pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Vec<CfgFlag> {
+pub(crate) fn get(
+ cargo_toml: Option<&ManifestPath>,
+ target: Option<&str>,
+ extra_env: &FxHashMap<String, String>,
+) -> Vec<CfgFlag> {
let _p = profile::span("rustc_cfg::get");
let mut res = Vec::with_capacity(6 * 2 + 1);
@@ -18,7 +23,7 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
}
}
- match get_rust_cfgs(cargo_toml, target) {
+ match get_rust_cfgs(cargo_toml, target, extra_env) {
Ok(rustc_cfgs) => {
tracing::debug!(
"rustc cfgs found: {:?}",
@@ -35,9 +40,14 @@ pub(crate) fn get(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Ve
res
}
-fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Result<String> {
+fn get_rust_cfgs(
+ cargo_toml: Option<&ManifestPath>,
+ target: Option<&str>,
+ extra_env: &FxHashMap<String, String>,
+) -> 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(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
@@ -52,6 +62,7 @@ fn get_rust_cfgs(cargo_toml: Option<&ManifestPath>, target: Option<&str>) -> Res
}
// using unstable cargo features failed, fall back to using plain rustc
let mut cmd = Command::new(toolchain::rustc());
+ cmd.envs(extra_env);
cmd.args(&["--print", "cfg", "-O"]);
if let Some(target) = target {
cmd.args(&["--target", target]);
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 362bb0f5e..fa8d76f3f 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -9,6 +9,7 @@ use std::{env, fs, iter, ops, path::PathBuf, process::Command};
use anyhow::{format_err, Result};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};
+use rustc_hash::FxHashMap;
use crate::{utf8_stdout, ManifestPath};
@@ -63,22 +64,38 @@ impl Sysroot {
self.by_name("proc_macro")
}
- pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
+ pub fn crates(&self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + '_ {
self.crates.iter().map(|(id, _data)| id)
}
+}
- pub fn discover(dir: &AbsPath) -> Result<Sysroot> {
- tracing::debug!("Discovering sysroot for {}", dir.display());
- let sysroot_dir = discover_sysroot_dir(dir)?;
- let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?;
+impl Sysroot {
+ /// Attempts to discover the toolchain's sysroot from the given `dir`.
+ pub fn discover(dir: &AbsPath, extra_env: &FxHashMap<String, String>) -> Result<Sysroot> {
+ tracing::debug!("discovering sysroot for {}", dir.display());
+ let sysroot_dir = discover_sysroot_dir(dir, extra_env)?;
+ let sysroot_src_dir =
+ discover_sysroot_src_dir_or_add_component(&sysroot_dir, dir, extra_env)?;
let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
Ok(res)
}
- pub fn discover_rustc(cargo_toml: &ManifestPath) -> Option<ManifestPath> {
- tracing::debug!("Discovering rustc source for {}", cargo_toml.display());
+ pub fn discover_rustc(
+ cargo_toml: &ManifestPath,
+ extra_env: &FxHashMap<String, String>,
+ ) -> Option<ManifestPath> {
+ tracing::debug!("discovering rustc source for {}", cargo_toml.display());
let current_dir = cargo_toml.parent();
- discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
+ let sysroot_dir = discover_sysroot_dir(current_dir, extra_env).ok()?;
+ get_rustc_src(&sysroot_dir)
+ }
+
+ pub fn with_sysroot_dir(sysroot_dir: AbsPathBuf) -> Result<Sysroot> {
+ let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir).ok_or_else(|| {
+ format_err!("can't load standard library from sysroot {}", sysroot_dir.display())
+ })?;
+ let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?;
+ Ok(res)
}
pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result<Sysroot> {
@@ -144,33 +161,46 @@ impl Sysroot {
}
}
-fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
+fn discover_sysroot_dir(
+ current_dir: &AbsPath,
+ extra_env: &FxHashMap<String, String>,
+) -> Result<AbsPathBuf> {
let mut rustc = Command::new(toolchain::rustc());
+ rustc.envs(extra_env);
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
tracing::debug!("Discovering sysroot by {:?}", rustc);
let stdout = utf8_stdout(rustc)?;
Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
}
-fn discover_sysroot_src_dir(
- sysroot_path: &AbsPathBuf,
- current_dir: &AbsPath,
-) -> Result<AbsPathBuf> {
+fn discover_sysroot_src_dir(sysroot_path: &AbsPathBuf) -> Option<AbsPathBuf> {
if let Ok(path) = env::var("RUST_SRC_PATH") {
- let path = AbsPathBuf::try_from(path.as_str())
- .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
- let core = path.join("core");
- if fs::metadata(&core).is_ok() {
- tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
- return Ok(path);
+ if let Ok(path) = AbsPathBuf::try_from(path.as_str()) {
+ let core = path.join("core");
+ if fs::metadata(&core).is_ok() {
+ tracing::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
+ return Some(path);
+ }
+ tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
+ } else {
+ tracing::debug!("RUST_SRC_PATH is set, but is invalid, ignoring");
}
- tracing::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
}
get_rust_src(sysroot_path)
+}
+
+fn discover_sysroot_src_dir_or_add_component(
+ sysroot_path: &AbsPathBuf,
+ current_dir: &AbsPath,
+ extra_env: &FxHashMap<String, String>,
+) -> Result<AbsPathBuf> {
+ discover_sysroot_src_dir(sysroot_path)
.or_else(|| {
let mut rustup = Command::new(toolchain::rustup());
+ rustup.envs(extra_env);
rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
+ tracing::info!("adding rust-src component by {:?}", rustup);
utf8_stdout(rustup).ok()?;
get_rust_src(sysroot_path)
})
@@ -189,7 +219,7 @@ try installing the Rust source the same way you installed rustc",
fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> {
let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
let rustc_src = ManifestPath::try_from(rustc_src).ok()?;
- tracing::debug!("Checking for rustc source code: {}", rustc_src.display());
+ tracing::debug!("checking for rustc source code: {}", rustc_src.display());
if fs::metadata(&rustc_src).is_ok() {
Some(rustc_src)
} else {
@@ -199,7 +229,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> {
fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
let rust_src = sysroot_path.join("lib/rustlib/src/rust/library");
- tracing::debug!("Checking sysroot: {}", rust_src.display());
+ tracing::debug!("checking sysroot library: {}", rust_src.display());
if fs::metadata(&rust_src).is_ok() {
Some(rust_src)
} else {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 9ccb6e910..e2444e249 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -92,13 +92,17 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
}
fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
- project_workspace.to_crate_graph(&mut |_, _| Ok(Vec::new()), &mut {
- let mut counter = 0;
- move |_path| {
- counter += 1;
- Some(FileId(counter))
- }
- })
+ project_workspace.to_crate_graph(
+ &mut |_, _| Ok(Vec::new()),
+ &mut {
+ let mut counter = 0;
+ move |_path| {
+ counter += 1;
+ Some(FileId(counter))
+ }
+ },
+ &Default::default(),
+ )
}
fn check_crate_graph(crate_graph: CrateGraph, expect: Expect) {
@@ -181,6 +185,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -256,6 +263,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -331,6 +341,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -406,6 +419,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -473,6 +489,9 @@ fn cargo_hello_world_project_model_with_wildcard_overrides() {
repo: Some(
"https://github.com/rust-lang/libc",
),
+ name: Some(
+ "libc",
+ ),
},
is_proc_macro: false,
},
@@ -563,6 +582,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -640,6 +662,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -717,6 +742,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -794,6 +822,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -861,6 +892,9 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
repo: Some(
"https://github.com/rust-lang/libc",
),
+ name: Some(
+ "libc",
+ ),
},
is_proc_macro: false,
},
@@ -942,6 +976,9 @@ fn cargo_hello_world_project_model() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -1019,6 +1056,9 @@ fn cargo_hello_world_project_model() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -1096,6 +1136,9 @@ fn cargo_hello_world_project_model() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -1173,6 +1216,9 @@ fn cargo_hello_world_project_model() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello-world",
+ ),
},
is_proc_macro: false,
},
@@ -1240,6 +1286,9 @@ fn cargo_hello_world_project_model() {
repo: Some(
"https://github.com/rust-lang/libc",
),
+ name: Some(
+ "libc",
+ ),
},
is_proc_macro: false,
},
@@ -1800,6 +1849,9 @@ fn rust_project_hello_world_project_model() {
),
origin: CratesIo {
repo: None,
+ name: Some(
+ "hello_world",
+ ),
},
is_proc_macro: false,
},
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 818bbed6a..2780c62ed 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};
+use std::{collections::VecDeque, fmt, fs, process::Command, sync::Arc};
use anyhow::{format_err, Context, Result};
use base_db::{
@@ -21,8 +21,8 @@ use crate::{
cfg_flag::CfgFlag,
rustc_cfg,
sysroot::SysrootCrate,
- utf8_stdout, CargoConfig, CargoWorkspace, ManifestPath, ProjectJson, ProjectManifest, Sysroot,
- TargetKind, WorkspaceBuildScripts,
+ utf8_stdout, CargoConfig, CargoWorkspace, InvocationStrategy, ManifestPath, Package,
+ ProjectJson, ProjectManifest, Sysroot, TargetKind, WorkspaceBuildScripts,
};
/// A set of cfg-overrides per crate.
@@ -156,11 +156,16 @@ impl ProjectWorkspace {
})?;
let project_location = project_json.parent().to_path_buf();
let project_json = ProjectJson::new(&project_location, data);
- ProjectWorkspace::load_inline(project_json, config.target.as_deref())?
+ ProjectWorkspace::load_inline(
+ project_json,
+ config.target.as_deref(),
+ &config.extra_env,
+ )?
}
ProjectManifest::CargoToml(cargo_toml) => {
let cargo_version = utf8_stdout({
let mut cmd = Command::new(toolchain::cargo());
+ cmd.envs(&config.extra_env);
cmd.arg("--version");
cmd
})?;
@@ -183,22 +188,41 @@ impl ProjectWorkspace {
})?;
let cargo = CargoWorkspace::new(meta);
- let sysroot = if config.no_sysroot {
- None
- } else {
- Some(Sysroot::discover(cargo_toml.parent()).with_context(|| {
- format!(
+ let sysroot = match &config.sysroot {
+ Some(RustcSource::Path(path)) => {
+ Some(Sysroot::with_sysroot_dir(path.clone()).with_context(|| {
+ format!(
+ "Failed to find sysroot for Cargo.toml file {}.",
+ cargo_toml.display()
+ )
+ })?)
+ }
+ Some(RustcSource::Discover) => Some(
+ Sysroot::discover(cargo_toml.parent(), &config.extra_env).with_context(
+ || {
+ format!(
"Failed to find sysroot for Cargo.toml file {}. Is rust-src installed?",
cargo_toml.display()
)
- })?)
+ },
+ )?,
+ ),
+ None => None,
};
+ if let Some(sysroot) = &sysroot {
+ tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
+ }
let rustc_dir = match &config.rustc_source {
Some(RustcSource::Path(path)) => ManifestPath::try_from(path.clone()).ok(),
- Some(RustcSource::Discover) => Sysroot::discover_rustc(&cargo_toml),
+ Some(RustcSource::Discover) => {
+ Sysroot::discover_rustc(&cargo_toml, &config.extra_env)
+ }
None => None,
};
+ if let Some(rustc_dir) = &rustc_dir {
+ tracing::info!(rustc_dir = %rustc_dir.display(), "Using rustc source");
+ }
let rustc = match rustc_dir {
Some(rustc_dir) => Some({
@@ -216,7 +240,8 @@ impl ProjectWorkspace {
None => None,
};
- let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
+ let rustc_cfg =
+ rustc_cfg::get(Some(&cargo_toml), config.target.as_deref(), &config.extra_env);
let cfg_overrides = config.cfg_overrides();
ProjectWorkspace::Cargo {
@@ -237,6 +262,7 @@ impl ProjectWorkspace {
pub fn load_inline(
project_json: ProjectJson,
target: Option<&str>,
+ extra_env: &FxHashMap<String, String>,
) -> Result<ProjectWorkspace> {
let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) {
(Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?),
@@ -257,8 +283,11 @@ impl ProjectWorkspace {
}
(None, None) => None,
};
+ if let Some(sysroot) = &sysroot {
+ tracing::info!(src_root = %sysroot.src_root().display(), root = %sysroot.root().display(), "Using sysroot");
+ }
- let rustc_cfg = rustc_cfg::get(None, target);
+ let rustc_cfg = rustc_cfg::get(None, target, extra_env);
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
}
@@ -268,11 +297,13 @@ impl ProjectWorkspace {
.first()
.and_then(|it| it.parent())
.ok_or_else(|| format_err!("No detached files to load"))?,
+ &Default::default(),
)?;
- let rustc_cfg = rustc_cfg::get(None, None);
+ let rustc_cfg = rustc_cfg::get(None, None, &Default::default());
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
}
+ /// Runs the build scripts for this [`ProjectWorkspace`].
pub fn run_build_scripts(
&self,
config: &CargoConfig,
@@ -280,9 +311,13 @@ impl ProjectWorkspace {
) -> Result<WorkspaceBuildScripts> {
match self {
ProjectWorkspace::Cargo { cargo, toolchain, .. } => {
- WorkspaceBuildScripts::run(config, cargo, progress, toolchain).with_context(|| {
- format!("Failed to run build scripts for {}", &cargo.workspace_root().display())
- })
+ WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, toolchain)
+ .with_context(|| {
+ format!(
+ "Failed to run build scripts for {}",
+ &cargo.workspace_root().display()
+ )
+ })
}
ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
Ok(WorkspaceBuildScripts::default())
@@ -290,6 +325,49 @@ impl ProjectWorkspace {
}
}
+ /// Runs the build scripts for the given [`ProjectWorkspace`]s. Depending on the invocation
+ /// strategy this may run a single build process for all project workspaces.
+ pub fn run_all_build_scripts(
+ workspaces: &[ProjectWorkspace],
+ config: &CargoConfig,
+ progress: &dyn Fn(String),
+ ) -> Vec<Result<WorkspaceBuildScripts>> {
+ if matches!(config.invocation_strategy, InvocationStrategy::PerWorkspace)
+ || config.run_build_script_command.is_none()
+ {
+ return workspaces.iter().map(|it| it.run_build_scripts(config, progress)).collect();
+ }
+
+ let cargo_ws: Vec<_> = workspaces
+ .iter()
+ .filter_map(|it| match it {
+ ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
+ _ => None,
+ })
+ .collect();
+ let ref mut outputs = match WorkspaceBuildScripts::run_once(config, &cargo_ws, progress) {
+ Ok(it) => Ok(it.into_iter()),
+ // io::Error is not Clone?
+ Err(e) => Err(Arc::new(e)),
+ };
+
+ workspaces
+ .iter()
+ .map(|it| match it {
+ ProjectWorkspace::Cargo { cargo, .. } => match outputs {
+ Ok(outputs) => Ok(outputs.next().unwrap()),
+ Err(e) => Err(e.clone()).with_context(|| {
+ format!(
+ "Failed to run build scripts for {}",
+ &cargo.workspace_root().display()
+ )
+ }),
+ },
+ _ => Ok(WorkspaceBuildScripts::default()),
+ })
+ .collect()
+ }
+
pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
match self {
ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
@@ -303,6 +381,13 @@ impl ProjectWorkspace {
/// The return type contains the path and whether or not
/// the root is a member of the current workspace
pub fn to_roots(&self) -> Vec<PackageRoot> {
+ let mk_sysroot = |sysroot: Option<&Sysroot>| {
+ sysroot.map(|sysroot| PackageRoot {
+ is_local: false,
+ include: vec![sysroot.src_root().to_path_buf()],
+ exclude: Vec::new(),
+ })
+ };
match self {
ProjectWorkspace::Json { project, sysroot, rustc_cfg: _ } => project
.crates()
@@ -313,13 +398,7 @@ impl ProjectWorkspace {
})
.collect::<FxHashSet<_>>()
.into_iter()
- .chain(sysroot.as_ref().into_iter().flat_map(|sysroot| {
- sysroot.crates().map(move |krate| PackageRoot {
- is_local: false,
- include: vec![sysroot[krate].root.parent().to_path_buf()],
- exclude: Vec::new(),
- })
- }))
+ .chain(mk_sysroot(sysroot.as_ref()))
.collect::<Vec<_>>(),
ProjectWorkspace::Cargo {
cargo,
@@ -368,11 +447,7 @@ impl ProjectWorkspace {
}
PackageRoot { is_local, include, exclude }
})
- .chain(sysroot.iter().map(|sysroot| PackageRoot {
- is_local: false,
- include: vec![sysroot.src_root().to_path_buf()],
- exclude: Vec::new(),
- }))
+ .chain(mk_sysroot(sysroot.as_ref()))
.chain(rustc.iter().flat_map(|rustc| {
rustc.packages().map(move |krate| PackageRoot {
is_local: false,
@@ -389,11 +464,7 @@ impl ProjectWorkspace {
include: vec![detached_file.clone()],
exclude: Vec::new(),
})
- .chain(sysroot.crates().map(|krate| PackageRoot {
- is_local: false,
- include: vec![sysroot[krate].root.parent().to_path_buf()],
- exclude: Vec::new(),
- }))
+ .chain(mk_sysroot(Some(sysroot)))
.collect(),
}
}
@@ -416,6 +487,7 @@ impl ProjectWorkspace {
&self,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
+ extra_env: &FxHashMap<String, String>,
) -> CrateGraph {
let _p = profile::span("ProjectWorkspace::to_crate_graph");
@@ -426,6 +498,7 @@ impl ProjectWorkspace {
load,
project,
sysroot,
+ extra_env,
),
ProjectWorkspace::Cargo {
cargo,
@@ -464,6 +537,7 @@ fn project_json_to_crate_graph(
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson,
sysroot: &Option<Sysroot>,
+ extra_env: &FxHashMap<String, String>,
) -> CrateGraph {
let mut crate_graph = CrateGraph::default();
let sysroot_deps = sysroot
@@ -489,9 +563,9 @@ fn project_json_to_crate_graph(
};
let target_cfgs = match krate.target.as_deref() {
- Some(target) => {
- cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
- }
+ Some(target) => cfg_cache
+ .entry(target)
+ .or_insert_with(|| rustc_cfg::get(None, Some(target), extra_env)),
None => &rustc_cfg,
};
@@ -510,9 +584,15 @@ fn project_json_to_crate_graph(
proc_macro,
krate.is_proc_macro,
if krate.display_name.is_some() {
- CrateOrigin::CratesIo { repo: krate.repository.clone() }
+ CrateOrigin::CratesIo {
+ repo: krate.repository.clone(),
+ name: krate
+ .display_name
+ .clone()
+ .map(|n| n.canonical_name().to_string()),
+ }
} else {
- CrateOrigin::CratesIo { repo: None }
+ CrateOrigin::CratesIo { repo: None, name: None }
},
),
)
@@ -624,6 +704,8 @@ fn cargo_to_crate_graph(
lib_tgt = Some((crate_id, cargo[tgt].name.clone()));
pkg_to_lib_crate.insert(pkg, crate_id);
}
+ // Even crates that don't set proc-macro = true are allowed to depend on proc_macro
+ // (just none of the APIs work when called outside of a proc macro).
if let Some(proc_macro) = libproc_macro {
add_dep_with_prelude(
&mut crate_graph,
@@ -639,19 +721,19 @@ fn cargo_to_crate_graph(
}
// Set deps to the core, std and to the lib target of the current package
- for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
+ for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
// Add sysroot deps first so that a lib target named `core` etc. can overwrite them.
- public_deps.add(*from, &mut crate_graph);
+ public_deps.add(from, &mut crate_graph);
if let Some((to, name)) = lib_tgt.clone() {
- if to != *from && *kind != TargetKind::BuildScript {
+ if to != from && kind != TargetKind::BuildScript {
// (build script can not depend on its library target)
// For root projects with dashes in their name,
// cargo metadata does not do any normalization,
// so we do it ourselves currently
let name = CrateName::normalize_dashes(&name);
- add_dep(&mut crate_graph, *from, name, to);
+ add_dep(&mut crate_graph, from, name, to);
}
}
}
@@ -663,17 +745,17 @@ fn cargo_to_crate_graph(
for dep in cargo[pkg].dependencies.iter() {
let name = CrateName::new(&dep.name).unwrap();
if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
- for (from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
- if dep.kind == DepKind::Build && *kind != TargetKind::BuildScript {
+ for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() {
+ if dep.kind == DepKind::Build && kind != TargetKind::BuildScript {
// Only build scripts may depend on build dependencies.
continue;
}
- if dep.kind != DepKind::Build && *kind == TargetKind::BuildScript {
+ if dep.kind != DepKind::Build && kind == TargetKind::BuildScript {
// Build scripts may only depend on build dependencies.
continue;
}
- add_dep(&mut crate_graph, *from, name.clone(), to)
+ add_dep(&mut crate_graph, from, name.clone(), to)
}
}
}
@@ -684,9 +766,9 @@ fn cargo_to_crate_graph(
// and create dependencies on them for the crates which opt-in to that
if let Some(rustc_workspace) = rustc {
handle_rustc_crates(
+ &mut crate_graph,
rustc_workspace,
load,
- &mut crate_graph,
&cfg_options,
override_cfg,
load_proc_macro,
@@ -730,14 +812,17 @@ fn detached_files_to_crate_graph(
let detached_file_crate = crate_graph.add_crate_root(
file_id,
Edition::CURRENT,
- display_name,
+ display_name.clone(),
None,
cfg_options.clone(),
cfg_options.clone(),
Env::default(),
Ok(Vec::new()),
false,
- CrateOrigin::CratesIo { repo: None },
+ CrateOrigin::CratesIo {
+ repo: None,
+ name: display_name.map(|n| n.canonical_name().to_string()),
+ },
);
public_deps.add(detached_file_crate, &mut crate_graph);
@@ -746,16 +831,16 @@ fn detached_files_to_crate_graph(
}
fn handle_rustc_crates(
+ crate_graph: &mut CrateGraph,
rustc_workspace: &CargoWorkspace,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
- crate_graph: &mut CrateGraph,
cfg_options: &CfgOptions,
override_cfg: &CfgOverrides,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult,
- pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
+ pkg_to_lib_crate: &mut FxHashMap<Package, CrateId>,
public_deps: &SysrootPublicDeps,
cargo: &CargoWorkspace,
- pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<(CrateId, TargetKind)>>,
+ pkg_crates: &FxHashMap<Package, Vec<(CrateId, TargetKind)>>,
build_scripts: &WorkspaceBuildScripts,
) {
let mut rustc_pkg_crates = FxHashMap::default();
@@ -769,8 +854,8 @@ fn handle_rustc_crates(
let mut queue = VecDeque::new();
queue.push_back(root_pkg);
while let Some(pkg) = queue.pop_front() {
- // Don't duplicate packages if they are dependended on a diamond pattern
- // N.B. if this line is omitted, we try to analyse over 4_800_000 crates
+ // Don't duplicate packages if they are dependent on a diamond pattern
+ // N.B. if this line is omitted, we try to analyze over 4_800_000 crates
// which is not ideal
if rustc_pkg_crates.contains_key(&pkg) {
continue;
@@ -913,7 +998,7 @@ fn add_target_crate_root(
env,
proc_macro,
is_proc_macro,
- CrateOrigin::CratesIo { repo: pkg.repository.clone() },
+ CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) },
)
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
index 539258918..544502853 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml
@@ -18,14 +18,14 @@ name = "rust-analyzer"
path = "src/bin/main.rs"
[dependencies]
-anyhow = "1.0.57"
+anyhow = "1.0.62"
crossbeam-channel = "0.5.5"
dissimilar = "1.0.4"
-itertools = "0.10.3"
+itertools = "0.10.5"
scip = "0.1.1"
lsp-types = { version = "0.93.1", features = ["proposed"] }
parking_lot = "0.12.1"
-xflags = "0.2.4"
+xflags = "0.3.0"
oorandom = "11.1.3"
rustc-hash = "1.1.0"
serde = { version = "1.0.137", features = ["derive"] }
@@ -33,10 +33,10 @@ serde_json = { version = "1.0.81", features = ["preserve_order"] }
threadpool = "1.8.1"
rayon = "1.5.3"
num_cpus = "1.13.1"
-mimalloc = { version = "0.1.29", default-features = false, optional = true }
-lsp-server = { version = "0.6.0", path = "../../lib/lsp-server" }
+mimalloc = { version = "0.1.30", default-features = false, optional = true }
+lsp-server = { version = "0.7.0", path = "../../lib/lsp-server" }
tracing = "0.1.35"
-tracing-subscriber = { version = "0.3.14", default-features = false, features = [
+tracing-subscriber = { version = "0.3.16", default-features = false, features = [
"env-filter",
"registry",
"fmt",
@@ -87,7 +87,6 @@ jemalloc = ["jemallocator", "profile/jemalloc"]
force-always-assert = ["always-assert/force"]
in-rust-tree = [
"proc-macro-srv/sysroot-abi",
- "sourcegen/in-rust-tree",
"ide/in-rust-tree",
"syntax/in-rust-tree",
]
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/logger.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/logger.rs
index 298814af5..ac10721d9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/logger.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/logger.rs
@@ -132,7 +132,7 @@ where
let ext = span.extensions();
- // `FormattedFields` is a a formatted representation of the span's
+ // `FormattedFields` is a formatted representation of the span's
// fields, which is stored in its extensions by the `fmt` layer's
// `new_span` method. The fields will have been formatted
// by the same field formatter that's provided to the event
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index f6a680297..eabfcf194 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -37,16 +37,15 @@ fn main() {
process::exit(code);
}
- if let Err(err) = try_main() {
+ let flags = flags::RustAnalyzer::from_env_or_exit();
+ if let Err(err) = try_main(flags) {
tracing::error!("Unexpected error: {}", err);
eprintln!("{}", err);
process::exit(101);
}
}
-fn try_main() -> Result<()> {
- let flags = flags::RustAnalyzer::from_env()?;
-
+fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
#[cfg(debug_assertions)]
if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
#[allow(unused_mut)]
@@ -76,10 +75,6 @@ fn try_main() -> Result<()> {
println!("rust-analyzer {}", rust_analyzer::version());
return Ok(());
}
- if cmd.help {
- println!("{}", flags::RustAnalyzer::HELP);
- return Ok(());
- }
with_extra_thread("LspServer", run_server)?;
}
flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
index 1c39e9391..6ede194ba 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -4,7 +4,7 @@ use std::mem;
use cfg::{CfgAtom, CfgExpr};
use ide::{FileId, RunnableKind, TestId};
-use project_model::{self, ManifestPath, TargetKind};
+use project_model::{self, CargoFeatures, ManifestPath, TargetKind};
use vfs::AbsPathBuf;
use crate::{global_state::GlobalStateSnapshot, Result};
@@ -35,41 +35,41 @@ impl CargoTargetSpec {
match kind {
RunnableKind::Test { test_id, attr } => {
- args.push("test".to_string());
+ args.push("test".to_owned());
extra_args.push(test_id.to_string());
if let TestId::Path(_) = test_id {
- extra_args.push("--exact".to_string());
+ extra_args.push("--exact".to_owned());
}
- extra_args.push("--nocapture".to_string());
+ extra_args.push("--nocapture".to_owned());
if attr.ignore {
- extra_args.push("--ignored".to_string());
+ extra_args.push("--ignored".to_owned());
}
}
RunnableKind::TestMod { path } => {
- args.push("test".to_string());
- extra_args.push(path.to_string());
- extra_args.push("--nocapture".to_string());
+ args.push("test".to_owned());
+ extra_args.push(path.clone());
+ extra_args.push("--nocapture".to_owned());
}
RunnableKind::Bench { test_id } => {
- args.push("bench".to_string());
+ args.push("bench".to_owned());
extra_args.push(test_id.to_string());
if let TestId::Path(_) = test_id {
- extra_args.push("--exact".to_string());
+ extra_args.push("--exact".to_owned());
}
- extra_args.push("--nocapture".to_string());
+ extra_args.push("--nocapture".to_owned());
}
RunnableKind::DocTest { test_id } => {
- args.push("test".to_string());
- args.push("--doc".to_string());
+ args.push("test".to_owned());
+ args.push("--doc".to_owned());
extra_args.push(test_id.to_string());
- extra_args.push("--nocapture".to_string());
+ extra_args.push("--nocapture".to_owned());
}
RunnableKind::Bin => {
let subcommand = match spec {
Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => "test",
_ => "run",
};
- args.push(subcommand.to_string());
+ args.push(subcommand.to_owned());
}
}
@@ -82,29 +82,35 @@ impl CargoTargetSpec {
};
let cargo_config = snap.config.cargo();
- if cargo_config.all_features {
- args.push("--all-features".to_string());
- for feature in target_required_features {
- args.push("--features".to_string());
- args.push(feature);
- }
- } else {
- let mut features = Vec::new();
- if let Some(cfg) = cfg.as_ref() {
- required_features(cfg, &mut features);
+ match &cargo_config.features {
+ CargoFeatures::All => {
+ args.push("--all-features".to_owned());
+ for feature in target_required_features {
+ args.push("--features".to_owned());
+ args.push(feature);
+ }
}
+ CargoFeatures::Selected { features, no_default_features } => {
+ let mut feats = Vec::new();
+ if let Some(cfg) = cfg.as_ref() {
+ required_features(cfg, &mut feats);
+ }
- features.extend(cargo_config.features);
- features.extend(target_required_features);
+ feats.extend(features.iter().cloned());
+ feats.extend(target_required_features);
- features.dedup();
- for feature in features {
- args.push("--features".to_string());
- args.push(feature);
+ feats.dedup();
+ for feature in feats {
+ args.push("--features".to_owned());
+ args.push(feature);
+ }
+
+ if *no_default_features {
+ args.push("--no-default-features".to_owned());
+ }
}
}
-
Ok((args, extra_args))
}
@@ -112,7 +118,7 @@ impl CargoTargetSpec {
global_state_snapshot: &GlobalStateSnapshot,
file_id: FileId,
) -> Result<Option<CargoTargetSpec>> {
- let crate_id = match &*global_state_snapshot.analysis.crate_for(file_id)? {
+ let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? {
&[crate_id, ..] => crate_id,
_ => return Ok(None),
};
@@ -136,7 +142,7 @@ impl CargoTargetSpec {
}
pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) {
- buf.push("--package".to_string());
+ buf.push("--package".to_owned());
buf.push(self.package);
// Can't mix --doc with other target flags
@@ -145,23 +151,23 @@ impl CargoTargetSpec {
}
match self.target_kind {
TargetKind::Bin => {
- buf.push("--bin".to_string());
+ buf.push("--bin".to_owned());
buf.push(self.target);
}
TargetKind::Test => {
- buf.push("--test".to_string());
+ buf.push("--test".to_owned());
buf.push(self.target);
}
TargetKind::Bench => {
- buf.push("--bench".to_string());
+ buf.push("--bench".to_owned());
buf.push(self.target);
}
TargetKind::Example => {
- buf.push("--example".to_string());
+ buf.push("--example".to_owned());
buf.push(self.target);
}
TargetKind::Lib => {
- buf.push("--lib".to_string());
+ buf.push("--lib".to_owned());
}
TargetKind::Other | TargetKind::BuildScript => (),
}
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 f52e1e751..01fccc83e 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
@@ -24,7 +24,7 @@ use ide_db::base_db::{
use itertools::Itertools;
use oorandom::Rand32;
use profile::{Bytes, StopWatch};
-use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
+use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustcSource};
use rayon::prelude::*;
use rustc_hash::FxHashSet;
use stdx::format_to;
@@ -55,7 +55,10 @@ impl flags::AnalysisStats {
};
let mut cargo_config = CargoConfig::default();
- cargo_config.no_sysroot = self.no_sysroot;
+ cargo_config.sysroot = match self.no_sysroot {
+ true => None,
+ false => Some(RustcSource::Discover),
+ };
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: !self.disable_build_scripts,
with_proc_macro: !self.disable_proc_macros,
@@ -80,7 +83,8 @@ impl flags::AnalysisStats {
Some(build_scripts_sw.elapsed())
};
- let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?;
+ let (host, vfs, _proc_macro) =
+ load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
let db = host.raw_database();
eprint!("{:<20} {}", "Database loaded:", db_load_sw.elapsed());
eprint!(" (metadata {}", metadata_time);
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 aa32654fb..5bcc97e22 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
@@ -31,8 +31,6 @@ xflags::xflags! {
default cmd lsp-server {
/// Print version.
optional --version
- /// Print help.
- optional -h, --help
/// Dump a LSP config JSON schema.
optional --print-config-schema
@@ -54,10 +52,10 @@ xflags::xflags! {
}
/// Batch typecheck project and print summary statistics
- cmd analysis-stats
+ cmd analysis-stats {
/// Directory with Cargo.toml.
required path: PathBuf
- {
+
optional --output format: OutputFormat
/// Randomize order in which crates, modules, and items are processed.
@@ -84,38 +82,37 @@ xflags::xflags! {
optional --skip-inference
}
- cmd diagnostics
+ cmd diagnostics {
/// Directory with Cargo.toml.
required path: PathBuf
- {
+
/// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis.
optional --disable-build-scripts
/// Don't use expand proc macros.
optional --disable-proc-macros
}
- cmd ssr
+ cmd ssr {
/// A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`)
repeated rule: SsrRule
- {}
+ }
- cmd search
+ cmd search {
/// A structured search replace pattern (`$a.foo($b)`)
repeated pattern: SsrPattern
- {
/// Prints debug information for any nodes with source exactly equal to snippet.
optional --debug snippet: String
}
cmd proc-macro {}
- cmd lsif
+ cmd lsif {
required path: PathBuf
- {}
+ }
- cmd scip
+ cmd scip {
required path: PathBuf
- {}
+ }
}
}
@@ -150,7 +147,6 @@ pub enum RustAnalyzerCmd {
#[derive(Debug)]
pub struct LspServer {
pub version: bool,
- pub help: bool,
pub print_config_schema: bool,
}
@@ -218,7 +214,10 @@ pub struct Scip {
}
impl RustAnalyzer {
- pub const HELP: &'static str = Self::HELP_;
+ #[allow(dead_code)]
+ pub fn from_env_or_exit() -> Self {
+ Self::from_env_or_exit_()
+ }
#[allow(dead_code)]
pub fn from_env() -> xflags::Result<Self> {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
index 5d1c013c3..5dba545b8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -6,7 +6,7 @@ use anyhow::Result;
use crossbeam_channel::{unbounded, Receiver};
use hir::db::DefDatabase;
use ide::{AnalysisHost, Change};
-use ide_db::base_db::CrateGraph;
+use ide_db::{base_db::CrateGraph, FxHashMap};
use proc_macro_api::ProcMacroServer;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
use vfs::{loader::Handle, AbsPath, AbsPathBuf};
@@ -38,7 +38,7 @@ pub fn load_workspace_at(
workspace.set_build_scripts(build_scripts)
}
- load_workspace(workspace, load_config)
+ load_workspace(workspace, &cargo_config.extra_env, load_config)
}
// Note: Since this function is used by external tools that use rust-analyzer as a library
@@ -48,6 +48,7 @@ pub fn load_workspace_at(
// these tools need access to `ProjectWorkspace`, too, which `load_workspace_at` hides.
pub fn load_workspace(
ws: ProjectWorkspace,
+ extra_env: &FxHashMap<String, String>,
load_config: &LoadCargoConfig,
) -> Result<(AnalysisHost, vfs::Vfs, Option<ProcMacroServer>)> {
let (sender, receiver) = unbounded();
@@ -59,10 +60,26 @@ pub fn load_workspace(
};
let proc_macro_client = if load_config.with_proc_macro {
- let path = AbsPathBuf::assert(std::env::current_exe()?);
- Ok(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap())
+ let mut path = AbsPathBuf::assert(std::env::current_exe()?);
+ let mut args = vec!["proc-macro"];
+
+ if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } =
+ &ws
+ {
+ if let Some(sysroot) = sysroot.as_ref() {
+ let standalone_server_name =
+ format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
+ let server_path = sysroot.root().join("libexec").join(&standalone_server_name);
+ if std::fs::metadata(&server_path).is_ok() {
+ path = server_path;
+ args = vec![];
+ }
+ }
+ }
+
+ ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|e| e.to_string())
} else {
- Err("proc macro server not started".to_owned())
+ Err("proc macro server disabled".to_owned())
};
let crate_graph = ws.to_crate_graph(
@@ -75,6 +92,7 @@ pub fn load_workspace(
vfs.set_file_contents(path.clone(), contents);
vfs.file_id(&path)
},
+ extra_env,
);
let project_folders = ProjectFolders::new(&[ws], &[]);
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 491c55a04..748306ea5 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
@@ -299,7 +299,8 @@ impl flags::Lsif {
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
- let (host, vfs, _proc_macro) = load_workspace(workspace, &load_cargo_config)?;
+ let (host, vfs, _proc_macro) =
+ load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
let db = host.raw_database();
let analysis = host.analysis();
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 65cc993c4..8b77ccde0 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
@@ -8,8 +8,8 @@ use std::{
use crate::line_index::{LineEndings, LineIndex, OffsetEncoding};
use hir::Name;
use ide::{
- LineCol, MonikerDescriptorKind, MonikerResult, StaticIndex, StaticIndexedFile, TextRange,
- TokenId,
+ LineCol, MonikerDescriptorKind, StaticIndex, StaticIndexedFile, TextRange, TokenId,
+ TokenStaticData,
};
use ide_db::LineIndexDatabase;
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace};
@@ -40,7 +40,8 @@ impl flags::Scip {
let workspace = ProjectWorkspace::load(manifest, &cargo_config, no_progress)?;
- let (host, vfs, _) = load_workspace(workspace, &load_cargo_config)?;
+ let (host, vfs, _) =
+ load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
let db = host.raw_database();
let analysis = host.analysis();
@@ -74,7 +75,7 @@ impl flags::Scip {
let mut symbols_emitted: HashSet<TokenId> = HashSet::default();
let mut tokens_to_symbol: HashMap<TokenId, String> = HashMap::new();
- for file in si.files {
+ for StaticIndexedFile { file_id, tokens, .. } in si.files {
let mut local_count = 0;
let mut new_local_symbol = || {
let new_symbol = scip::types::Symbol::new_local(local_count);
@@ -83,7 +84,6 @@ impl flags::Scip {
new_symbol
};
- let StaticIndexedFile { file_id, tokens, .. } = file;
let relative_path = match get_relative_filepath(&vfs, &rootpath, file_id) {
Some(relative_path) => relative_path,
None => continue,
@@ -106,28 +106,20 @@ impl flags::Scip {
let mut occurrence = scip_types::Occurrence::default();
occurrence.range = text_range_to_scip_range(&line_index, range);
- occurrence.symbol = match tokens_to_symbol.get(&id) {
- Some(symbol) => symbol.clone(),
- None => {
- let symbol = match &token.moniker {
- Some(moniker) => moniker_to_symbol(&moniker),
- None => new_local_symbol(),
- };
-
- let symbol = scip::symbol::format_symbol(symbol);
- tokens_to_symbol.insert(id, symbol.clone());
- symbol
- }
- };
+ occurrence.symbol = tokens_to_symbol
+ .entry(id)
+ .or_insert_with(|| {
+ let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol);
+ scip::symbol::format_symbol(symbol)
+ })
+ .clone();
if let Some(def) = token.definition {
if def.range == range {
occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32;
}
- if !symbols_emitted.contains(&id) {
- symbols_emitted.insert(id);
-
+ if symbols_emitted.insert(id) {
let mut symbol_info = scip_types::SymbolInformation::default();
symbol_info.symbol = occurrence.symbol.clone();
if let Some(hover) = &token.hover {
@@ -206,9 +198,11 @@ fn new_descriptor(name: Name, suffix: scip_types::descriptor::Suffix) -> scip_ty
///
/// Only returns a Symbol when it's a non-local symbol.
/// So if the visibility isn't outside of a document, then it will return None
-fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
+fn token_to_symbol(token: &TokenStaticData) -> Option<scip_types::Symbol> {
use scip_types::descriptor::Suffix::*;
+ let moniker = token.moniker.as_ref()?;
+
let package_name = moniker.package_information.name.clone();
let version = moniker.package_information.version.clone();
let descriptors = moniker
@@ -232,7 +226,7 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
})
.collect();
- scip_types::Symbol {
+ Some(scip_types::Symbol {
scheme: "rust-analyzer".into(),
package: Some(scip_types::Package {
manager: "cargo".to_string(),
@@ -243,19 +237,15 @@ fn moniker_to_symbol(moniker: &MonikerResult) -> scip_types::Symbol {
.into(),
descriptors,
..Default::default()
- }
+ })
}
#[cfg(test)]
mod test {
use super::*;
- use hir::Semantics;
- use ide::{AnalysisHost, FilePosition};
- use ide_db::defs::IdentClass;
- use ide_db::{base_db::fixture::ChangeFixture, helpers::pick_best_token};
+ use ide::{AnalysisHost, FilePosition, StaticIndex, TextSize};
+ use ide_db::base_db::fixture::ChangeFixture;
use scip::symbol::format_symbol;
- use syntax::SyntaxKind::*;
- use syntax::{AstNode, T};
fn position(ra_fixture: &str) -> (AnalysisHost, FilePosition) {
let mut host = AnalysisHost::default();
@@ -272,53 +262,33 @@ mod test {
fn check_symbol(ra_fixture: &str, expected: &str) {
let (host, position) = position(ra_fixture);
+ let analysis = host.analysis();
+ let si = StaticIndex::compute(&analysis);
+
let FilePosition { file_id, offset } = position;
- let db = host.raw_database();
- let sema = &Semantics::new(db);
- 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 => 2,
- kind if kind.is_trivia() => 0,
- _ => 1,
- })
- .expect("OK OK");
-
- let navs = sema
- .descend_into_macros(original_token.clone())
- .into_iter()
- .filter_map(|token| {
- IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
- it.into_iter().flat_map(|def| {
- let module = def.module(db).unwrap();
- let current_crate = module.krate();
-
- match MonikerResult::from_def(sema.db, def, current_crate) {
- Some(moniker_result) => Some(moniker_to_symbol(&moniker_result)),
- None => None,
- }
- })
- })
- })
- .flatten()
- .collect::<Vec<_>>();
+ let mut found_symbol = None;
+ for file in &si.files {
+ if file.file_id != file_id {
+ continue;
+ }
+ for &(range, id) in &file.tokens {
+ if range.contains(offset - TextSize::from(1)) {
+ let token = si.tokens.get(id).unwrap();
+ found_symbol = token_to_symbol(token);
+ break;
+ }
+ }
+ }
if expected == "" {
- assert_eq!(0, navs.len(), "must have no symbols {:?}", navs);
+ assert!(found_symbol.is_none(), "must have no symbols {:?}", found_symbol);
return;
}
- assert_eq!(1, navs.len(), "must have one symbol {:?}", navs);
-
- let res = navs.get(0).unwrap();
- let formatted = format_symbol(res.clone());
+ assert!(found_symbol.is_some(), "must have one symbol {:?}", found_symbol);
+ let res = found_symbol.unwrap();
+ let formatted = format_symbol(res);
assert_eq!(formatted, expected);
}
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 54dcb42d9..85322f12a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -7,7 +7,7 @@
//! configure the server itself, feature flags are passed into analysis, and
//! tweak things like automatic insertion of `()` in completions.
-use std::{ffi::OsString, fmt, iter, path::PathBuf};
+use std::{fmt, iter, path::PathBuf};
use flycheck::FlycheckConfig;
use ide::{
@@ -22,7 +22,8 @@ use ide_db::{
use itertools::Itertools;
use lsp_types::{ClientCapabilities, MarkupKind};
use project_model::{
- CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, UnsetTestCrates,
+ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource,
+ UnsetTestCrates,
};
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{de::DeserializeOwned, Deserialize};
@@ -68,6 +69,19 @@ config_data! {
cargo_autoreload: bool = "true",
/// Run build scripts (`build.rs`) for more precise code analysis.
cargo_buildScripts_enable: bool = "true",
+ /// Specifies the working directory for running build scripts.
+ /// - "workspace": run build scripts for a workspace in the workspace's root directory.
+ /// This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
+ /// - "root": run build scripts in the project's root directory.
+ /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// is set.
+ cargo_buildScripts_invocationLocation: InvocationLocation = "\"workspace\"",
+ /// Specifies the invocation strategy to use when running the build scripts 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#`
+ /// is set.
+ cargo_buildScripts_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Override the command rust-analyzer uses to run build scripts and
/// build procedural macros. The command is required to output json
/// and should therefore include `--message-format=json` or a similar
@@ -84,14 +98,22 @@ config_data! {
/// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
/// avoid checking unnecessary things.
cargo_buildScripts_useRustcWrapper: bool = "true",
+ /// Extra environment variables that will be set when running cargo, rustc
+ /// or other commands within the workspace. Useful for setting RUSTFLAGS.
+ cargo_extraEnv: FxHashMap<String, String> = "{}",
/// List of features to activate.
///
/// Set this to `"all"` to pass `--all-features` to cargo.
- cargo_features: CargoFeatures = "[]",
+ cargo_features: CargoFeaturesDef = "[]",
/// Whether to pass `--no-default-features` to cargo.
cargo_noDefaultFeatures: bool = "false",
- /// Internal config for debugging, disables loading of sysroot crates.
- cargo_noSysroot: bool = "false",
+ /// Relative path to the sysroot, or "discover" to try to automatically find it via
+ /// "rustc --print sysroot".
+ ///
+ /// Unsetting this disables sysroot loading.
+ ///
+ /// This option does not take effect until rust-analyzer is restarted.
+ cargo_sysroot: Option<String> = "\"discover\"",
/// Compilation target override (target triple).
cargo_target: Option<String> = "null",
/// Unsets `#[cfg(test)]` for the specified crates.
@@ -105,11 +127,28 @@ config_data! {
checkOnSave_enable: bool = "true",
/// Extra arguments for `cargo check`.
checkOnSave_extraArgs: Vec<String> = "[]",
+ /// Extra environment variables that will be set when running `cargo check`.
+ /// Extends `#rust-analyzer.cargo.extraEnv#`.
+ checkOnSave_extraEnv: FxHashMap<String, String> = "{}",
/// List of features to activate. Defaults to
/// `#rust-analyzer.cargo.features#`.
///
/// Set to `"all"` to pass `--all-features` to Cargo.
- checkOnSave_features: Option<CargoFeatures> = "null",
+ checkOnSave_features: Option<CargoFeaturesDef> = "null",
+ /// 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`.
+ /// - "root": run checks in the project's root directory.
+ /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+ /// is set.
+ checkOnSave_invocationLocation: InvocationLocation = "\"workspace\"",
+ /// Specifies the invocation strategy to use when running the checkOnSave 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#`
+ /// is set.
+ checkOnSave_invocationStrategy: InvocationStrategy = "\"per_workspace\"",
/// Whether to pass `--no-default-features` to Cargo. Defaults to
/// `#rust-analyzer.cargo.noDefaultFeatures#`.
checkOnSave_noDefaultFeatures: Option<bool> = "null",
@@ -219,7 +258,6 @@ config_data! {
files_excludeDirs: Vec<PathBuf> = "[]",
/// Controls file watching implementation.
files_watcher: FilesWatcherDef = "\"client\"",
-
/// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords.
highlightRelated_breakPoints_enable: bool = "true",
/// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`).
@@ -263,6 +301,8 @@ config_data! {
imports_group_enable: bool = "true",
/// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
imports_merge_glob: bool = "true",
+ /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
+ imports_prefer_no_std: bool = "false",
/// The path structure for newly inserted paths to use.
imports_prefix: ImportPrefixDef = "\"plain\"",
@@ -307,6 +347,7 @@ config_data! {
/// Join lines unwraps trivial blocks.
joinLines_unwrapTrivialBlock: bool = "true",
+
/// Whether to show `Debug` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_debug_enable: bool = "true",
@@ -318,6 +359,8 @@ config_data! {
/// Whether to show `Implementations` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_implementations_enable: bool = "true",
+ /// Where to render annotations.
+ lens_location: AnnotationLocation = "\"above_name\"",
/// Whether to show `References` lens for Struct, Enum, and Union.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_adt_enable: bool = "false",
@@ -359,6 +402,9 @@ config_data! {
/// this is rust-analyzer itself, but we override this in tests).
procMacro_server: Option<PathBuf> = "null",
+ /// Exclude imports from find-all-references.
+ references_excludeImports: bool = "false",
+
/// Command to be executed instead of 'cargo' for runnables.
runnables_command: Option<String> = "null",
/// Additional arguments to be passed to cargo for runnables such as
@@ -494,6 +540,25 @@ pub struct LensConfig {
pub refs_adt: bool, // for Struct, Enum, Union and Trait
pub refs_trait: bool, // for Struct, Enum, Union and Trait
pub enum_variant_refs: bool,
+
+ // annotations
+ pub location: AnnotationLocation,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum AnnotationLocation {
+ AboveName,
+ AboveWholeItem,
+}
+
+impl From<AnnotationLocation> for ide::AnnotationLocation {
+ fn from(location: AnnotationLocation) -> Self {
+ match location {
+ AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName,
+ AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem,
+ }
+ }
}
impl LensConfig {
@@ -918,6 +983,7 @@ impl Config {
ExprFillDefaultDef::Default => ExprFillDefaultMode::Default,
},
insert_use: self.insert_use_config(),
+ prefer_no_std: self.data.imports_prefer_no_std,
}
}
@@ -929,19 +995,31 @@ impl Config {
}
}
+ pub fn extra_env(&self) -> &FxHashMap<String, String> {
+ &self.data.cargo_extraEnv
+ }
+
+ pub fn check_on_save_extra_env(&self) -> FxHashMap<String, String> {
+ let mut extra_env = self.data.cargo_extraEnv.clone();
+ extra_env.extend(self.data.checkOnSave_extraEnv.clone());
+ extra_env
+ }
+
pub fn lru_capacity(&self) -> Option<usize> {
self.data.lru_capacity
}
- pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, Vec<OsString>)> {
+ pub fn proc_macro_srv(&self) -> Option<(AbsPathBuf, /* is path explicitly set */ bool)> {
if !self.data.procMacro_enable {
return None;
}
- let path = match &self.data.procMacro_server {
- Some(it) => self.root_path.join(it),
- None => AbsPathBuf::assert(std::env::current_exe().ok()?),
- };
- Some((path, vec!["proc-macro".into()]))
+ Some(match &self.data.procMacro_server {
+ Some(it) => (
+ AbsPathBuf::try_from(it.clone()).unwrap_or_else(|path| self.root_path.join(path)),
+ true,
+ ),
+ None => (AbsPathBuf::assert(std::env::current_exe().ok()?), false),
+ })
}
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
@@ -984,20 +1062,39 @@ impl Config {
RustcSource::Path(self.root_path.join(rustc_src))
}
});
+ let sysroot = self.data.cargo_sysroot.as_ref().map(|sysroot| {
+ if sysroot == "discover" {
+ RustcSource::Discover
+ } else {
+ RustcSource::Path(self.root_path.join(sysroot))
+ }
+ });
CargoConfig {
- no_default_features: self.data.cargo_noDefaultFeatures,
- all_features: matches!(self.data.cargo_features, CargoFeatures::All),
features: match &self.data.cargo_features {
- CargoFeatures::All => vec![],
- CargoFeatures::Listed(it) => it.clone(),
+ CargoFeaturesDef::All => CargoFeatures::All,
+ CargoFeaturesDef::Selected(features) => CargoFeatures::Selected {
+ features: features.clone(),
+ no_default_features: self.data.cargo_noDefaultFeatures,
+ },
},
target: self.data.cargo_target.clone(),
- no_sysroot: self.data.cargo_noSysroot,
+ sysroot,
rustc_source,
unset_test_crates: UnsetTestCrates::Only(self.data.cargo_unsetTest.clone()),
wrap_rustc_in_build_scripts: self.data.cargo_buildScripts_useRustcWrapper,
+ invocation_strategy: match self.data.cargo_buildScripts_invocationStrategy {
+ InvocationStrategy::Once => project_model::InvocationStrategy::Once,
+ InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace,
+ },
+ invocation_location: match self.data.cargo_buildScripts_invocationLocation {
+ InvocationLocation::Root => {
+ project_model::InvocationLocation::Root(self.root_path.clone())
+ }
+ InvocationLocation::Workspace => project_model::InvocationLocation::Workspace,
+ },
run_build_script_command: self.data.cargo_buildScripts_overrideCommand.clone(),
+ extra_env: self.data.cargo_extraEnv.clone(),
}
}
@@ -1023,7 +1120,23 @@ impl Config {
Some(args) if !args.is_empty() => {
let mut args = args.clone();
let command = args.remove(0);
- FlycheckConfig::CustomCommand { command, args }
+ FlycheckConfig::CustomCommand {
+ command,
+ args,
+ extra_env: self.check_on_save_extra_env(),
+ invocation_strategy: match self.data.checkOnSave_invocationStrategy {
+ InvocationStrategy::Once => flycheck::InvocationStrategy::Once,
+ InvocationStrategy::PerWorkspace => {
+ flycheck::InvocationStrategy::PerWorkspace
+ }
+ },
+ invocation_location: match self.data.checkOnSave_invocationLocation {
+ InvocationLocation::Root => {
+ flycheck::InvocationLocation::Root(self.root_path.clone())
+ }
+ InvocationLocation::Workspace => flycheck::InvocationLocation::Workspace,
+ },
+ }
}
Some(_) | None => FlycheckConfig::CargoCommand {
command: self.data.checkOnSave_command.clone(),
@@ -1039,7 +1152,7 @@ impl Config {
.unwrap_or(self.data.cargo_noDefaultFeatures),
all_features: matches!(
self.data.checkOnSave_features.as_ref().unwrap_or(&self.data.cargo_features),
- CargoFeatures::All
+ CargoFeaturesDef::All
),
features: match self
.data
@@ -1047,10 +1160,11 @@ impl Config {
.clone()
.unwrap_or_else(|| self.data.cargo_features.clone())
{
- CargoFeatures::All => vec![],
- CargoFeatures::Listed(it) => it,
+ CargoFeaturesDef::All => vec![],
+ CargoFeaturesDef::Selected(it) => it,
},
extra_args: self.data.checkOnSave_extraArgs.clone(),
+ extra_env: self.check_on_save_extra_env(),
},
};
Some(flycheck_config)
@@ -1133,6 +1247,7 @@ impl Config {
CallableCompletionDef::None => None,
},
insert_use: self.insert_use_config(),
+ prefer_no_std: self.data.imports_prefer_no_std,
snippet_cap: SnippetCap::new(try_or_def!(
self.caps
.text_document
@@ -1147,6 +1262,10 @@ impl Config {
}
}
+ pub fn find_all_refs_exclude_imports(&self) -> bool {
+ self.data.references_excludeImports
+ }
+
pub fn snippet_cap(&self) -> bool {
self.experimental("snippetTextEdit")
}
@@ -1156,6 +1275,7 @@ impl Config {
snippet_cap: SnippetCap::new(self.experimental("snippetTextEdit")),
allowed: None,
insert_use: self.insert_use_config(),
+ prefer_no_std: self.data.imports_prefer_no_std,
}
}
@@ -1185,6 +1305,7 @@ impl Config {
refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
enum_variant_refs: self.data.lens_enable
&& self.data.lens_references_enumVariant_enable,
+ location: self.data.lens_location,
}
}
@@ -1509,10 +1630,24 @@ enum CallableCompletionDef {
#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
-enum CargoFeatures {
+enum CargoFeaturesDef {
#[serde(deserialize_with = "de_unit_v::all")]
All,
- Listed(Vec<String>),
+ Selected(Vec<String>),
+}
+
+#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum InvocationStrategy {
+ Once,
+ PerWorkspace,
+}
+
+#[derive(Deserialize, Debug, Clone)]
+#[serde(rename_all = "snake_case")]
+enum InvocationLocation {
+ Root,
+ Workspace,
}
#[derive(Deserialize, Debug, Clone)]
@@ -1857,7 +1992,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"Only show mutable reborrow hints."
]
},
- "CargoFeatures" => set! {
+ "CargoFeaturesDef" => set! {
"anyOf": [
{
"type": "string",
@@ -1874,7 +2009,7 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
}
],
},
- "Option<CargoFeatures>" => set! {
+ "Option<CargoFeaturesDef>" => set! {
"anyOf": [
{
"type": "string",
@@ -1921,6 +2056,30 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"Use server-side file watching",
],
},
+ "AnnotationLocation" => set! {
+ "type": "string",
+ "enum": ["above_name", "above_whole_item"],
+ "enumDescriptions": [
+ "Render annotations above the name of the item.",
+ "Render annotations above the whole item, including documentation comments and attributes."
+ ],
+ },
+ "InvocationStrategy" => set! {
+ "type": "string",
+ "enum": ["per_workspace", "once"],
+ "enumDescriptions": [
+ "The command will be executed for each workspace.",
+ "The command will be executed once."
+ ],
+ },
+ "InvocationLocation" => set! {
+ "type": "string",
+ "enum": ["workspace", "root"],
+ "enumDescriptions": [
+ "The command will be executed in the corresponding workspace root.",
+ "The command will be executed in the project root."
+ ],
+ },
_ => panic!("missing entry for {}: {}", ty, default),
}
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 f16559148..57899b599 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
@@ -52,7 +52,7 @@ impl<'a> RequestDispatcher<'a> {
let _pctx = stdx::panic_context::enter(panic_context);
f(self.global_state, params)
};
- if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
+ if let Ok(response) = result_to_response::<R>(req.id, result) {
self.global_state.respond(response);
}
@@ -80,7 +80,7 @@ impl<'a> RequestDispatcher<'a> {
f(global_state_snapshot, params)
});
- if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
+ if let Ok(response) = thread_result_to_response::<R>(req.id, result) {
self.global_state.respond(response);
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
index 7bdd34d1f..f2db9a273 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/from_proto.rs
@@ -95,22 +95,22 @@ pub(crate) fn annotation(
match resolve {
lsp_ext::CodeLensResolveData::Impls(params) => {
- let file_id =
- snap.url_to_file_id(&params.text_document_position_params.text_document.uri)?;
+ let pos @ FilePosition { file_id, .. } =
+ file_position(snap, params.text_document_position_params)?;
let line_index = snap.file_line_index(file_id)?;
Ok(Annotation {
range: text_range(&line_index, code_lens.range)?,
- kind: AnnotationKind::HasImpls { file_id, data: None },
+ kind: AnnotationKind::HasImpls { pos, data: None },
})
}
lsp_ext::CodeLensResolveData::References(params) => {
- let file_id = snap.url_to_file_id(&params.text_document.uri)?;
+ let pos @ FilePosition { file_id, .. } = file_position(snap, params)?;
let line_index = snap.file_line_index(file_id)?;
Ok(Annotation {
range: text_range(&line_index, code_lens.range)?,
- kind: AnnotationKind::HasReferences { file_id, data: None },
+ kind: AnnotationKind::HasReferences { pos, data: None },
})
}
}
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 92df4d70f..3fb06c31f 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
@@ -64,7 +64,7 @@ pub(crate) struct GlobalState {
pub(crate) source_root_config: SourceRootConfig,
pub(crate) proc_macro_clients: Vec<Result<ProcMacroServer, String>>,
- pub(crate) flycheck: Vec<FlycheckHandle>,
+ pub(crate) flycheck: Arc<[FlycheckHandle]>,
pub(crate) flycheck_sender: Sender<flycheck::Message>,
pub(crate) flycheck_receiver: Receiver<flycheck::Message>,
@@ -117,6 +117,7 @@ pub(crate) struct GlobalStateSnapshot {
vfs: Arc<RwLock<(vfs::Vfs, NoHashHashMap<FileId, LineEndings>)>>,
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
pub(crate) proc_macros_loaded: bool,
+ pub(crate) flycheck: Arc<[FlycheckHandle]>,
}
impl std::panic::UnwindSafe for GlobalStateSnapshot {}
@@ -155,7 +156,7 @@ impl GlobalState {
source_root_config: SourceRootConfig::default(),
proc_macro_clients: vec![],
- flycheck: Vec::new(),
+ flycheck: Arc::new([]),
flycheck_sender,
flycheck_receiver,
@@ -185,11 +186,48 @@ impl GlobalState {
let (change, changed_files) = {
let mut change = Change::new();
let (vfs, line_endings_map) = &mut *self.vfs.write();
- let changed_files = vfs.take_changes();
+ let mut changed_files = vfs.take_changes();
if changed_files.is_empty() {
return false;
}
+ // important: this needs to be a stable sort, the order between changes is relevant
+ // for the same file ids
+ changed_files.sort_by_key(|file| file.file_id);
+ // 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 no longer observe the file text from the
+ // create or modify which may cause problems later on
+ changed_files.dedup_by(|a, b| {
+ use vfs::ChangeKind::*;
+
+ if a.file_id != b.file_id {
+ return false;
+ }
+
+ match (a.change_kind, b.change_kind) {
+ // duplicate can be merged
+ (Create, Create) | (Modify, Modify) | (Delete, Delete) => true,
+ // just leave the create, modify is irrelevant
+ (Create, Modify) => {
+ std::mem::swap(a, b);
+ true
+ }
+ // modify becomes irrelevant if the file is deleted
+ (Modify, Delete) => true,
+ // we should fully remove this occurrence,
+ // but leaving just a delete works as well
+ (Create, Delete) => true,
+ // this is equivalent to a modify
+ (Delete, Create) => {
+ a.change_kind = Modify;
+ true
+ }
+ // can't really occur
+ (Modify, Create) => false,
+ (Delete, Modify) => false,
+ }
+ });
+
for file in &changed_files {
if let Some(path) = vfs.file_path(file.file_id).as_path() {
let path = path.to_path_buf();
@@ -258,6 +296,7 @@ impl GlobalState {
mem_docs: self.mem_docs.clone(),
semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(),
+ flycheck: self.flycheck.clone(),
}
}
@@ -317,6 +356,10 @@ impl GlobalState {
}
}
+ pub(crate) fn is_completed(&self, request: &lsp_server::Request) -> bool {
+ self.req_queue.incoming.is_completed(&request.id)
+ }
+
fn send(&mut self, message: lsp_server::Message) {
self.sender.send(message).unwrap()
}
@@ -357,6 +400,10 @@ impl GlobalStateSnapshot {
url_from_abs_path(path)
}
+ pub(crate) fn file_id_to_file_path(&self, file_id: FileId) -> vfs::VfsPath {
+ self.vfs.read().0.file_path(file_id)
+ }
+
pub(crate) fn cargo_target_for_crate_root(
&self,
crate_id: CrateId,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
index e79cf3d3f..34795a8eb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers.rs
@@ -10,8 +10,8 @@ use std::{
use anyhow::Context;
use ide::{
AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange,
- HoverAction, HoverGotoTypeData, Query, RangeInfo, Runnable, RunnableKind, SingleResolve,
- SourceChange, TextEdit,
+ HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind,
+ SingleResolve, SourceChange, TextEdit,
};
use ide_db::SymbolKind;
use lsp_server::ErrorCode;
@@ -658,7 +658,7 @@ pub(crate) fn handle_parent_module(
// check if invoked at the crate root
let file_id = from_proto::file_id(&snap, &params.text_document.uri)?;
- let crate_id = match snap.analysis.crate_for(file_id)?.first() {
+ let crate_id = match snap.analysis.crates_for(file_id)?.first() {
Some(&crate_id) => crate_id,
None => return Ok(None),
};
@@ -1012,6 +1012,8 @@ pub(crate) fn handle_references(
let _p = profile::span("handle_references");
let position = from_proto::file_position(&snap, params.text_document_position)?;
+ let exclude_imports = snap.config.find_all_refs_exclude_imports();
+
let refs = match snap.analysis.find_all_refs(position, None)? {
None => return Ok(None),
Some(refs) => refs,
@@ -1032,7 +1034,11 @@ pub(crate) fn handle_references(
refs.references
.into_iter()
.flat_map(|(file_id, refs)| {
- refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
+ refs.into_iter()
+ .filter(|&(_, category)| {
+ !exclude_imports || category != Some(ReferenceCategory::Import)
+ })
+ .map(move |(range, _)| FileRange { file_id, range })
})
.chain(decl)
})
@@ -1234,6 +1240,7 @@ pub(crate) fn handle_code_lens(
annotate_references: lens_config.refs_adt,
annotate_method_references: lens_config.method_refs,
annotate_enum_variant_references: lens_config.enum_variant_refs,
+ location: lens_config.location.into(),
},
file_id,
)?;
@@ -1283,7 +1290,7 @@ pub(crate) fn handle_document_highlight(
.into_iter()
.map(|ide::HighlightedRange { range, category }| lsp_types::DocumentHighlight {
range: to_proto::range(&line_index, range),
- kind: category.map(to_proto::document_highlight_kind),
+ kind: category.and_then(to_proto::document_highlight_kind),
})
.collect();
Ok(Some(res))
@@ -1775,13 +1782,22 @@ fn run_rustfmt(
) -> Result<Option<Vec<lsp_types::TextEdit>>> {
let file_id = from_proto::file_id(snap, &text_document.uri)?;
let file = snap.analysis.file_text(file_id)?;
- let crate_ids = snap.analysis.crate_for(file_id)?;
+
+ // find the edition of the package the file belongs to
+ // (if it belongs to multiple we'll just pick the first one and pray)
+ let edition = snap
+ .analysis
+ .relevant_crates_for(file_id)?
+ .into_iter()
+ .find_map(|crate_id| snap.cargo_target_for_crate_root(crate_id))
+ .map(|(ws, target)| ws[ws[target].package].edition);
let line_index = snap.file_line_index(file_id)?;
let mut command = match snap.config.rustfmt() {
RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => {
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
@@ -1800,9 +1816,7 @@ fn run_rustfmt(
);
}
}
- if let Some(&crate_id) = crate_ids.first() {
- // Assume all crates are in the same edition
- let edition = snap.analysis.crate_edition(crate_id)?;
+ if let Some(edition) = edition {
cmd.arg("--edition");
cmd.arg(edition.to_string());
}
@@ -1839,6 +1853,7 @@ fn run_rustfmt(
}
RustfmtConfig::CustomCommand { command, args } => {
let mut cmd = process::Command::new(command);
+ cmd.envs(snap.config.extra_env());
cmd.args(args);
cmd
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index e49a98685..96b1cb6b1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -145,6 +145,7 @@ fn integrated_completion_benchmark() {
skip_glob_imports: true,
},
snippets: Vec::new(),
+ prefer_no_std: false,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -182,6 +183,7 @@ fn integrated_completion_benchmark() {
skip_glob_imports: true,
},
snippets: Vec::new(),
+ prefer_no_std: false,
};
let position =
FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
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 5a37cbe2e..b3cea64d4 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
@@ -87,6 +87,7 @@ impl GlobalState {
state: Progress,
message: Option<String>,
fraction: Option<f64>,
+ cancel_token: Option<String>,
) {
if !self.config.work_done_progress() {
return;
@@ -95,7 +96,10 @@ impl GlobalState {
assert!((0.0..=1.0).contains(&f));
(f * 100.0) as u32
});
- let token = lsp_types::ProgressToken::String(format!("rustAnalyzer/{}", title));
+ let cancellable = Some(cancel_token.is_some());
+ let token = lsp_types::ProgressToken::String(
+ cancel_token.unwrap_or_else(|| format!("rustAnalyzer/{}", title)),
+ );
let work_done_progress = match state {
Progress::Begin => {
self.send_request::<lsp_types::request::WorkDoneProgressCreate>(
@@ -105,14 +109,14 @@ impl GlobalState {
lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
title: title.into(),
- cancellable: None,
+ cancellable,
message,
percentage,
})
}
Progress::Report => {
lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
- cancellable: None,
+ cancellable,
message,
percentage,
})
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 3cfbc2e4e..2c928a580 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
@@ -10,7 +10,7 @@ use std::{
use always_assert::always;
use crossbeam_channel::{select, Receiver};
use flycheck::FlycheckHandle;
-use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
+use ide_db::base_db::{SourceDatabaseExt, VfsPath};
use itertools::Itertools;
use lsp_server::{Connection, Notification, Request};
use lsp_types::notification::Notification as _;
@@ -191,7 +191,7 @@ impl GlobalState {
// NOTE: don't count blocking select! call as a loop-turn time
let _p = profile::span("GlobalState::handle_event");
- tracing::debug!("handle_event({:?})", event);
+ tracing::debug!("{:?} handle_event({:?})", loop_start, event);
let task_queue_len = self.task_pool.handle.len();
if task_queue_len > 0 {
tracing::info!("task queue len: {}", task_queue_len);
@@ -257,7 +257,7 @@ impl GlobalState {
}
};
- self.report_progress("Indexing", state, message, Some(fraction));
+ self.report_progress("Indexing", state, message, Some(fraction), None);
}
}
Event::Vfs(message) => {
@@ -425,7 +425,9 @@ impl GlobalState {
fn handle_task(&mut self, prime_caches_progress: &mut Vec<PrimeCachesProgress>, task: Task) {
match task {
Task::Response(response) => self.respond(response),
- Task::Retry(req) => self.on_request(req),
+ // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work.
+ Task::Retry(req) if !self.is_completed(&req) => self.on_request(req),
+ Task::Retry(_) => (),
Task::Diagnostics(diagnostics_per_file) => {
for (file_id, diagnostics) in diagnostics_per_file {
self.diagnostics.set_native_diagnostics(file_id, diagnostics)
@@ -463,7 +465,7 @@ impl GlobalState {
}
};
- self.report_progress("Fetching", state, msg, None);
+ self.report_progress("Fetching", state, msg, None, None);
}
Task::FetchBuildData(progress) => {
let (state, msg) = match progress {
@@ -479,7 +481,7 @@ impl GlobalState {
};
if let Some(state) = state {
- self.report_progress("Loading", state, msg, None);
+ self.report_progress("Loading", state, msg, None, None);
}
}
}
@@ -516,6 +518,7 @@ impl GlobalState {
state,
Some(format!("{}/{}", n_done, n_total)),
Some(Progress::fraction(n_done, n_total)),
+ None,
)
}
}
@@ -540,7 +543,10 @@ impl GlobalState {
diag.fix,
),
Err(err) => {
- tracing::error!("File with cargo diagnostic not found in VFS: {}", err);
+ tracing::error!(
+ "flycheck {id}: File with cargo diagnostic not found in VFS: {}",
+ err
+ );
}
};
}
@@ -582,7 +588,13 @@ impl GlobalState {
} else {
format!("cargo check (#{})", id + 1)
};
- self.report_progress(&title, state, message, None);
+ self.report_progress(
+ &title,
+ state,
+ message,
+ None,
+ Some(format!("rust-analyzer/checkOnSave/{}", id)),
+ );
}
}
}
@@ -696,7 +708,16 @@ impl GlobalState {
this.cancel(id);
Ok(())
})?
- .on::<lsp_types::notification::WorkDoneProgressCancel>(|_this, _params| {
+ .on::<lsp_types::notification::WorkDoneProgressCancel>(|this, params| {
+ if let lsp_types::NumberOrString::String(s) = &params.token {
+ if let Some(id) = s.strip_prefix("rust-analyzer/checkOnSave/") {
+ if let Ok(id) = u32::from_str_radix(id, 10) {
+ if let Some(flycheck) = this.flycheck.get(id as usize) {
+ flycheck.cancel();
+ }
+ }
+ }
+ }
// Just ignore this. It is OK to continue sending progress
// notifications for this token, as the client can't know when
// we accepted notification.
@@ -709,7 +730,7 @@ impl GlobalState {
.insert(path.clone(), DocumentData::new(params.text_document.version))
.is_err();
if already_exists {
- tracing::error!("duplicate DidOpenTextDocument: {}", path)
+ tracing::error!("duplicate DidOpenTextDocument: {}", path);
}
this.vfs
.write()
@@ -756,69 +777,7 @@ impl GlobalState {
Ok(())
})?
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
- let mut updated = false;
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
- let (vfs, _) = &*this.vfs.read();
-
- // Trigger flychecks for all workspaces that depend on the saved file
- if let Some(file_id) = vfs.file_id(&vfs_path) {
- let analysis = this.analysis_host.analysis();
- // Crates containing or depending on the saved file
- let crate_ids: Vec<_> = analysis
- .crate_for(file_id)?
- .into_iter()
- .flat_map(|id| {
- this.analysis_host
- .raw_database()
- .crate_graph()
- .transitive_rev_deps(id)
- })
- .sorted()
- .unique()
- .collect();
-
- let crate_root_paths: Vec<_> = crate_ids
- .iter()
- .filter_map(|&crate_id| {
- analysis
- .crate_root(crate_id)
- .map(|file_id| {
- vfs.file_path(file_id).as_path().map(ToOwned::to_owned)
- })
- .transpose()
- })
- .collect::<ide::Cancellable<_>>()?;
- let crate_root_paths: Vec<_> =
- crate_root_paths.iter().map(Deref::deref).collect();
-
- // Find all workspaces that have at least one target containing the saved file
- let workspace_ids =
- this.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
- project_model::ProjectWorkspace::Cargo { cargo, .. } => {
- cargo.packages().any(|pkg| {
- cargo[pkg].targets.iter().any(|&it| {
- crate_root_paths.contains(&cargo[it].root.as_path())
- })
- })
- }
- project_model::ProjectWorkspace::Json { project, .. } => project
- .crates()
- .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
- project_model::ProjectWorkspace::DetachedFiles { .. } => false,
- });
-
- // Find and trigger corresponding flychecks
- for flycheck in &this.flycheck {
- for (id, _) in workspace_ids.clone() {
- if id == flycheck.id() {
- updated = true;
- flycheck.restart();
- continue;
- }
- }
- }
- }
-
// Re-fetch workspaces if a workspace related file has changed
if let Some(abs_path) = vfs_path.as_path() {
if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
@@ -826,13 +785,90 @@ impl GlobalState {
.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
}
}
+
+ let file_id = this.vfs.read().0.file_id(&vfs_path);
+ if let Some(file_id) = file_id {
+ let world = this.snapshot();
+ let mut updated = false;
+ let task = move || -> std::result::Result<(), ide::Cancelled> {
+ // Trigger flychecks for all workspaces that depend on the saved file
+ // Crates containing or depending on the saved file
+ let crate_ids: Vec<_> = world
+ .analysis
+ .crates_for(file_id)?
+ .into_iter()
+ .flat_map(|id| world.analysis.transitive_rev_deps(id))
+ .flatten()
+ .sorted()
+ .unique()
+ .collect();
+
+ let crate_root_paths: Vec<_> = crate_ids
+ .iter()
+ .filter_map(|&crate_id| {
+ world
+ .analysis
+ .crate_root(crate_id)
+ .map(|file_id| {
+ world
+ .file_id_to_file_path(file_id)
+ .as_path()
+ .map(ToOwned::to_owned)
+ })
+ .transpose()
+ })
+ .collect::<ide::Cancellable<_>>()?;
+ let crate_root_paths: Vec<_> =
+ crate_root_paths.iter().map(Deref::deref).collect();
+
+ // Find all workspaces that have at least one target containing the saved file
+ let workspace_ids =
+ world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
+ project_model::ProjectWorkspace::Cargo { cargo, .. } => {
+ cargo.packages().any(|pkg| {
+ cargo[pkg].targets.iter().any(|&it| {
+ crate_root_paths.contains(&cargo[it].root.as_path())
+ })
+ })
+ }
+ project_model::ProjectWorkspace::Json { project, .. } => {
+ project.crates().any(|(c, _)| {
+ crate_ids.iter().any(|&crate_id| crate_id == c)
+ })
+ }
+ project_model::ProjectWorkspace::DetachedFiles { .. } => false,
+ });
+
+ // Find and trigger corresponding flychecks
+ for flycheck in world.flycheck.iter() {
+ for (id, _) in workspace_ids.clone() {
+ if id == flycheck.id() {
+ updated = true;
+ flycheck.restart();
+ continue;
+ }
+ }
+ }
+ // No specific flycheck was triggered, so let's trigger all of them.
+ if !updated {
+ for flycheck in world.flycheck.iter() {
+ flycheck.restart();
+ }
+ }
+ Ok(())
+ };
+ this.task_pool.handle.spawn_with_sender(move |_| {
+ if let Err(e) = std::panic::catch_unwind(task) {
+ tracing::error!("DidSaveTextDocument flycheck task panicked: {e:?}")
+ }
+ });
+ return Ok(());
+ }
}
// No specific flycheck was triggered, so let's trigger all of them.
- if !updated {
- for flycheck in &this.flycheck {
- flycheck.restart();
- }
+ for flycheck in this.flycheck.iter() {
+ flycheck.restart();
}
Ok(())
})?
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 e47f70fff..e1f651786 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -143,6 +143,7 @@ impl GlobalState {
project_model::ProjectWorkspace::load_inline(
it.clone(),
cargo_config.target.as_deref(),
+ &cargo_config.extra_env,
)
}
})
@@ -174,10 +175,8 @@ impl GlobalState {
sender.send(Task::FetchBuildData(BuildDataProgress::Report(msg))).unwrap()
}
};
- let mut res = Vec::new();
- for ws in workspaces.iter() {
- res.push(ws.run_build_scripts(&config, &progress));
- }
+ let res = ProjectWorkspace::run_all_build_scripts(&workspaces, &config, &progress);
+
sender.send(Task::FetchBuildData(BuildDataProgress::End((workspaces, res)))).unwrap();
});
}
@@ -305,41 +304,50 @@ impl GlobalState {
format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX);
if self.proc_macro_clients.is_empty() {
- if let Some((path, args)) = self.config.proc_macro_srv() {
+ if let Some((path, path_manually_set)) = self.config.proc_macro_srv() {
tracing::info!("Spawning proc-macro servers");
self.proc_macro_clients = self
.workspaces
.iter()
.map(|ws| {
- let mut args = args.clone();
- let mut path = path.clone();
-
- if let ProjectWorkspace::Cargo { sysroot, .. }
- | ProjectWorkspace::Json { sysroot, .. } = ws
- {
- tracing::debug!("Found a cargo workspace...");
- if let Some(sysroot) = sysroot.as_ref() {
- tracing::debug!("Found a cargo workspace with a sysroot...");
- let server_path =
- sysroot.root().join("libexec").join(&standalone_server_name);
- if std::fs::metadata(&server_path).is_ok() {
- tracing::debug!(
- "And the server exists at {}",
- server_path.display()
- );
- path = server_path;
- args = vec![];
- } else {
- tracing::debug!(
- "And the server does not exist at {}",
- server_path.display()
- );
+ let (path, args) = if path_manually_set {
+ tracing::debug!(
+ "Pro-macro server path explicitly set: {}",
+ path.display()
+ );
+ (path.clone(), vec![])
+ } else {
+ let mut sysroot_server = None;
+ if let ProjectWorkspace::Cargo { sysroot, .. }
+ | ProjectWorkspace::Json { sysroot, .. } = ws
+ {
+ if let Some(sysroot) = sysroot.as_ref() {
+ let server_path = sysroot
+ .root()
+ .join("libexec")
+ .join(&standalone_server_name);
+ if std::fs::metadata(&server_path).is_ok() {
+ tracing::debug!(
+ "Sysroot proc-macro server exists at {}",
+ server_path.display()
+ );
+ sysroot_server = Some(server_path);
+ } else {
+ tracing::debug!(
+ "Sysroot proc-macro server does not exist at {}",
+ server_path.display()
+ );
+ }
}
}
- }
+ sysroot_server.map_or_else(
+ || (path.clone(), vec!["proc-macro".to_owned()]),
+ |path| (path, vec![]),
+ )
+ };
tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
- ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
+ ProcMacroServer::spawn(path.clone(), args).map_err(|err| {
let error = format!(
"Failed to run proc-macro server from path {}, error: {:?}",
path.display(),
@@ -398,7 +406,11 @@ impl GlobalState {
dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(),
)
};
- crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
+ crate_graph.extend(ws.to_crate_graph(
+ &mut load_proc_macro,
+ &mut load,
+ &self.config.cargo().extra_env,
+ ));
}
crate_graph
};
@@ -454,39 +466,54 @@ impl GlobalState {
let config = match self.config.flycheck() {
Some(it) => it,
None => {
- self.flycheck = Vec::new();
+ self.flycheck = Arc::new([]);
self.diagnostics.clear_check_all();
return;
}
};
let sender = self.flycheck_sender.clone();
- self.flycheck = self
- .workspaces
- .iter()
- .enumerate()
- .filter_map(|(id, w)| match w {
- ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
- ProjectWorkspace::Json { project, .. } => {
- // Enable flychecks for json projects if a custom flycheck command was supplied
- // in the workspace configuration.
- match config {
- FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
- _ => None,
- }
- }
- ProjectWorkspace::DetachedFiles { .. } => None,
- })
- .map(|(id, root)| {
- let sender = sender.clone();
- FlycheckHandle::spawn(
- id,
- Box::new(move |msg| sender.send(msg).unwrap()),
- config.clone(),
- root.to_path_buf(),
- )
- })
- .collect();
+ let invocation_strategy = match config {
+ FlycheckConfig::CargoCommand { .. } => flycheck::InvocationStrategy::PerWorkspace,
+ FlycheckConfig::CustomCommand { invocation_strategy, .. } => invocation_strategy,
+ };
+
+ self.flycheck = match invocation_strategy {
+ flycheck::InvocationStrategy::Once => vec![FlycheckHandle::spawn(
+ 0,
+ Box::new(move |msg| sender.send(msg).unwrap()),
+ config.clone(),
+ self.config.root_path().clone(),
+ )],
+ flycheck::InvocationStrategy::PerWorkspace => {
+ self.workspaces
+ .iter()
+ .enumerate()
+ .filter_map(|(id, w)| match w {
+ ProjectWorkspace::Cargo { cargo, .. } => Some((id, cargo.workspace_root())),
+ ProjectWorkspace::Json { project, .. } => {
+ // Enable flychecks for json projects if a custom flycheck command was supplied
+ // in the workspace configuration.
+ match config {
+ FlycheckConfig::CustomCommand { .. } => Some((id, project.path())),
+ _ => None,
+ }
+ }
+ ProjectWorkspace::DetachedFiles { .. } => None,
+ })
+ .map(|(id, root)| {
+ let sender = sender.clone();
+ FlycheckHandle::spawn(
+ id,
+ Box::new(move |msg| sender.send(msg).unwrap()),
+ config.clone(),
+ root.to_path_buf(),
+ )
+ })
+ .collect()
+ }
+ }
+ .into();
}
}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
index e083b9d0e..5936454a7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/to_proto.rs
@@ -83,10 +83,11 @@ pub(crate) fn structure_node_kind(kind: StructureNodeKind) -> lsp_types::SymbolK
pub(crate) fn document_highlight_kind(
category: ReferenceCategory,
-) -> lsp_types::DocumentHighlightKind {
+) -> Option<lsp_types::DocumentHighlightKind> {
match category {
- ReferenceCategory::Read => lsp_types::DocumentHighlightKind::READ,
- ReferenceCategory::Write => lsp_types::DocumentHighlightKind::WRITE,
+ ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
+ ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
+ ReferenceCategory::Import => None,
}
}
@@ -1176,13 +1177,13 @@ pub(crate) fn code_lens(
})
}
}
- AnnotationKind::HasImpls { file_id, data } => {
+ AnnotationKind::HasImpls { pos: file_range, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_id)?;
+ let line_index = snap.file_line_index(file_range.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_id);
+ let url = url(snap, file_range.file_id);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
@@ -1220,13 +1221,13 @@ pub(crate) fn code_lens(
data: Some(to_value(lsp_ext::CodeLensResolveData::Impls(goto_params)).unwrap()),
})
}
- AnnotationKind::HasReferences { file_id, data } => {
+ AnnotationKind::HasReferences { pos: file_range, data } => {
if !client_commands_config.show_reference {
return Ok(());
}
- let line_index = snap.file_line_index(file_id)?;
+ let line_index = snap.file_line_index(file_range.file_id)?;
let annotation_range = range(&line_index, annotation.range);
- let url = url(snap, file_id);
+ let url = url(snap, file_range.file_id);
let id = lsp_types::TextDocumentIdentifier { uri: url.clone() };
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 4cc46af1b..fa55f7d90 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
@@ -18,7 +18,6 @@ mod tidy;
use std::{collections::HashMap, path::PathBuf, time::Instant};
-use expect_test::expect;
use lsp_types::{
notification::DidOpenTextDocument,
request::{
@@ -60,7 +59,7 @@ use std::collections::Spam;
"#,
)
.with_config(serde_json::json!({
- "cargo": { "noSysroot": false }
+ "cargo": { "sysroot": "discover" }
}))
.server()
.wait_until_workspace_is_loaded();
@@ -615,7 +614,7 @@ fn main() {{}}
librs, libs
))
.with_config(serde_json::json!({
- "cargo": { "noSysroot": false }
+ "cargo": { "sysroot": "discover" }
}))
.server()
.wait_until_workspace_is_loaded();
@@ -743,7 +742,7 @@ fn main() {
"buildScripts": {
"enable": true
},
- "noSysroot": true,
+ "sysroot": null,
}
}))
.server()
@@ -821,7 +820,10 @@ fn main() {
}
#[test]
+// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again
+#[cfg(any())]
fn resolve_proc_macro() {
+ use expect_test::expect;
if skip_slow_tests() {
return;
}
@@ -898,7 +900,7 @@ pub fn foo(_input: TokenStream) -> TokenStream {
"buildScripts": {
"enable": true
},
- "noSysroot": true,
+ "sysroot": null,
},
"procMacro": {
"enable": true,
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 4fa88c3c6..7257445da 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
@@ -34,7 +34,7 @@ impl<'a> Project<'a> {
config: serde_json::json!({
"cargo": {
// Loading standard library is costly, let's ignore it by default
- "noSysroot": true,
+ "sysroot": null,
// Can't use test binary as rustc wrapper.
"buildScripts": {
"useRustcWrapper": false
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/tidy.rs
index 58099a58d..24e68eca6 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
@@ -177,6 +177,7 @@ fn check_licenses() {
let sh = &Shell::new().unwrap();
let expected = "
+(MIT OR Apache-2.0) AND Unicode-DFS-2016
0BSD OR MIT OR Apache-2.0
Apache-2.0
Apache-2.0 OR BSL-1.0
diff --git a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
index a84110d94..e75867e2d 100644
--- a/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/sourcegen/Cargo.toml
@@ -11,6 +11,3 @@ doctest = false
[dependencies]
xshell = "0.2.2"
-
-[features]
-in-rust-tree = []
diff --git a/src/tools/rust-analyzer/crates/stdx/Cargo.toml b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
index 092b99ae5..e0657ab0f 100644
--- a/src/tools/rust-analyzer/crates/stdx/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/stdx/Cargo.toml
@@ -10,7 +10,7 @@ rust-version = "1.57"
doctest = false
[dependencies]
-libc = "0.2.126"
+libc = "0.2.135"
backtrace = { version = "0.3.65", optional = true }
always-assert = { version = "0.1.2", features = ["log"] }
# Think twice before adding anything here
diff --git a/src/tools/rust-analyzer/crates/syntax/Cargo.toml b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
index 0e2dec386..1ef903371 100644
--- a/src/tools/rust-analyzer/crates/syntax/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/syntax/Cargo.toml
@@ -12,11 +12,11 @@ doctest = false
[dependencies]
cov-mark = "2.0.0-pre.1"
-itertools = "0.10.3"
-rowan = "0.15.8"
+itertools = "0.10.5"
+rowan = "0.15.10"
rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" }
rustc-hash = "1.1.0"
-once_cell = "1.12.0"
+once_cell = "1.15.0"
indexmap = "1.9.1"
smol_str = "0.1.23"
@@ -28,7 +28,7 @@ profile = { path = "../profile", version = "0.0.0" }
[dev-dependencies]
rayon = "1.5.3"
expect-test = "1.4.0"
-proc-macro2 = "1.0.39"
+proc-macro2 = "1.0.47"
quote = "1.0.20"
ungrammar = "1.16.1"
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index eadebbe8a..660c057e9 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -235,6 +235,24 @@ impl ast::GenericParamList {
}
}
}
+
+ /// Constructs a matching [`ast::GenericArgList`]
+ pub fn to_generic_args(&self) -> ast::GenericArgList {
+ let args = self.generic_params().filter_map(|param| match param {
+ ast::GenericParam::LifetimeParam(it) => {
+ Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
+ }
+ ast::GenericParam::TypeParam(it) => {
+ Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
+ }
+ ast::GenericParam::ConstParam(it) => {
+ // Name-only const params get parsed as `TypeArg`s
+ Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
+ }
+ });
+
+ make::generic_arg_list(args)
+ }
}
impl ast::WhereClause {
@@ -248,6 +266,42 @@ impl ast::WhereClause {
}
}
+impl ast::TypeParam {
+ pub fn remove_default(&self) {
+ if let Some((eq, last)) = self
+ .syntax()
+ .children_with_tokens()
+ .find(|it| it.kind() == T![=])
+ .zip(self.syntax().last_child_or_token())
+ {
+ ted::remove_all(eq..=last);
+
+ // remove any trailing ws
+ if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
+ last.detach();
+ }
+ }
+ }
+}
+
+impl ast::ConstParam {
+ pub fn remove_default(&self) {
+ if let Some((eq, last)) = self
+ .syntax()
+ .children_with_tokens()
+ .find(|it| it.kind() == T![=])
+ .zip(self.syntax().last_child_or_token())
+ {
+ ted::remove_all(eq..=last);
+
+ // remove any trailing ws
+ if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
+ last.detach();
+ }
+ }
+ }
+}
+
pub trait Removable: AstNode {
fn remove(&self);
}
@@ -264,7 +318,7 @@ impl Removable for ast::TypeBoundList {
impl ast::PathSegment {
pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
if self.generic_arg_list().is_none() {
- let arg_list = make::generic_arg_list().clone_for_update();
+ let arg_list = make::generic_arg_list(empty()).clone_for_update();
ted::append_child(self.syntax(), arg_list.syntax());
}
self.generic_arg_list().unwrap()
@@ -591,7 +645,7 @@ impl ast::RecordPatFieldList {
}
fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
- let comma = match syntax
+ match syntax
.siblings_with_tokens(Direction::Next)
.filter_map(|it| it.into_token())
.find(|it| it.kind() == T![,])
@@ -602,8 +656,7 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
ted::insert(Position::after(syntax), &comma);
comma
}
- };
- comma
+ }
}
impl ast::StmtList {
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 83f8bbac5..4057a75e7 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -88,6 +88,9 @@ pub mod ext {
block_expr(None, None)
}
+ pub fn ty_name(name: ast::Name) -> ast::Type {
+ ty_path(ident_path(&name.to_string()))
+ }
pub fn ty_bool() -> ast::Type {
ty_path(ident_path("bool"))
}
@@ -160,6 +163,7 @@ pub fn assoc_item_list() -> ast::AssocItemList {
ast_from_text("impl C for D {}")
}
+// FIXME: `ty_params` should be `ast::GenericArgList`
pub fn impl_(
ty: ast::Path,
params: Option<ast::GenericParamList>,
@@ -185,10 +189,6 @@ pub fn impl_trait(
ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
}
-pub(crate) fn generic_arg_list() -> ast::GenericArgList {
- ast_from_text("const S: T<> = ();")
-}
-
pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
ast_from_text(&format!("type __ = {name_ref};"))
}
@@ -718,6 +718,21 @@ pub fn generic_param_list(
ast_from_text(&format!("fn f<{args}>() {{ }}"))
}
+pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
+ ast_from_text(&format!("const S: T<{ty}> = ();"))
+}
+
+pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
+ ast_from_text(&format!("const S: T<{lifetime}> = ();"))
+}
+
+pub(crate) fn generic_arg_list(
+ args: impl IntoIterator<Item = ast::GenericArg>,
+) -> ast::GenericArgList {
+ let args = args.into_iter().join(", ");
+ ast_from_text(&format!("const S: T<{args}> = ();"))
+}
+
pub fn visibility_pub_crate() -> ast::Visibility {
ast_from_text("pub(crate) struct S")
}
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 bb92c51e9..fe82aa907 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
@@ -873,3 +873,33 @@ impl ast::MatchGuard {
support::child(&self.syntax)
}
}
+
+impl From<ast::Item> for ast::AnyHasAttrs {
+ fn from(node: ast::Item) -> Self {
+ Self::new(node)
+ }
+}
+
+impl From<ast::AssocItem> for ast::AnyHasAttrs {
+ fn from(node: ast::AssocItem) -> Self {
+ Self::new(node)
+ }
+}
+
+impl From<ast::Variant> for ast::AnyHasAttrs {
+ fn from(node: ast::Variant) -> Self {
+ Self::new(node)
+ }
+}
+
+impl From<ast::RecordField> for ast::AnyHasAttrs {
+ fn from(node: ast::RecordField) -> Self {
+ Self::new(node)
+ }
+}
+
+impl From<ast::TupleField> for ast::AnyHasAttrs {
+ fn from(node: ast::TupleField) -> Self {
+ Self::new(node)
+ }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
index 4f5e273a5..84c66b27e 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs
@@ -92,7 +92,7 @@ impl<T> Parse<T> {
SyntaxNode::new_root(self.green.clone())
}
pub fn errors(&self) -> &[SyntaxError] {
- &*self.errors
+ &self.errors
}
}
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 8c806e792..c824f5af7 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -61,6 +61,8 @@
//! "
//! ```
+use std::iter;
+
use rustc_hash::FxHashMap;
use stdx::trim_indent;
@@ -259,7 +261,7 @@ impl MiniCore {
if res.has_flag(entry) {
panic!("duplicate minicore flag: {:?}", entry);
}
- res.activated_flags.push(entry.to_string());
+ res.activated_flags.push(entry.to_owned());
}
res
@@ -273,35 +275,34 @@ impl MiniCore {
let raw_mini_core = include_str!("./minicore.rs");
let mut lines = raw_mini_core.split_inclusive('\n');
- let mut parsing_flags = false;
let mut implications = Vec::new();
// Parse `//!` preamble and extract flags and dependencies.
- for line in lines.by_ref() {
- let line = match line.strip_prefix("//!") {
- Some(it) => it,
- None => {
- assert!(line.trim().is_empty());
- break;
- }
- };
-
- if parsing_flags {
- let (flag, deps) = line.split_once(':').unwrap();
- let flag = flag.trim();
- self.valid_flags.push(flag.to_string());
- for dep in deps.split(", ") {
- let dep = dep.trim();
- if !dep.is_empty() {
- self.assert_valid_flag(dep);
- implications.push((flag, dep));
- }
- }
+ let trim_doc: fn(&str) -> Option<&str> = |line| match line.strip_prefix("//!") {
+ Some(it) => Some(it),
+ None => {
+ assert!(line.trim().is_empty(), "expected empty line after minicore header");
+ None
}
+ };
+ for line in lines
+ .by_ref()
+ .map_while(trim_doc)
+ .skip_while(|line| !line.contains("Available flags:"))
+ .skip(1)
+ {
+ let (flag, deps) = line.split_once(':').unwrap();
+ let flag = flag.trim();
+
+ self.valid_flags.push(flag.to_string());
+ implications.extend(
+ iter::repeat(flag)
+ .zip(deps.split(", ").map(str::trim).filter(|dep| !dep.is_empty())),
+ );
+ }
- if line.contains("Available flags:") {
- parsing_flags = true;
- }
+ for (_, dep) in &implications {
+ self.assert_valid_flag(dep);
}
for flag in &self.activated_flags {
@@ -332,7 +333,7 @@ impl MiniCore {
}
if let Some(region) = trimmed.strip_prefix("// endregion:") {
let prev = active_regions.pop().unwrap();
- assert_eq!(prev, region);
+ assert_eq!(prev, region, "unbalanced region pairs");
continue;
}
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 6df29db47..69d2e62b2 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -8,35 +8,36 @@
//! We then strip all the code marked with other flags.
//!
//! Available flags:
-//! sized:
-//! unsize: sized
+//! add:
+//! as_ref: sized
+//! bool_impl: option, fn
+//! clone: sized
//! coerce_unsized: unsize
-//! slice:
-//! range:
-//! deref: sized
+//! copy: clone
+//! default: sized
//! deref_mut: deref
-//! index: sized
+//! deref: sized
+//! derive:
+//! drop:
+//! eq: sized
+//! fmt: result
//! fn:
-//! try:
-//! pin:
+//! from: sized
//! future: pin
-//! option:
-//! result:
+//! generator: pin
+//! hash:
+//! index: sized
//! iterator: option
//! iterators: iterator, fn
-//! default: sized
-//! hash:
-//! clone: sized
-//! copy: clone
-//! from: sized
-//! eq: sized
+//! option:
//! ord: eq, option
-//! derive:
-//! fmt: result
-//! bool_impl: option, fn
-//! add:
-//! as_ref: sized
-//! drop:
+//! pin:
+//! range:
+//! result:
+//! sized:
+//! slice:
+//! try:
+//! unsize: sized
pub mod marker {
// region:sized
@@ -182,6 +183,19 @@ pub mod ops {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
+
+ impl<T: ?Sized> Deref for &T {
+ type Target = T;
+ fn deref(&self) -> &T {
+ loop {}
+ }
+ }
+ impl<T: ?Sized> Deref for &mut T {
+ type Target = T;
+ fn deref(&self) -> &T {
+ loop {}
+ }
+ }
// region:deref_mut
#[lang = "deref_mut"]
pub trait DerefMut: Deref {
@@ -347,6 +361,27 @@ pub mod ops {
fn add(self, rhs: Rhs) -> Self::Output;
}
// endregion:add
+
+ // region:generator
+ mod generator {
+ use crate::pin::Pin;
+
+ #[lang = "generator"]
+ pub trait Generator<R = ()> {
+ type Yield;
+ #[lang = "generator_return"]
+ type Return;
+ fn resume(self: Pin<&mut Self>, arg: R) -> GeneratorState<Self::Yield, Self::Return>;
+ }
+
+ #[lang = "generator_state"]
+ pub enum GeneratorState<Y, R> {
+ Yielded(Y),
+ Complete(R),
+ }
+ }
+ pub use self::generator::{Generator, GeneratorState};
+ // endregion:generator
}
// region:eq
@@ -455,6 +490,19 @@ pub mod pin {
pub struct Pin<P> {
pointer: P,
}
+ impl<P> Pin<P> {
+ pub fn new(pointer: P) -> Pin<P> {
+ loop {}
+ }
+ }
+ // region:deref
+ impl<P: crate::ops::Deref> crate::ops::Deref for Pin<P> {
+ type Target = P::Target;
+ fn deref(&self) -> &P::Target {
+ loop {}
+ }
+ }
+ // endregion:deref
}
// endregion:pin
@@ -536,7 +584,7 @@ pub mod iter {
}
}
}
- pub use self::adapters::{Take, FilterMap};
+ pub use self::adapters::{FilterMap, Take};
mod sources {
mod repeat {
diff --git a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
index cf14bbd3c..7a90d64a9 100644
--- a/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/text-edit/Cargo.toml
@@ -10,5 +10,5 @@ rust-version = "1.57"
doctest = false
[dependencies]
-itertools = "0.10.3"
+itertools = "0.10.5"
text-size = "1.1.0"
diff --git a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
index 7d3b9e09e..3e0f31f19 100644
--- a/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/toolchain/Cargo.toml
@@ -10,4 +10,4 @@ rust-version = "1.57"
doctest = false
[dependencies]
-home = "0.5.3"
+home = "0.5.4"
diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index fcc693a7d..df5dc24e2 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -14,7 +14,7 @@ tracing = "0.1.35"
jod-thread = "0.1.2"
walkdir = "2.3.2"
crossbeam-channel = "0.5.5"
-notify = "=5.0.0-pre.16"
+notify = "5.0"
vfs = { path = "../vfs", version = "0.0.0" }
paths = { path = "../paths", version = "0.0.0" }
diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md
index c7f152acc..4ac75b4bb 100644
--- a/src/tools/rust-analyzer/docs/dev/README.md
+++ b/src/tools/rust-analyzer/docs/dev/README.md
@@ -98,7 +98,7 @@ After I am done with the fix, I use `cargo xtask install --client` to try the ne
If I need to fix something in the `rust-analyzer` crate, I feel sad because it's on the boundary between the two processes, and working there is slow.
I usually just `cargo xtask install --server` and poke changes from my live environment.
Note that this uses `--release`, which is usually faster overall, because loading stdlib into debug version of rust-analyzer takes a lot of time.
-To speed things up, sometimes I open a temporary hello-world project which has `"rust-analyzer.cargo.noSysroot": true` in `.code/settings.json`.
+To speed things up, sometimes I open a temporary hello-world project which has `"rust-analyzer.cargo.sysroot": null` in `.code/settings.json`.
This flag causes rust-analyzer to skip loading the sysroot, which greatly reduces the amount of things rust-analyzer needs to do, and makes printf's more useful.
Note that you should only use the `eprint!` family of macros for debugging: stdout is used for LSP communication, and `print!` would break it.
diff --git a/src/tools/rust-analyzer/docs/dev/guide.md b/src/tools/rust-analyzer/docs/dev/guide.md
index 808eb5d10..52a13da31 100644
--- a/src/tools/rust-analyzer/docs/dev/guide.md
+++ b/src/tools/rust-analyzer/docs/dev/guide.md
@@ -40,8 +40,8 @@ terms of files and offsets, and **not** in terms of Rust concepts like structs,
traits, etc. The "typed" API with Rust specific types is slightly lower in the
stack, we'll talk about it later.
-[`AnalysisHost`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L265-L284
-[`Analysis`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L291-L478
+[`AnalysisHost`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L265-L284
+[`Analysis`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L291-L478
The reason for this separation of `Analysis` and `AnalysisHost` is that we want to apply
changes "uniquely", but we might also want to fork an `Analysis` and send it to
@@ -88,9 +88,8 @@ is lower than Cargo's model of packages: each Cargo package consists of several
targets, each of which is a separate crate (or several crates, if you try
different feature combinations).
-Procedural macros should become inputs as well, but currently they are not
-supported. Procedural macro will be a black box `Box<dyn Fn(TokenStream) -> TokenStream>`
-function, and will be inserted into the crate graph just like dependencies.
+Procedural macros are inputs as well, roughly modeled as a crate with a bunch of
+additional black box `dyn Fn(TokenStream) -> TokenStream` functions.
Soon we'll talk how we build an LSP server on top of `Analysis`, but first,
let's deal with that paths issue.
diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md
index 30e137013..97e376787 100644
--- a/src/tools/rust-analyzer/docs/dev/syntax.md
+++ b/src/tools/rust-analyzer/docs/dev/syntax.md
@@ -8,10 +8,10 @@ This guide describes the current state of syntax trees and parsing in rust-analy
The things described are implemented in three places
-* [rowan](https://github.com/rust-analyzer/rowan/tree/v0.9.0) -- a generic library for rowan syntax trees.
-* [ra_syntax](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/ra_syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API.
+* [rowan](https://github.com/rust-analyzer/rowan/tree/v0.15.10) -- a generic library for rowan syntax trees.
+* [syntax](https://github.com/rust-lang/rust-analyzer/tree/36a70b7435c48837018c71576d7bb4e8f763f501/crates/syntax) crate inside rust-analyzer which wraps `rowan` into rust-analyzer specific API.
Nothing in rust-analyzer except this crate knows about `rowan`.
-* [parser](https://github.com/rust-lang/rust-analyzer/tree/cf5bdf464cad7ceb9a67e07985a3f4d3799ec0b6/crates/parser) crate parses input tokens into an `ra_syntax` tree
+* [parser](https://github.com/rust-lang/rust-analyzer/tree/36a70b7435c48837018c71576d7bb4e8f763f501/crates/parser) crate parses input tokens into a `syntax` tree
## Design Goals
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 72b925726..502833de7 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -24,6 +24,25 @@ Automatically refresh project info via `cargo metadata` on
--
Run build scripts (`build.rs`) for more precise code analysis.
--
+[[rust-analyzer.cargo.buildScripts.invocationLocation]]rust-analyzer.cargo.buildScripts.invocationLocation (default: `"workspace"`)::
++
+--
+Specifies the working directory for running build scripts.
+- "workspace": run build scripts for a workspace in the workspace's root directory.
+ This is incompatible with `#rust-analyzer.cargo.buildScripts.invocationStrategy#` set to `once`.
+- "root": run build scripts in the project's root directory.
+This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#`
+is set.
+--
+[[rust-analyzer.cargo.buildScripts.invocationStrategy]]rust-analyzer.cargo.buildScripts.invocationStrategy (default: `"per_workspace"`)::
++
+--
+Specifies the invocation strategy to use when running the build scripts 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#`
+is set.
+--
[[rust-analyzer.cargo.buildScripts.overrideCommand]]rust-analyzer.cargo.buildScripts.overrideCommand (default: `null`)::
+
--
@@ -46,6 +65,12 @@ cargo check --quiet --workspace --message-format=json --all-targets
Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to
avoid checking unnecessary things.
--
+[[rust-analyzer.cargo.extraEnv]]rust-analyzer.cargo.extraEnv (default: `{}`)::
++
+--
+Extra environment variables that will be set when running cargo, rustc
+or other commands within the workspace. Useful for setting RUSTFLAGS.
+--
[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
+
--
@@ -58,10 +83,15 @@ Set this to `"all"` to pass `--all-features` to cargo.
--
Whether to pass `--no-default-features` to cargo.
--
-[[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`)::
+[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`)::
+
--
-Internal config for debugging, disables loading of sysroot crates.
+Relative path to the sysroot, or "discover" to try to automatically find it via
+"rustc --print sysroot".
+
+Unsetting this disables sysroot loading.
+
+This option does not take effect until rust-analyzer is restarted.
--
[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`)::
+
@@ -93,6 +123,12 @@ Run specified `cargo check` command for diagnostics on save.
--
Extra arguments for `cargo check`.
--
+[[rust-analyzer.checkOnSave.extraEnv]]rust-analyzer.checkOnSave.extraEnv (default: `{}`)::
++
+--
+Extra environment variables that will be set when running `cargo check`.
+Extends `#rust-analyzer.cargo.extraEnv#`.
+--
[[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`)::
+
--
@@ -101,6 +137,25 @@ List of features to activate. Defaults to
Set to `"all"` to pass `--all-features` to Cargo.
--
+[[rust-analyzer.checkOnSave.invocationLocation]]rust-analyzer.checkOnSave.invocationLocation (default: `"workspace"`)::
++
+--
+Specifies the working directory for running checks.
+- "workspace": run checks for workspaces in the corresponding workspaces' root directories.
+ This falls back to "root" if `#rust-analyzer.cargo.checkOnSave.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#`
+is set.
+--
+[[rust-analyzer.checkOnSave.invocationStrategy]]rust-analyzer.checkOnSave.invocationStrategy (default: `"per_workspace"`)::
++
+--
+Specifies the invocation strategy to use when running the checkOnSave 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#`
+is set.
+--
[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
+
--
@@ -353,6 +408,11 @@ Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-i
--
Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`.
--
+[[rust-analyzer.imports.prefer.no.std]]rust-analyzer.imports.prefer.no.std (default: `false`)::
++
+--
+Prefer to unconditionally use imports of the core and alloc crate, over the std crate.
+--
[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`)::
+
--
@@ -474,6 +534,11 @@ client doesn't set the corresponding capability.
Whether to show `Implementations` lens. Only applies when
`#rust-analyzer.lens.enable#` is set.
--
+[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`)::
++
+--
+Where to render annotations.
+--
[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`)::
+
--
@@ -546,6 +611,11 @@ This config takes a map of crate names with the exported proc-macro names to ign
Internal config, path to proc-macro server executable (typically,
this is rust-analyzer itself, but we override this in tests).
--
+[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`)::
++
+--
+Exclude imports from find-all-references.
+--
[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`)::
+
--
diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc
index 9bd3b6a69..c30838e5f 100644
--- a/src/tools/rust-analyzer/docs/user/manual.adoc
+++ b/src/tools/rust-analyzer/docs/user/manual.adoc
@@ -174,14 +174,25 @@ On Unix, running the editor from a shell or changing the `.desktop` file to set
==== `rustup`
-`rust-analyzer` is available in `rustup`, but only in the nightly toolchain:
+`rust-analyzer` is available in `rustup`:
[source,bash]
----
-$ rustup +nightly component add rust-analyzer-preview
+$ rustup component add rust-analyzer
----
-However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue].
+However, in contrast to `component add clippy` or `component add rustfmt`, this does not actually place a `rust-analyzer` binary in `~/.cargo/bin`, see https://github.com/rust-lang/rustup/issues/2411[this issue]. You can find the path to the binary using:
+[source,bash]
+----
+$ rustup which --toolchain stable rust-analyzer
+----
+You can link to there from `~/.cargo/bin` or configure your editor to use the full path.
+
+Alternatively you might be able to configure your editor to start `rust-analyzer` using the command:
+[source,bash]
+----
+$ rustup run stable rust-analyzer
+----
==== Arch Linux
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index 204d120d0..5922bbfdb 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "lsp-server"
-version = "0.6.0"
+version = "0.7.0"
description = "Generic LSP server scaffold."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"
@@ -8,9 +8,9 @@ edition = "2021"
[dependencies]
log = "0.4.17"
-serde_json = "1.0.81"
-serde = { version = "1.0.137", features = ["derive"] }
-crossbeam-channel = "0.5.5"
+serde_json = "1.0.86"
+serde = { version = "1.0.144", features = ["derive"] }
+crossbeam-channel = "0.5.6"
[dev-dependencies]
-lsp-types = "0.93.0"
+lsp-types = "0.93.1"
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
index 97e5bd35c..b241561f9 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs
@@ -98,7 +98,7 @@ pub struct ResponseError {
}
#[derive(Clone, Copy, Debug)]
-#[allow(unused)]
+#[non_exhaustive]
pub enum ErrorCode {
// Defined by JSON RPC:
ParseError = -32700,
@@ -135,6 +135,14 @@ pub enum ErrorCode {
///
/// @since 3.17.0
ServerCancelled = -32802,
+
+ /// A request failed but it was syntactically correct, e.g the
+ /// method name was known and the parameters were valid. The error
+ /// message should contain human readable information about why
+ /// the request failed.
+ ///
+ /// @since 3.17.0
+ RequestFailed = -32803,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs
index 1f3d44715..e5f19be20 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/req_queue.rs
@@ -35,6 +35,7 @@ impl<I> Incoming<I> {
pub fn register(&mut self, id: RequestId, data: I) {
self.pending.insert(id, data);
}
+
pub fn cancel(&mut self, id: RequestId) -> Option<Response> {
let _data = self.complete(id.clone())?;
let error = ResponseError {
@@ -44,9 +45,14 @@ impl<I> Incoming<I> {
};
Some(Response { id, result: None, error: Some(error) })
}
+
pub fn complete(&mut self, id: RequestId) -> Option<I> {
self.pending.remove(&id)
}
+
+ pub fn is_completed(&self, id: &RequestId) -> bool {
+ !self.pending.contains_key(id)
+ }
}
impl<O> Outgoing<O> {
@@ -56,6 +62,7 @@ impl<O> Outgoing<O> {
self.next_id += 1;
Request::new(id, method, params)
}
+
pub fn complete(&mut self, id: RequestId) -> Option<O> {
self.pending.remove(&id)
}
diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml
index 95d44e9b9..0be0bf920 100644
--- a/src/tools/rust-analyzer/xtask/Cargo.toml
+++ b/src/tools/rust-analyzer/xtask/Cargo.toml
@@ -7,9 +7,9 @@ edition = "2021"
rust-version = "1.57"
[dependencies]
-anyhow = "1.0.57"
+anyhow = "1.0.62"
flate2 = "1.0.24"
write-json = "0.1.2"
xshell = "0.2.2"
-xflags = "0.2.4"
+xflags = "0.3.0"
# Avoid adding more dependencies to this crate
diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs
index 993c64cce..0fce48898 100644
--- a/src/tools/rust-analyzer/xtask/src/flags.rs
+++ b/src/tools/rust-analyzer/xtask/src/flags.rs
@@ -7,10 +7,6 @@ xflags::xflags! {
/// Run custom build command.
cmd xtask {
- default cmd help {
- /// Print help information.
- optional -h, --help
- }
/// Install rust-analyzer server or editor plugin.
cmd install {
@@ -42,9 +38,9 @@ xflags::xflags! {
optional --dry-run
}
/// Builds a benchmark version of rust-analyzer and puts it into `./target`.
- cmd bb
+ cmd bb {
required suffix: String
- {}
+ }
}
}
@@ -58,7 +54,6 @@ pub struct Xtask {
#[derive(Debug)]
pub enum XtaskCmd {
- Help(Help),
Install(Install),
FuzzTests(FuzzTests),
Release(Release),
@@ -69,11 +64,6 @@ pub enum XtaskCmd {
}
#[derive(Debug)]
-pub struct Help {
- pub help: bool,
-}
-
-#[derive(Debug)]
pub struct Install {
pub client: bool,
pub code_bin: Option<String>,
@@ -111,7 +101,10 @@ pub struct Bb {
}
impl Xtask {
- pub const HELP: &'static str = Self::HELP_;
+ #[allow(dead_code)]
+ pub fn from_env_or_exit() -> Self {
+ Self::from_env_or_exit_()
+ }
#[allow(dead_code)]
pub fn from_env() -> xflags::Result<Self> {
diff --git a/src/tools/rust-analyzer/xtask/src/main.rs b/src/tools/rust-analyzer/xtask/src/main.rs
index 335ac324a..a37f469ad 100644
--- a/src/tools/rust-analyzer/xtask/src/main.rs
+++ b/src/tools/rust-analyzer/xtask/src/main.rs
@@ -25,15 +25,12 @@ use std::{
use xshell::{cmd, Shell};
fn main() -> anyhow::Result<()> {
+ let flags = flags::Xtask::from_env_or_exit();
+
let sh = &Shell::new()?;
sh.change_dir(project_root());
- let flags = flags::Xtask::from_env()?;
match flags.subcommand {
- flags::XtaskCmd::Help(_) => {
- println!("{}", flags::Xtask::HELP);
- Ok(())
- }
flags::XtaskCmd::Install(cmd) => cmd.run(sh),
flags::XtaskCmd::FuzzTests(_) => run_fuzzer(sh),
flags::XtaskCmd::Release(cmd) => cmd.run(sh),