From a8f54712da1ea9bec0866bf6a813b72d06676f21 Mon Sep 17 00:00:00 2001 From: Vishruth-Thimmaiah <vishruththimmaiah@gmail.com> Date: Thu, 16 Jan 2025 16:52:11 +0530 Subject: [PATCH 01/75] fix: upmap ranges in convert_tuple_struct_to_named_struct assist fixes #18766 --- .../convert_tuple_struct_to_named_struct.rs | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 3c84f83906a9b..f6e516db88835 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -140,8 +140,10 @@ fn edit_struct_references( match_ast! { match node { ast::TupleStructPat(tuple_struct_pat) => { + let file_range = ctx.sema.original_range_opt(&node)?; + edit.edit_file(file_range.file_id); edit.replace( - tuple_struct_pat.syntax().text_range(), + file_range.range, ast::make::record_pat_with_fields( tuple_struct_pat.path()?, ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( @@ -921,6 +923,104 @@ pub struct $0Foo(#[my_custom_attr] u32); "#, r#" pub struct Foo { #[my_custom_attr] field1: u32 } +"#, + ); + } + + #[test] + fn convert_in_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A$0(usize), +} +fn main() { + let e = Expr::A(0); + foo!(e, Expr::A(0)); +} +"#, + r#" +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} +enum Expr { + A { field1: usize }, +} +fn main() { + let e = Expr::A { field1: 0 }; + foo!(e, Expr::A { field1: 0 }); +} +"#, + ); + } + + #[test] + fn convert_in_multi_file_macro_pattern_args() { + check_assist( + convert_tuple_struct_to_named_struct, + r#" +//- /main.rs +mod foo; + +enum Test { + A$0(i32) +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A(0); + foo!(a, Test::A(0)); +} +"#, + r#" +//- /main.rs +mod foo; + +enum Test { + A { field1: i32 } +} + +//- /foo.rs +use crate::Test; + +macro_rules! foo { + ($expression:expr, $pattern:pat) => { + match $expression { + $pattern => true, + _ => false + } + }; +} + +fn foo() { + let a = Test::A { field1: 0 }; + foo!(a, Test::A { field1: 0 }); +} "#, ); } From 1f45995dea280baa6381279221c9c60ff7af342d Mon Sep 17 00:00:00 2001 From: Wilfred Hughes <me@wilfred.me.uk> Date: Thu, 23 Jan 2025 15:44:20 -0800 Subject: [PATCH 02/75] manual: Convert to mdbook Split manual.adoc into markdown files, one for each chapter. For the parts of the manual that are generated from source code doc comments, update the comments to use markdown syntax and update the code generators to write to `generated.md` files. For the weekly release, stop copying the .adoc files to the `rust-analyzer/rust-analyzer.github.io` at release time. Instead, we'll sync the manual hourly from this repository. See https://github.com/rust-analyzer/rust-analyzer.github.io/pull/226 for the sync. This PR should be merged first, and that PR needs to be merged before the next weekly release. This change is based on #15795, but rebased and updated. I've also manually checked each page for markdown syntax issues and fixed any I encountered. Co-authored-by: Lukas Wirth <lukastw97@gmail.com> Co-authored-by: Josh Rotenberg <joshrotenberg@gmail.com> --- src/tools/rust-analyzer/.gitignore | 7 +- .../src/handlers/apply_demorgan.rs | 4 +- .../ide-assists/src/handlers/auto_import.rs | 6 +- .../crates/ide-assists/src/tests/generated.rs | 2 +- .../src/completions/flyimport.rs | 8 +- .../src/completions/postfix/format_like.rs | 2 +- .../crates/ide-completion/src/lib.rs | 8 +- .../crates/ide-completion/src/snippet.rs | 12 +- .../crates/ide-db/src/apply_change.rs | 9 +- .../crates/ide-db/src/symbol_index.rs | 8 +- .../src/handlers/incorrect_case.rs | 2 +- .../rust-analyzer/crates/ide-ssr/src/lib.rs | 18 +- .../crates/ide/src/annotations.rs | 2 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 8 +- .../crates/ide/src/expand_macro.rs | 10 +- .../crates/ide/src/extend_selection.rs | 10 +- .../crates/ide/src/fetch_crates.rs | 10 +- .../crates/ide/src/file_structure.rs | 11 +- .../crates/ide/src/goto_definition.rs | 10 +- .../crates/ide/src/goto_implementation.rs | 10 +- .../crates/ide/src/goto_type_definition.rs | 10 +- .../crates/ide/src/highlight_related.rs | 12 +- .../rust-analyzer/crates/ide/src/hover.rs | 2 +- .../crates/ide/src/inlay_hints.rs | 8 +- .../rust-analyzer/crates/ide/src/interpret.rs | 8 +- .../crates/ide/src/join_lines.rs | 14 +- .../crates/ide/src/matching_brace.rs | 10 +- .../rust-analyzer/crates/ide/src/move_item.rs | 8 +- .../crates/ide/src/parent_module.rs | 10 +- .../crates/ide/src/references.rs | 10 +- .../rust-analyzer/crates/ide/src/rename.rs | 10 +- .../rust-analyzer/crates/ide/src/runnables.rs | 17 +- .../rust-analyzer/crates/ide/src/status.rs | 9 +- .../crates/ide/src/syntax_highlighting.rs | 179 +-- .../rust-analyzer/crates/ide/src/typing.rs | 11 +- .../crates/ide/src/typing/on_enter.rs | 24 +- .../crates/ide/src/view_crate_graph.rs | 8 +- .../rust-analyzer/crates/ide/src/view_hir.rs | 9 +- .../crates/ide/src/view_item_tree.rs | 8 +- .../crates/ide/src/view_memory_layout.rs | 8 +- .../rust-analyzer/crates/ide/src/view_mir.rs | 6 +- .../crates/ide/src/view_syntax_tree.rs | 8 +- .../parser/src/syntax_kind/generated.rs | 2 +- .../crates/rust-analyzer/src/config.rs | 20 +- .../crates/syntax/src/ast/generated/nodes.rs | 2 +- .../crates/syntax/src/ast/generated/tokens.rs | 2 +- src/tools/rust-analyzer/docs/book/README.md | 29 + src/tools/rust-analyzer/docs/book/book.toml | 41 + .../rust-analyzer/docs/book/src/README.md | 21 + .../rust-analyzer/docs/book/src/SUMMARY.md | 13 + .../rust-analyzer/docs/book/src/assists.md | 8 + .../docs/book/src/configuration.md | 51 + .../docs/book/src/configuration_generated.md | 1201 +++++++++++++++++ .../docs/book/src/diagnostics.md | 16 + .../docs/book/src/editor_features.md | 204 +++ .../rust-analyzer/docs/book/src/features.md | 3 + .../docs/book/src/installation.md | 644 +++++++++ .../docs/book/src/non_cargo_based_projects.md | 246 ++++ .../rust-analyzer/docs/book/src/privacy.md | 15 + .../rust-analyzer/docs/book/src/security.md | 19 + .../docs/book/src/troubleshooting.md | 50 + src/tools/rust-analyzer/docs/dev/style.md | 4 +- src/tools/rust-analyzer/docs/user/.gitignore | 1 - .../docs/user/generated_config.adoc | 1197 ---------------- src/tools/rust-analyzer/docs/user/manual.adoc | 1121 --------------- src/tools/rust-analyzer/xtask/src/codegen.rs | 13 +- .../xtask/src/codegen/assists_doc_tests.rs | 10 +- .../xtask/src/codegen/diagnostics_docs.rs | 6 +- .../xtask/src/codegen/feature_docs.rs | 16 +- src/tools/rust-analyzer/xtask/src/release.rs | 21 +- 70 files changed, 2836 insertions(+), 2686 deletions(-) create mode 100644 src/tools/rust-analyzer/docs/book/README.md create mode 100644 src/tools/rust-analyzer/docs/book/book.toml create mode 100644 src/tools/rust-analyzer/docs/book/src/README.md create mode 100644 src/tools/rust-analyzer/docs/book/src/SUMMARY.md create mode 100644 src/tools/rust-analyzer/docs/book/src/assists.md create mode 100644 src/tools/rust-analyzer/docs/book/src/configuration.md create mode 100644 src/tools/rust-analyzer/docs/book/src/configuration_generated.md create mode 100644 src/tools/rust-analyzer/docs/book/src/diagnostics.md create mode 100644 src/tools/rust-analyzer/docs/book/src/editor_features.md create mode 100644 src/tools/rust-analyzer/docs/book/src/features.md create mode 100644 src/tools/rust-analyzer/docs/book/src/installation.md create mode 100644 src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md create mode 100644 src/tools/rust-analyzer/docs/book/src/privacy.md create mode 100644 src/tools/rust-analyzer/docs/book/src/security.md create mode 100644 src/tools/rust-analyzer/docs/book/src/troubleshooting.md delete mode 100644 src/tools/rust-analyzer/docs/user/.gitignore delete mode 100644 src/tools/rust-analyzer/docs/user/generated_config.adoc delete mode 100644 src/tools/rust-analyzer/docs/user/manual.adoc diff --git a/src/tools/rust-analyzer/.gitignore b/src/tools/rust-analyzer/.gitignore index c4470a45078a2..7192e685e29bf 100644 --- a/src/tools/rust-analyzer/.gitignore +++ b/src/tools/rust-analyzer/.gitignore @@ -6,10 +6,11 @@ target/ *.log *.iml .vscode/settings.json -generated_assists.adoc -generated_features.adoc -generated_diagnostic.adoc .DS_Store /out/ /dump.lsif .envrc +docs/book/book +docs/book/src/assists_generated.md +docs/book/src/diagnostics_generated.md +docs/book/src/features_generated.md diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs index 70fb5680052b0..491727a30a88b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -15,7 +15,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // Assist: apply_demorgan // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law]. +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). // This transforms expressions of the form `!l || !r` into `!(l && r)`. // This also works with `&&`. This assist can only be applied with the cursor // on either `||` or `&&`. @@ -131,7 +131,7 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // Assist: apply_demorgan_iterator // -// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law] to +// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws) to // `Iterator::all` and `Iterator::any`. // // This transforms expressions of the form `!iter.any(|x| predicate(x))` into 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 d86948818b163..a92a000c3fbd3 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 @@ -38,7 +38,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // use super::AssistContext; // ``` // -// .Import Granularity +// #### Import Granularity // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // It has the following configurations: @@ -54,7 +54,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.granularity.group`. // -// .Import Prefix +// #### Import Prefix // // The style of imports in the same crate is configurable through the `imports.prefix` setting. // It has the following configurations: @@ -68,7 +68,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; // // In `VS Code` the configuration for this is `rust-analyzer.imports.prefix`. // -// image::https://user-images.githubusercontent.com/48062697/113020673-b85be580-917a-11eb-9022-59585f35d4f8.gif[] +//  // Assist: auto_import // 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 54e42f126bc53..4b0fa704a22ba 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 @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen assists-doc-tests`, do not edit by hand. +//! Generated by `cargo xtask codegen assists-doc-tests`, do not edit by hand. use super::check_doc_test; 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 435b88de4ae6e..9a2b4cd1b4242 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 @@ -83,19 +83,19 @@ use crate::{ // NOTE: currently, if an assoc item comes from a trait that's not currently imported, and it also has an unresolved and/or partially-qualified path, // no imports will be proposed. // -// .Fuzzy search details +// #### Fuzzy search details // // To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only // (i.e. in `HashMap` in the `std::collections::HashMap` path). // For the same reasons, avoids searching for any path imports for inputs with their length less than 2 symbols // (but shows all associated items for any input length). // -// .Import configuration +// #### Import configuration // // It is possible to configure how use-trees are merged with the `imports.granularity.group` setting. // Mimics the corresponding behavior of the `Auto Import` feature. // -// .LSP and performance implications +// #### LSP and performance implications // // The feature is enabled only if the LSP client supports LSP protocol version 3.16+ and reports the `additionalTextEdits` // (case-sensitive) resolve client capability in its client capabilities. @@ -103,7 +103,7 @@ use crate::{ // For clients with no such support, all edits have to be calculated on the completion request, including the fuzzy search completion ones, // which might be slow ergo the feature is automatically disabled. // -// .Feature toggle +// #### Feature toggle // // The feature can be forcefully turned off in the settings with the `rust-analyzer.completion.autoimport.enable` flag. // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corresponding 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 2755329bb31a5..c612170eb54bc 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 @@ -14,7 +14,7 @@ // ** `logw` -> `log::warn!(...)` // ** `loge` -> `log::error!(...)` // -// image::https://user-images.githubusercontent.com/48062697/113020656-b560f500-917a-11eb-87de-02991f61beb8.gif[] +//  use ide_db::{ syntax_helpers::format_string_exprs::{parse_format_exprs, with_placeholders, Arg}, 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 56d7eeaf8ea03..c1b4f54b666fb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -110,11 +110,13 @@ impl CompletionFieldsToResolve { // // There also snippet completions: // -// .Expressions +// #### Expressions +// // - `pd` -> `eprintln!(" = {:?}", );` // - `ppd` -> `eprintln!(" = {:#?}", );` // -// .Items +// #### Items +// // - `tfn` -> `#[test] fn feature(){}` // - `tmod` -> // ```rust @@ -131,7 +133,7 @@ impl CompletionFieldsToResolve { // Those are the additional completion options with automatic `use` import and options from all project importable items, // fuzzy matched against the completion input. // -// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] +//  /// Main entry point for completion. We run completion as a two-phase process. /// 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 04bb178c658f3..e5847b01e1852 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -8,8 +8,7 @@ // // A custom snippet can be defined by adding it to the `rust-analyzer.completion.snippets.custom` object respectively. // -// [source,json] -// ---- +// ```json // { // "rust-analyzer.completion.snippets.custom": { // "thread spawn": { @@ -25,7 +24,7 @@ // } // } // } -// ---- +// ``` // // In the example above: // @@ -39,6 +38,7 @@ // * `description` is an optional description of the snippet, if unset the snippet name will be used. // // * `requires` is an optional list of item paths that have to be resolvable in the current crate where the completion is rendered. + // On failure of resolution the snippet won't be applicable, otherwise the snippet will insert an import for the items on insertion if // the items aren't yet in scope. // @@ -55,8 +55,8 @@ // // For the VSCode editor, rust-analyzer also ships with a small set of defaults which can be removed // by overwriting the settings object mentioned above, the defaults are: -// [source,json] -// ---- +// +// ```json // { // "Arc::new": { // "postfix": "arc", @@ -98,7 +98,7 @@ // "scope": "expr" // } // } -// ---- +// ```` use hir::{ModPath, Name, Symbol}; use ide_db::imports::import_assets::LocatedImport; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs index 35e3a8d9bf7f5..46ff4fbf9e904 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs @@ -44,12 +44,11 @@ impl RootDatabase { // // Clears rust-analyzer's internal database and prints memory usage statistics. // - // |=== - // | Editor | Action Name - // + // | Editor | Action Name | + // |---------|-------------| // | VS Code | **rust-analyzer: Memory Usage (Clears Database)** - // |=== - // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[] + + //  pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes, usize)> { let mut acc: Vec<(String, Bytes, usize)> = vec![]; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index e5ce10a771ef9..bb4c289c9084e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -193,11 +193,9 @@ impl<DB> std::ops::Deref for Snap<DB> { // `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed // with `__` are hidden from the search results unless configured otherwise. // -// |=== -// | Editor | Shortcut -// -// | VS Code | kbd:[Ctrl+T] -// |=== +// | Editor | Shortcut | +// |---------|-----------| +// | VS Code | <kbd>Ctrl+T</kbd> pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index bbdeb7cf08507..0cc80bda2c8a0 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -12,7 +12,7 @@ use crate::{ // Diagnostic: incorrect-ident-case // -// This diagnostic is triggered if an item name doesn't follow https://doc.rust-lang.org/1.0.0/style/style/naming/README.html[Rust naming convention]. +// This diagnostic is triggered if an item name doesn't follow [Rust naming convention](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html). pub(crate) fn incorrect_case(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Diagnostic { let code = match d.expected_case { CaseType::LowerSnakeCase => DiagnosticCode::RustcLint("non_snake_case"), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs index 6b654f8934516..889258c94c535 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/lib.rs @@ -33,12 +33,10 @@ // // Supported constraints: // -// |=== -// | Constraint | Restricts placeholder -// -// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) -// | not(a) | Negates the constraint `a` -// |=== +// | Constraint | Restricts placeholder | +// |---------------|------------------------| +// | kind(literal) | Is a literal (e.g. `42` or `"forty two"`) | +// | not(a) | Negates the constraint `a` | // // Available via the command `rust-analyzer.ssr`. // @@ -52,11 +50,9 @@ // String::from((y + 5).foo(z)) // ``` // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Structural Search Replace** -// |=== +// | Editor | Action Name | +// |---------|--------------| +// | VS Code | **rust-analyzer: Structural Search Replace** | // // Also available as an assist, by writing a comment containing the structural // search and replace rule. You will only see the assist if the comment can diff --git a/src/tools/rust-analyzer/crates/ide/src/annotations.rs b/src/tools/rust-analyzer/crates/ide/src/annotations.rs index 18f866eb9fc96..006e6e8246e1c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/annotations.rs +++ b/src/tools/rust-analyzer/crates/ide/src/annotations.rs @@ -21,7 +21,7 @@ mod fn_references; // Provides user with annotations above items for looking up references or impl blocks // and running/debugging binaries. // -// image::https://user-images.githubusercontent.com/48062697/113020672-b7c34f00-917a-11eb-8f6e-858735660a0e.png[] +//  #[derive(Debug, Hash, PartialEq, Eq)] pub struct Annotation { pub range: TextRange, 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 cfd8919730ad2..e35e47e747188 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -122,11 +122,9 @@ pub(crate) fn remove_links(markdown: &str) -> String { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Open Docs**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Open Docs** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Open Docs** | pub(crate) fn external_docs( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 0ad894427b2c5..ad4308e06a14b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -19,13 +19,11 @@ pub struct ExpandedMacro { // // Shows the full macro expansion of the macro at the current caret position. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Expand macro recursively at caret** | // -// | VS Code | **rust-analyzer: Expand macro recursively at caret** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] +//  pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 3d49082f2858d..76414854e91ef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -17,13 +17,11 @@ use crate::FileRange; // Extends or shrinks the current selection to the encompassing syntactic construct // (expression, statement, item, module, etc). It works with multiple cursors. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Alt+Shift+→</kbd>, <kbd>Alt+Shift+←</kbd> | // -// | VS Code | kbd:[Alt+Shift+→], kbd:[Alt+Shift+←] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020651-b42fc800-917a-11eb-8a4f-cf1a07859fac.gif[] +//  pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { let sema = Semantics::new(db); let src = sema.parse_guess_edition(frange.file_id); diff --git a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs index 37b3cb03b3331..5ed2144430741 100644 --- a/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs +++ b/src/tools/rust-analyzer/crates/ide/src/fetch_crates.rs @@ -14,13 +14,11 @@ pub struct CrateInfo { // // Shows a view tree with all the dependencies of this project // -// |=== -// | Editor | Panel Name +// | Editor | Panel Name | +// |---------|------------| +// | VS Code | **Rust Dependencies** | // -// | VS Code | **Rust Dependencies** -// |=== -// -// image::https://user-images.githubusercontent.com/5748995/229394139-2625beab-f4c9-484b-84ed-ad5dee0b1e1a.png[] +//  pub(crate) fn fetch_crates(db: &RootDatabase) -> FxIndexSet<CrateInfo> { let crate_graph = db.crate_graph(); crate_graph diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs index 50977ee840c82..52fbab6fa12b1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs +++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs @@ -31,14 +31,11 @@ pub enum StructureNodeKind { // * draw breadcrumbs to describe the context around the cursor // * draw outline of the file // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Ctrl+Shift+O</kbd> | // -// | VS Code | kbd:[Ctrl+Shift+O] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020654-b42fc800-917a-11eb-8388-e7dc4d92b02e.gif[] - +//  pub(crate) fn file_structure(file: &SourceFile) -> Vec<StructureNode> { let mut res = Vec::new(); let mut stack = Vec::new(); 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 d18732a6b846b..45f694d2fb6e3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -31,13 +31,11 @@ use syntax::{ // // For outline modules, this will navigate to the source file of the module. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>F12</kbd> | // -// | VS Code | kbd:[F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065563-025fbe00-91b1-11eb-83e4-a5a703610b23.gif[] +//  pub(crate) fn goto_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs index e926378367ece..e1d834b5d1c69 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_implementation.rs @@ -12,13 +12,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the impl items of types. // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Ctrl+F12</kbd> // -// | VS Code | kbd:[Ctrl+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[] +//  pub(crate) fn goto_implementation( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs index 2610d6c8863a0..ddc274a830352 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs @@ -8,13 +8,11 @@ use crate::{FilePosition, NavigationTarget, RangeInfo, TryToNav}; // // Navigates to the type of an identifier. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **Go to Type Definition** | // -// | VS Code | **Go to Type Definition** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[] +//  pub(crate) fn goto_type_definition( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, 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 612bc36f62846..6463206596af5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -43,12 +43,12 @@ pub struct HighlightRelatedConfig { // // Highlights constructs related to the thing under the cursor: // -// . if on an identifier, highlights all references to that identifier in the current file -// .. additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope -// . if on an `async` or `await` token, highlights all yield points for that async context -// . if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context -// . if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context -// . if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. +// 1. if on an identifier, highlights all references to that identifier in the current file +// * additionally, if the identifier is a trait in a where clause, type parameter trait bound or use item, highlights all references to that trait's assoc items in the corresponding scope +// 1. if on an `async` or `await` token, highlights all yield points for that async context +// 1. if on a `return` or `fn` keyword, `?` character or `->` return type arrow, highlights all exit points for that context +// 1. if on a `break`, `loop`, `while` or `for` token, highlights all break points for that loop or block context +// 1. if on a `move` or `|` token that belongs to a closure, highlights all captures of the closure. // // Note: `?`, `|` and `->` do not currently trigger this behavior in the VSCode editor. pub(crate) fn highlight_related( diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 18a3fed07ece1..c8d0c6a250caa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -118,7 +118,7 @@ pub struct HoverResult { // Shows additional information, like the type of an expression or the documentation for a definition when "focusing" code. // Focusing is usually hovering with a mouse, but can also be triggered with a shortcut. // -// image::https://user-images.githubusercontent.com/48062697/113020658-b5f98b80-917a-11eb-9f88-3dbc27320c95.gif[] +//  pub(crate) fn hover( db: &RootDatabase, frange @ FileRange { file_id, range }: FileRange, 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 1f723c85df7aa..5955876d44681 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -59,7 +59,7 @@ mod range_exclusive; // // Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if // any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// [following criteria](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99) // are met: // // * the parameter name is a suffix of the function's name @@ -68,13 +68,13 @@ mod range_exclusive; // of argument with _ splitting it off // * the parameter name starts with `ra_fixture` // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// [well known name](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200) // in a unary function // * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// [single character](https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201) // in a unary function // -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +//  pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, diff --git a/src/tools/rust-analyzer/crates/ide/src/interpret.rs b/src/tools/rust-analyzer/crates/ide/src/interpret.rs index e0fdc3dd6f97c..ae11072e34b74 100644 --- a/src/tools/rust-analyzer/crates/ide/src/interpret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/interpret.rs @@ -7,11 +7,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode, TextRange}; // Feature: Interpret A Function, Static Or Const. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Interpret** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Interpret** | pub(crate) fn interpret(db: &RootDatabase, position: FilePosition) -> String { match find_and_interpret(db, position) { Some((duration, mut result)) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index e4670177ecf21..ea18a97070c3a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -21,17 +21,13 @@ pub struct JoinLinesConfig { // // Join selected lines into one, smartly fixing up whitespace, trailing commas, and braces. // -// See -// https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif[this gif] -// for the cases handled specially by joined lines. +// See [this gif](https://user-images.githubusercontent.com/1711539/124515923-4504e800-dde9-11eb-8d58-d97945a1a785.gif) for the cases handled specially by joined lines. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Join lines** | // -// | VS Code | **rust-analyzer: Join lines** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[] +//  pub(crate) fn join_lines( config: &JoinLinesConfig, file: &SourceFile, diff --git a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs index 5735615283663..67346ea9cf90f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/matching_brace.rs @@ -9,13 +9,11 @@ use syntax::{ // moves cursor to the matching brace. It uses the actual parser to determine // braces, so it won't confuse generics with comparisons. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Find matching brace** | // -// | VS Code | **rust-analyzer: Find matching brace** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[] +//  pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<TextSize> { const BRACES: &[SyntaxKind] = &[T!['{'], T!['}'], T!['['], T![']'], T!['('], T![')'], T![<], T![>], T![|], T![|]]; diff --git a/src/tools/rust-analyzer/crates/ide/src/move_item.rs b/src/tools/rust-analyzer/crates/ide/src/move_item.rs index b0df9257ba10b..3fb3a788b9182 100644 --- a/src/tools/rust-analyzer/crates/ide/src/move_item.rs +++ b/src/tools/rust-analyzer/crates/ide/src/move_item.rs @@ -17,14 +17,12 @@ pub enum Direction { // // Move item under cursor or selection up and down. // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: Move item up** // | VS Code | **rust-analyzer: Move item down** -// |=== // -// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[] +//  pub(crate) fn move_item( db: &RootDatabase, range: FileRange, 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 7a0c28d925aee..6d82f9b0634b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/parent_module.rs +++ b/src/tools/rust-analyzer/crates/ide/src/parent_module.rs @@ -15,13 +15,11 @@ use crate::NavigationTarget; // // Navigates to the parent module of the current module. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Locate parent module** | // -// | VS Code | **rust-analyzer: Locate parent module** -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[] +//  /// This returns `Vec` because a module may be included from several places. pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index b1079312d3bb0..069818d50e76a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -43,13 +43,11 @@ pub struct Declaration { // // Shows all references of the item at the cursor location // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>Shift+Alt+F12</kbd> | // -// | VS Code | kbd:[Shift+Alt+F12] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113020670-b7c34f00-917a-11eb-8003-370ac5f2b3cb.gif[] +//  pub(crate) fn find_all_refs( sema: &Semantics<'_, RootDatabase>, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 07dfd83c4eb7f..3d7ff5f76a01a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -71,13 +71,11 @@ pub(crate) fn prepare_rename( // // Renames the item below the cursor and all of its references // -// |=== -// | Editor | Shortcut +// | Editor | Shortcut | +// |---------|----------| +// | VS Code | <kbd>F2</kbd> | // -// | VS Code | kbd:[F2] -// |=== -// -// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[] +//  pub(crate) fn rename( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 32edacee51c7d..78c9f2309a0d4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -119,12 +119,11 @@ impl Runnable { // location**. Super useful for repeatedly running just a single test. Do bind this // to a shortcut! // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Run** | // -// | VS Code | **rust-analyzer: Run** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[] +//  pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { let sema = Semantics::new(db); @@ -207,11 +206,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { // The simplest way to use this feature is via the context menu. Right-click on // the selected item. The context menu opens. Select **Peek Related Tests**. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Peek Related Tests** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Peek Related Tests** | pub(crate) fn related_tests( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/status.rs b/src/tools/rust-analyzer/crates/ide/src/status.rs index 9e823daa2bec9..b0022cfac765b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/status.rs +++ b/src/tools/rust-analyzer/crates/ide/src/status.rs @@ -29,12 +29,11 @@ use triomphe::Arc; // // Shows internal statistic about memory usage of rust-analyzer. // -// |=== -// | Editor | Action Name +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Status** | // -// | VS Code | **rust-analyzer: Status** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] +//  pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { let mut buf = String::new(); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f53f0aec09842..f42b520c3dc9c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -76,113 +76,118 @@ pub struct HighlightConfig { // We also give special modifier for `mut` and `&mut` local variables. // // -// .Token Tags +// #### Token Tags // // Rust-analyzer currently emits the following token tags: // // - For items: -// + -// [horizontal] -// attribute:: Emitted for attribute macros. -// enum:: Emitted for enums. -// function:: Emitted for free-standing functions. -// derive:: Emitted for derive macros. -// macro:: Emitted for function-like macros. -// method:: Emitted for associated functions, also knowns as methods. -// namespace:: Emitted for modules. -// struct:: Emitted for structs. -// trait:: Emitted for traits. -// typeAlias:: Emitted for type aliases and `Self` in `impl`s. -// union:: Emitted for unions. +// +// | | | +// |-----------|--------------------------------| +// | attribute | Emitted for attribute macros. | +// |enum| Emitted for enums. | +// |function| Emitted for free-standing functions. | +// |derive| Emitted for derive macros. | +// |macro| Emitted for function-like macros. | +// |method| Emitted for associated functions, also knowns as methods. | +// |namespace| Emitted for modules. | +// |struct| Emitted for structs.| +// |trait| Emitted for traits.| +// |typeAlias| Emitted for type aliases and `Self` in `impl`s.| +// |union| Emitted for unions.| // // - For literals: -// + -// [horizontal] -// boolean:: Emitted for the boolean literals `true` and `false`. -// character:: Emitted for character literals. -// number:: Emitted for numeric literals. -// string:: Emitted for string literals. -// escapeSequence:: Emitted for escaped sequences inside strings like `\n`. -// formatSpecifier:: Emitted for format specifiers `{:?}` in `format!`-like macros. +// +// | | | +// |-----------|--------------------------------| +// | boolean| Emitted for the boolean literals `true` and `false`.| +// | character| Emitted for character literals.| +// | number| Emitted for numeric literals.| +// | string| Emitted for string literals.| +// | escapeSequence| Emitted for escaped sequences inside strings like `\n`.| +// | formatSpecifier| Emitted for format specifiers `{:?}` in `format!`-like macros.| // // - For operators: -// + -// [horizontal] -// operator:: Emitted for general operators. -// arithmetic:: Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`. -// bitwise:: Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`. -// comparison:: Emitted for the comparison operators `>`, `<`, `==`, `>=`, `<=`, `!=`. -// logical:: Emitted for the logical operators `||`, `&&`, `!`. +// +// | | | +// |-----------|--------------------------------| +// |operator| Emitted for general operators.| +// |arithmetic| Emitted for the arithmetic operators `+`, `-`, `*`, `/`, `+=`, `-=`, `*=`, `/=`.| +// |bitwise| Emitted for the bitwise operators `|`, `&`, `!`, `^`, `|=`, `&=`, `^=`.| +// |comparison| Emitted for the comparison oerators `>`, `<`, `==`, `>=`, `<=`, `!=`.| +// |logical| Emitted for the logical operatos `||`, `&&`, `!`.| // // - For punctuation: -// + -// [horizontal] -// punctuation:: Emitted for general punctuation. -// attributeBracket:: Emitted for attribute invocation brackets, that is the `#[` and `]` tokens. -// angle:: Emitted for `<>` angle brackets. -// brace:: Emitted for `{}` braces. -// bracket:: Emitted for `[]` brackets. -// parenthesis:: Emitted for `()` parentheses. -// colon:: Emitted for the `:` token. -// comma:: Emitted for the `,` token. -// dot:: Emitted for the `.` token. -// semi:: Emitted for the `;` token. -// macroBang:: Emitted for the `!` token in macro calls. // -// //- +// | | | +// |-----------|--------------------------------| +// |punctuation| Emitted for general punctuation.| +// |attributeBracket| Emitted for attribute invocation brackets, that is the `#[` and `]` tokens.| +// |angle| Emitted for `<>` angle brackets.| +// |brace| Emitted for `{}` braces.| +// |bracket| Emitted for `[]` brackets.| +// |parenthesis| Emitted for `()` parentheses.| +// |colon| Emitted for the `:` token.| +// |comma| Emitted for the `,` token.| +// |dot| Emitted for the `.` token.| +// |semi| Emitted for the `;` token.| +// |macroBang| Emitted for the `!` token in macro calls.| // -// [horizontal] -// builtinAttribute:: Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example. -// builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. -// comment:: Emitted for comments. -// constParameter:: Emitted for const parameters. -// deriveHelper:: Emitted for derive helper attributes. -// enumMember:: Emitted for enum variants. -// generic:: Emitted for generic tokens that have no mapping. -// keyword:: Emitted for keywords. -// label:: Emitted for labels. -// lifetime:: Emitted for lifetimes. -// parameter:: Emitted for non-self function parameters. -// property:: Emitted for struct and union fields. -// selfKeyword:: Emitted for the self function parameter and self path-specifier. -// selfTypeKeyword:: Emitted for the Self type parameter. -// toolModule:: Emitted for tool modules. -// typeParameter:: Emitted for type parameters. -// unresolvedReference:: Emitted for unresolved references, names that rust-analyzer can't find the definition of. -// variable:: Emitted for locals, constants and statics. +//- // +// | | | +// |-----------|--------------------------------| +// |builtinAttribute| Emitted for names to builtin attributes in attribute path, the `repr` in `#[repr(u8)]` for example.| +// |builtinType| Emitted for builtin types like `u32`, `str` and `f32`.| +// |comment| Emitted for comments.| +// |constParameter| Emitted for const parameters.| +// |deriveHelper| Emitted for derive helper attributes.| +// |enumMember| Emitted for enum variants.| +// |generic| Emitted for generic tokens that have no mapping.| +// |keyword| Emitted for keywords.| +// |label| Emitted for labels.| +// |lifetime| Emitted for lifetimes.| +// |parameter| Emitted for non-self function parameters.| +// |property| Emitted for struct and union fields.| +// |selfKeyword| Emitted for the self function parameter and self path-specifier.| +// |selfTypeKeyword| Emitted for the Self type parameter.| +// |toolModule| Emitted for tool modules.| +// |typeParameter| Emitted for type parameters.| +// |unresolvedReference| Emitted for unresolved references, names that rust-analyzer can't find the definition of.| +// |variable| Emitted for locals, constants and statics.| // -// .Token Modifiers +// +// #### Token Modifiers // // Token modifiers allow to style some elements in the source code more precisely. // // Rust-analyzer currently emits the following token modifiers: // -// [horizontal] -// async:: Emitted for async functions and the `async` and `await` keywords. -// attribute:: Emitted for tokens inside attributes. -// callable:: Emitted for locals whose types implements one of the `Fn*` traits. -// constant:: Emitted for consts. -// consuming:: Emitted for locals that are being consumed when use in a function call. -// controlFlow:: Emitted for control-flow related tokens, this includes the `?` operator. -// crateRoot:: Emitted for crate names, like `serde` and `crate`. -// declaration:: Emitted for names of definitions, like `foo` in `fn foo() {}`. -// defaultLibrary:: Emitted for items from built-in crates (std, core, alloc, test and proc_macro). -// documentation:: Emitted for documentation comments. -// injected:: Emitted for doc-string injected highlighting like rust source blocks in documentation. -// intraDocLink:: Emitted for intra doc links in doc-strings. -// library:: Emitted for items that are defined outside of the current crate. -// macro:: Emitted for tokens inside macro calls. -// mutable:: Emitted for mutable locals and statics as well as functions taking `&mut self`. -// public:: Emitted for items that are from the current crate and are `pub`. -// reference:: Emitted for locals behind a reference and functions taking `self` by reference. -// static:: Emitted for "static" functions, also known as functions that do not take a `self` param, as well as statics and consts. -// trait:: Emitted for associated trait items. -// unsafe:: Emitted for unsafe operations, like unsafe function calls, as well as the `unsafe` token. -// +// | | | +// |-----------|--------------------------------| +// |async| Emitted for async functions and the `async` and `await` keywords.| +// |attribute| Emitted for tokens inside attributes.| +// |callable| Emitted for locals whose types implements one of the `Fn*` traits.| +// |constant| Emitted for const.| +// |consuming| Emitted for locals that are being consumed when use in a function call.| +// |controlFlow| Emitted for control-flow related tokens, this includes th `?` operator.| +// |crateRoot| Emitted for crate names, like `serde` and `crate.| +// |declaration| Emitted for names of definitions, like `foo` in `fn foo(){}`.| +// |defaultLibrary| Emitted for items from built-in crates (std, core, allc, test and proc_macro).| +// |documentation| Emitted for documentation comment.| +// |injected| Emitted for doc-string injected highlighting like rust source blocks in documentation.| +// |intraDocLink| Emitted for intra doc links in doc-string.| +// |library| Emitted for items that are defined outside of the current crae.| +// |macro| Emitted for tokens inside macro call.| +// |mutable| Emitted for mutable locals and statics as well as functions taking `&mut self`.| +// |public| Emitted for items that are from the current crate and are `pub.| +// |reference| Emitted for locals behind a reference and functions taking self` by reference.| +// |static| Emitted for "static" functions, also known as functions that d not take a `self` param, as well as statics and consts.| +// |trait| Emitted for associated trait item.| +// |unsafe| Emitted for unsafe operations, like unsafe function calls, as ell as the `unsafe` token.| // -// image::https://user-images.githubusercontent.com/48062697/113164457-06cfb980-9239-11eb-819b-0f93e646acf8.png[] -// image::https://user-images.githubusercontent.com/48062697/113187625-f7f50100-9250-11eb-825e-91c58f236071.png[] +//  +//  pub(crate) fn highlight( db: &RootDatabase, config: HighlightConfig, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing.rs b/src/tools/rust-analyzer/crates/ide/src/typing.rs index 47d75f1c9570d..8c9dd05145272 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing.rs @@ -51,16 +51,15 @@ struct ExtendedTextEdit { // - typing `{` in a use item adds a closing `}` in the right place // - typing `>` to complete a return type `->` will insert a whitespace after it // -// VS Code:: +// #### VS Code // // Add the following to `settings.json`: -// [source,json] -// ---- +// ```json // "editor.formatOnType": true, -// ---- +// ``` // -// image::https://user-images.githubusercontent.com/48062697/113166163-69758500-923a-11eb-81ee-eb33ec380399.gif[] -// image::https://user-images.githubusercontent.com/48062697/113171066-105c2000-923f-11eb-87ab-f4a263346567.gif[] +//  +//  pub(crate) fn on_char_typed( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index e249c38c73d62..c6d1c283f4eca 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -16,12 +16,12 @@ use ide_db::text_edit::TextEdit; // Feature: On Enter // -// rust-analyzer can override kbd:[Enter] key to make it smarter: +// rust-analyzer can override <kbd>Enter</kbd> key to make it smarter: // -// - kbd:[Enter] inside triple-slash comments automatically inserts `///` -// - kbd:[Enter] in the middle or after a trailing space in `//` inserts `//` -// - kbd:[Enter] inside `//!` doc comments automatically inserts `//!` -// - kbd:[Enter] after `{` indents contents and closing `}` of single-line block +// - <kbd>Enter</kbd> inside triple-slash comments automatically inserts `///` +// - <kbd>Enter</kbd> in the middle or after a trailing space in `//` inserts `//` +// - <kbd>Enter</kbd> inside `//!` doc comments automatically inserts `//!` +// - <kbd>Enter</kbd> after `{` indents contents and closing `}` of single-line block // // This action needs to be assigned to shortcut explicitly. // @@ -29,29 +29,27 @@ use ide_db::text_edit::TextEdit; // Similarly, if rust-analyzer crashes or stops responding, `Enter` might not work. // In that case, you can still press `Shift-Enter` to insert a newline. // -// VS Code:: +// #### VS Code // // Add the following to `keybindings.json`: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust" // } -// ---- +// ```` // // When using the Vim plugin: -// [source,json] -// ---- +// ```json // { // "key": "Enter", // "command": "rust-analyzer.onEnter", // "when": "editorTextFocus && !suggestWidgetVisible && editorLangId == rust && vim.mode == 'Insert'" // } -// ---- +// ```` // -// image::https://user-images.githubusercontent.com/48062697/113065578-04c21800-91b1-11eb-82b8-22b8c481e645.gif[] +//  pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<TextEdit> { let parse = db.parse(EditionedFileId::current_edition(position.file_id)); let file = parse.tree(); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 9ff099f479e7b..24a986b85bf47 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -12,11 +12,9 @@ use triomphe::Arc; // // Only workspace crates are included, no crates.io dependencies or sysroot crates. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Crate Graph** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Crate Graph** | pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> { let crate_graph = db.crate_graph(); let crates_to_render = crate_graph diff --git a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs index fe532f4cc55ee..bfdf9d0f3374e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_hir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_hir.rs @@ -4,12 +4,11 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Hir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|--------------| // | VS Code | **rust-analyzer: View Hir** -// |=== -// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[] +// +//  pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs index a6352b99d4f52..67c241cbb9153 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_item_tree.rs @@ -6,11 +6,9 @@ use span::EditionedFileId; // // Displays the ItemTree of the currently open file, for debugging. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: Debug ItemTree** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: Debug ItemTree** | pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let file_id = sema diff --git a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs index ff74e05e9437c..edb83bc4eac4c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_memory_layout.rs @@ -75,11 +75,9 @@ impl FieldOrTupleIdx { // // Displays the recursive memory layout of a datatype. // -// |=== -// | Editor | Action Name -// -// | VS Code | **rust-analyzer: View Memory Layout** -// |=== +// | Editor | Action Name | +// |---------|-------------| +// | VS Code | **rust-analyzer: View Memory Layout** | pub(crate) fn view_memory_layout( db: &RootDatabase, position: FilePosition, diff --git a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs index 7a228375d5e97..aa4ff64a819e1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_mir.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_mir.rs @@ -4,11 +4,9 @@ use syntax::{algo::ancestors_at_offset, ast, AstNode}; // Feature: View Mir // -// |=== -// | Editor | Action Name -// +// | Editor | Action Name | +// |---------|-------------| // | VS Code | **rust-analyzer: View Mir** -// |=== pub(crate) fn view_mir(db: &RootDatabase, position: FilePosition) -> String { body_mir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index 218ee15a7dd32..f14ab45a5c322 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -11,11 +11,9 @@ use syntax::{ // // Shows a tree view with the syntax tree of the current file // -// |=== -// | Editor | Panel Name -// -// | VS Code | **Rust Syntax Tree** -// |=== +// | Editor | Panel Name | +// |---------|-------------| +// | VS Code | **Rust Syntax Tree** | pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); let parse = sema.parse_guess_edition(file_id); diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 318f71a2d4df2..79900425a17cc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(bad_style, missing_docs, unreachable_pub)] use crate::Edition; 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 44325fa1a29e6..7656c07c94851 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -528,7 +528,7 @@ config_data! { imports_granularity_enforce: bool = false, /// How imports should be grouped into use statements. imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, - /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. + /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. 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, @@ -3624,21 +3624,9 @@ fn manual(fields: &[SchemaField]) -> String { let name = format!("rust-analyzer.{}", field.replace('_', ".")); let doc = doc_comment_to_string(doc); if default.contains('\n') { - format_to_acc!( - acc, - r#"[[{name}]]{name}:: -+ --- -Default: ----- -{default} ----- -{doc} --- -"# - ) + format_to_acc!(acc, " **{name}**\n\nDefault:\n\n```{default}\n\n```\n\n {doc}\n\n ") } else { - format_to_acc!(acc, "[[{name}]]{name} (default: `{default}`)::\n+\n--\n{doc}--\n") + format_to_acc!(acc, "**{name}** (default: {default})\n\n {doc}\n\n") } }) } @@ -3716,7 +3704,7 @@ mod tests { #[test] fn generate_config_documentation() { - let docs_path = project_root().join("docs/user/generated_config.adoc"); + let docs_path = project_root().join("docs/book/src/configuration_generated.md"); let expected = FullConfigInput::manual(); ensure_file_contents(docs_path.as_std_path(), &expected); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1b22..287601e2e27f7 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. #![allow(non_snake_case)] use crate::{ diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs index 85d20c2bd8ce4..df2e9619db1c3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/tokens.rs @@ -1,4 +1,4 @@ -//! Generated by `cargo codegen grammar`, do not edit by hand. +//! Generated by `cargo xtask codegen grammar`, do not edit by hand. use crate::{ ast::AstToken, diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md new file mode 100644 index 0000000000000..a9d10df664355 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -0,0 +1,29 @@ +# rust-analyzer documentation + +The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/). + +## Quick start + +To run the documentation site locally: + +```shell +cargo install mdbook +cd docs/book +mdbook serve +# make changes to documentation files in doc/book/src +# ... +``` + +mdbook will rebuild the documentation as changes are made. + +## Making updates + +While not required, installing the mdbook binary can be helfpul in order to see the changes. +Start with the mdbook [User Guide](https://rust-lang.github.io/mdBook/guide/installation.html) to familiarize yourself with the tool. + +## Generated documentation + +Four sections are generated dynamically: assists, configuration, diagnostics and features. Their content is found in the `generated.md` files +of the respective book section, for example `src/configuration_generated.md`, and are included in the book via mdbook's +[include](https://rust-lang.github.io/mdBook/format/mdbook.html#including-files) functionality. Generated files can be rebuilt by running the various +test cases that generate them, or by simply running all of the `rust-analyzer` tests with `cargo test`. diff --git a/src/tools/rust-analyzer/docs/book/book.toml b/src/tools/rust-analyzer/docs/book/book.toml new file mode 100644 index 0000000000000..ba3c1dede5db7 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/book.toml @@ -0,0 +1,41 @@ +[book] +authors = ["The rust-analyzer authors"] +language = "en" +multilingual = false +src = "src" +title = "rust-analyzer" + +[rust] +edition = "2021" + +[output.html] +edit-url-template = "https://github.com/rust-lang/rust-analyzer/edit/master/manual/{path}" +git-repository-url = "https://github.com/rust-lang/rust-analyzer/tree/master/manual" +mathjax-support = true +site-url = "/manual/" + +[output.html.playground] +editable = true +runnable = false +line-numbers = true + +[output.html.search] +boost-hierarchy = 2 +boost-paragraph = 1 +boost-title = 2 +expand = true +heading-split-level = 2 +limit-results = 20 +use-boolean-and = true + +[output.html.redirect] +"/manual.html" = "/index.html" + +[output.html.fold] +enable = true +level = 3 + +[preprocessor.toc] +command = "mdbook-toc" +renderer = ["html"] +max-level = 3 diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md new file mode 100644 index 0000000000000..545e4a3e62df8 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -0,0 +1,21 @@ +# rust-analyzer + +At its core, rust-analyzer is a **library** for semantic analysis of +Rust code as it changes over time. This manual focuses on a specific +usage of the library -- running it as part of a server that implements +the [Language Server +Protocol](https://microsoft.github.io/language-server-protocol/) (LSP). +The LSP allows various code editors, like VS Code, Emacs or Vim, to +implement semantic features like completion or goto definition by +talking to an external language server process. + +To improve this document, send a pull request: +[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/docs/book/README.md) + +The manual is written in markdown and includes +some extra files which are generated from the source code. Run +`cargo test` and `cargo xtask codegen` to create these. + +If you have questions about using rust-analyzer, please ask them in the +["IDEs and Editors"](https://users.rust-lang.org/c/ide/14) topic of Rust +users forum. diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md new file mode 100644 index 0000000000000..7e6cd5972d408 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -0,0 +1,13 @@ +# Summary + +- [Introduction](README.md) +- [Installation](installation.md) +- [Troubleshooting](troubleshooting.md) +- [Configuration](configuration.md) +- [Non-Cargo Based Projects](non_cargo_based_projects.md) +- [Security](security.md) +- [Privacy](privacy.md) +- [Features](features.md) +- [Assists (Code Actions)](assists.md) +- [Diagnostics](diagnostics.md) +- [Editor Features](editor_features.md) diff --git a/src/tools/rust-analyzer/docs/book/src/assists.md b/src/tools/rust-analyzer/docs/book/src/assists.md new file mode 100644 index 0000000000000..9d7c3bc1d5b94 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/assists.md @@ -0,0 +1,8 @@ +# Assists + +Assists, or code actions, are small local refactorings, available in a +particular context. They are usually triggered by a shortcut or by +clicking a light bulb icon in the editor. Cursor position or selection +is signified by `┃` character. + +{{#include assists_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration.md b/src/tools/rust-analyzer/docs/book/src/configuration.md new file mode 100644 index 0000000000000..221a571c17c84 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration.md @@ -0,0 +1,51 @@ +# Configuration + +**Source:** +[config.rs](https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs) + +The [Installation](#_installation) section contains details on +configuration for some of the editors. In general `rust-analyzer` is +configured via LSP messages, which means that it’s up to the editor to +decide on the exact format and location of configuration files. + +Some clients, such as [VS Code](#vs-code) or [COC plugin in +Vim](#coc-rust-analyzer) provide `rust-analyzer` specific configuration +UIs. Others may require you to know a bit more about the interaction +with `rust-analyzer`. + +For the later category, it might help to know that the initial +configuration is specified as a value of the `initializationOptions` +field of the [`InitializeParams` message, in the LSP +protocol](https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize). +The spec says that the field type is `any?`, but `rust-analyzer` is +looking for a JSON object that is constructed using settings from the +list below. Name of the setting, ignoring the `rust-analyzer.` prefix, +is used as a path, and value of the setting becomes the JSON property +value. + +For example, a very common configuration is to enable proc-macro +support, can be achieved by sending this JSON: + + { + "cargo": { + "buildScripts": { + "enable": true, + }, + }, + "procMacro": { + "enable": true, + } + } + +Please consult your editor’s documentation to learn more about how to +configure [LSP +servers](https://microsoft.github.io/language-server-protocol/). + +To verify which configuration is actually used by `rust-analyzer`, set +`RA_LOG` environment variable to `rust_analyzer=info` and look for +config-related messages. Logs should show both the JSON that +`rust-analyzer` sees as well as the updated config. + +This is the list of config options `rust-analyzer` supports: + +{{#include configuration_generated.md}} diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md new file mode 100644 index 0000000000000..49eb7248898e5 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -0,0 +1,1201 @@ +**rust-analyzer.assist.emitMustUse** (default: false) + + Whether to insert #[must_use] when generating `as_` methods +for enum variants. + + +**rust-analyzer.assist.expressionFillDefault** (default: "todo") + + Placeholder expression to use for missing expressions in assists. + + +**rust-analyzer.assist.termSearch.borrowcheck** (default: true) + + Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. + + +**rust-analyzer.assist.termSearch.fuel** (default: 1800) + + Term search fuel in "units of work" for assists (Defaults to 1800). + + +**rust-analyzer.cachePriming.enable** (default: true) + + Warm up caches on project load. + + +**rust-analyzer.cachePriming.numThreads** (default: "physical") + + How many worker threads to handle priming caches. The default `0` means to pick automatically. + + +**rust-analyzer.cargo.allTargets** (default: true) + + Pass `--all-targets` to cargo invocation. + + +**rust-analyzer.cargo.autoreload** (default: true) + + Automatically refresh project info via `cargo metadata` on +`Cargo.toml` or `.cargo/config.toml` changes. + + +**rust-analyzer.cargo.buildScripts.enable** (default: true) + + Run build scripts (`build.rs`) for more precise code analysis. + + +**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 Rust workspace with the +workspace as the working directory. +If `once` is set, the command will be executed once with the opened project as the +working directory. +This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` +is set. + + +**rust-analyzer.cargo.buildScripts.overrideCommand** (default: null) + + 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 +option. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + +By default, a cargo invocation will be constructed for the configured +targets and features, with the following base command line: + +```bash +cargo check --quiet --workspace --message-format=json --all-targets --keep-going +``` +. + + +**rust-analyzer.cargo.buildScripts.rebuildOnSave** (default: true) + + Rerun proc-macros building/build-scripts running when proc-macro +or build-script sources change and are saved. + + +**rust-analyzer.cargo.buildScripts.useRustcWrapper** (default: true) + + Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to +avoid checking unnecessary things. + + + **rust-analyzer.cargo.cfgs** + +Default: + +```[ + "debug_assertions", + "miri" +] + +``` + + List of cfg options to enable with the given values. + + + **rust-analyzer.cargo.extraArgs** (default: []) + + Extra arguments that are passed to every cargo invocation. + + +**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** (default: []) + + List of features to activate. + +Set this to `"all"` to pass `--all-features` to cargo. + + +**rust-analyzer.cargo.noDefaultFeatures** (default: false) + + Whether to pass `--no-default-features` to cargo. + + +**rust-analyzer.cargo.sysroot** (default: "discover") + + 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.sysrootSrc** (default: null) + + Relative path to the sysroot library sources. If left unset, this will default to +`{cargo.sysroot}/lib/rustlib/src/rust/library`. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.cargo.target** (default: null) + + Compilation target override (target tuple). + + +**rust-analyzer.cargo.targetDir** (default: null) + + Optional path to a rust-analyzer specific target directory. +This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro +building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + +Set to `true` to use a subdirectory of the existing target directory or +set to a path relative to the workspace to use that path. + + +**rust-analyzer.cfg.setTest** (default: true) + + Set `cfg(test)` for local crates. Defaults to true. + + +**rust-analyzer.checkOnSave** (default: true) + + Run the check command for diagnostics on save. + + +**rust-analyzer.check.allTargets** (default: null) + + Check all targets and tests (`--all-targets`). Defaults to +`#rust-analyzer.cargo.allTargets#`. + + +**rust-analyzer.check.command** (default: "check") + + Cargo command to use for `cargo check`. + + +**rust-analyzer.check.extraArgs** (default: []) + + Extra arguments for `cargo check`. + + +**rust-analyzer.check.extraEnv** (default: {}) + + Extra environment variables that will be set when running `cargo check`. +Extends `#rust-analyzer.cargo.extraEnv#`. + + +**rust-analyzer.check.features** (default: null) + + List of features to activate. Defaults to +`#rust-analyzer.cargo.features#`. + +Set to `"all"` to pass `--all-features` to Cargo. + + +**rust-analyzer.check.ignore** (default: []) + + List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + +For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + + +**rust-analyzer.check.invocationStrategy** (default: "per_workspace") + + Specifies the invocation strategy to use when running the check command. +If `per_workspace` is set, the command will be executed for each workspace. +If `once` is set, the command will be executed once. +This config only has an effect when `#rust-analyzer.check.overrideCommand#` +is set. + + +**rust-analyzer.check.noDefaultFeatures** (default: null) + + Whether to pass `--no-default-features` to Cargo. Defaults to +`#rust-analyzer.cargo.noDefaultFeatures#`. + + +**rust-analyzer.check.overrideCommand** (default: null) + + Override the command rust-analyzer uses instead of `cargo check` for +diagnostics on save. The command is required to output json and +should therefore include `--message-format=json` or a similar option +(if your client supports the `colorDiagnosticOutput` experimental +capability, you can use `--message-format=json-diagnostic-rendered-ansi`). + +If you're changing this because you're using some tool wrapping +Cargo, you might also want to change +`#rust-analyzer.cargo.buildScripts.overrideCommand#`. + +If there are multiple linked projects/workspaces, this command is invoked for +each of them, with the working directory being the workspace root +(i.e., the folder containing the `Cargo.toml`). This can be overwritten +by changing `#rust-analyzer.check.invocationStrategy#`. + +If `$saved_file` is part of the command, rust-analyzer will pass +the absolute path of the saved file to the provided command. This is +intended to be used with non-Cargo build systems. +Note that `$saved_file` is experimental and may be removed in the future. + +An example command would be: + +```bash +cargo check --workspace --message-format=json --all-targets +``` +. + + +**rust-analyzer.check.targets** (default: null) + + Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + +Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. +`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + +Aliased as `"checkOnSave.targets"`. + + +**rust-analyzer.check.workspace** (default: true) + + Whether `--workspace` should be passed to `cargo check`. +If false, `-p <package>` will be passed instead if applicable. In case it is not, no +check will be performed. + + +**rust-analyzer.completion.addSemicolonToUnit** (default: true) + + Whether to automatically add a semicolon when completing unit-returning functions. + +In `match` arms it completes a comma instead. + + +**rust-analyzer.completion.autoAwait.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + + +**rust-analyzer.completion.autoIter.enable** (default: true) + + Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + + +**rust-analyzer.completion.autoimport.enable** (default: true) + + Toggles the additional completions that automatically add imports when completed. +Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + + + **rust-analyzer.completion.autoimport.exclude** + +Default: + +```[ + { + "path": "core::borrow::Borrow", + "type": "methods" + }, + { + "path": "core::borrow::BorrowMut", + "type": "methods" + } +] + +``` + + A list of full paths to items to exclude from auto-importing completions. + +Traits in this list won't have their methods suggested in completions unless the trait +is in scope. + +You can either specify a string path which defaults to type "always" or use the more verbose +form `{ "path": "path::to::item", type: "always" }`. + +For traits the type "methods" can be used to only exclude the methods but not the trait itself. + +This setting also inherits `#rust-analyzer.completion.excludeTraits#`. + + + **rust-analyzer.completion.autoself.enable** (default: true) + + Toggles the additional completions that automatically show method calls and field accesses +with `self` prefixed to them when inside a method. + + +**rust-analyzer.completion.callable.snippets** (default: "fill_arguments") + + Whether to add parenthesis and argument snippets when completing function. + + +**rust-analyzer.completion.excludeTraits** (default: []) + + A list of full paths to traits whose methods to exclude from completion. + +Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. + +Note that the trait themselves can still be completed. + + +**rust-analyzer.completion.fullFunctionSignatures.enable** (default: false) + + Whether to show full function/method signatures in completion docs. + + +**rust-analyzer.completion.hideDeprecated** (default: false) + + Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + + +**rust-analyzer.completion.limit** (default: null) + + Maximum number of completions to return. If `None`, the limit is infinite. + + +**rust-analyzer.completion.postfix.enable** (default: true) + + Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + + +**rust-analyzer.completion.privateEditable.enable** (default: false) + + Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + + + **rust-analyzer.completion.snippets.custom** + +Default: + +```{ + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + } +} + +``` + + Custom completion snippets. + + + **rust-analyzer.completion.termSearch.enable** (default: false) + + Whether to enable term search based snippets like `Some(foo.bar().baz())`. + + +**rust-analyzer.completion.termSearch.fuel** (default: 1000) + + Term search fuel in "units of work" for autocompletion (Defaults to 1000). + + +**rust-analyzer.diagnostics.disabled** (default: []) + + List of rust-analyzer diagnostics to disable. + + +**rust-analyzer.diagnostics.enable** (default: true) + + Whether to show native rust-analyzer diagnostics. + + +**rust-analyzer.diagnostics.experimental.enable** (default: false) + + Whether to show experimental rust-analyzer diagnostics that might +have more false positives than usual. + + +**rust-analyzer.diagnostics.remapPrefix** (default: {}) + + Map of prefixes to be substituted when parsing diagnostic file paths. +This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + + +**rust-analyzer.diagnostics.styleLints.enable** (default: false) + + Whether to run additional style lints. + + +**rust-analyzer.diagnostics.warningsAsHint** (default: []) + + List of warnings that should be displayed with hint severity. + +The warnings will be indicated by faded text or three dots in code +and will not show up in the `Problems Panel`. + + +**rust-analyzer.diagnostics.warningsAsInfo** (default: []) + + List of warnings that should be displayed with info severity. + +The warnings will be indicated by a blue squiggly underline in code +and a blue icon in the `Problems Panel`. + + +**rust-analyzer.files.excludeDirs** (default: []) + + These directories will be ignored by rust-analyzer. They are +relative to the workspace root, and globs are not supported. You may +also need to add the folders to Code's `files.watcherExclude`. + + +**rust-analyzer.files.watcher** (default: "client") + + Controls file watching implementation. + + +**rust-analyzer.highlightRelated.breakPoints.enable** (default: true) + + Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + + +**rust-analyzer.highlightRelated.closureCaptures.enable** (default: true) + + Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + + +**rust-analyzer.highlightRelated.exitPoints.enable** (default: true) + + Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + + +**rust-analyzer.highlightRelated.references.enable** (default: true) + + Enables highlighting of related references while the cursor is on any identifier. + + +**rust-analyzer.highlightRelated.yieldPoints.enable** (default: true) + + Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + + +**rust-analyzer.hover.actions.debug.enable** (default: true) + + Whether to show `Debug` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.enable** (default: true) + + Whether to show HoverActions in Rust files. + + +**rust-analyzer.hover.actions.gotoTypeDef.enable** (default: true) + + Whether to show `Go to Type Definition` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.implementations.enable** (default: true) + + Whether to show `Implementations` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.references.enable** (default: false) + + Whether to show `References` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.run.enable** (default: true) + + Whether to show `Run` action. Only applies when +`#rust-analyzer.hover.actions.enable#` is set. + + +**rust-analyzer.hover.actions.updateTest.enable** (default: true) + + Whether to show `Update Test` action. Only applies when +`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. + + +**rust-analyzer.hover.documentation.enable** (default: true) + + Whether to show documentation on hover. + + +**rust-analyzer.hover.documentation.keywords.enable** (default: true) + + Whether to show keyword hover popups. Only applies when +`#rust-analyzer.hover.documentation.enable#` is set. + + +**rust-analyzer.hover.links.enable** (default: true) + + Use markdown syntax for links on hover. + + +**rust-analyzer.hover.maxSubstitutionLength** (default: 20) + + Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. + +This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. + +The default is 20 characters. + + +**rust-analyzer.hover.memoryLayout.alignment** (default: "hexadecimal") + + How to render the align information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.enable** (default: true) + + Whether to show memory layout data on hover. + + +**rust-analyzer.hover.memoryLayout.niches** (default: false) + + How to render the niche information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.offset** (default: "hexadecimal") + + How to render the offset information in a memory layout hover. + + +**rust-analyzer.hover.memoryLayout.size** (default: "both") + + How to render the size information in a memory layout hover. + + +**rust-analyzer.hover.show.enumVariants** (default: 5) + + How many variants of an enum to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.fields** (default: 5) + + How many fields of a struct, variant or union to display when hovering on. Show none if empty. + + +**rust-analyzer.hover.show.traitAssocItems** (default: null) + + How many associated items of a trait to display when hovering a trait. + + +**rust-analyzer.imports.granularity.enforce** (default: false) + + Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. + + +**rust-analyzer.imports.granularity.group** (default: "crate") + + How imports should be grouped into use statements. + + +**rust-analyzer.imports.group.enable** (default: true) + + Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. + + +**rust-analyzer.imports.merge.glob** (default: true) + + Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. + + +**rust-analyzer.imports.preferNoStd** (default: false) + + Prefer to unconditionally use imports of the core and alloc crate, over the std crate. + + +**rust-analyzer.imports.preferPrelude** (default: false) + + Whether to prefer import paths containing a `prelude` module. + + +**rust-analyzer.imports.prefix** (default: "plain") + + The path structure for newly inserted paths to use. + + +**rust-analyzer.imports.prefixExternPrelude** (default: false) + + Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". + + +**rust-analyzer.inlayHints.bindingModeHints.enable** (default: false) + + Whether to show inlay type hints for binding modes. + + +**rust-analyzer.inlayHints.chainingHints.enable** (default: true) + + Whether to show inlay type hints for method chains. + + +**rust-analyzer.inlayHints.closingBraceHints.enable** (default: true) + + Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + + +**rust-analyzer.inlayHints.closingBraceHints.minLines** (default: 25) + + Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 +to always show them). + + +**rust-analyzer.inlayHints.closureCaptureHints.enable** (default: false) + + Whether to show inlay hints for closure captures. + + +**rust-analyzer.inlayHints.closureReturnTypeHints.enable** (default: "never") + + Whether to show inlay type hints for return types of closures. + + +**rust-analyzer.inlayHints.closureStyle** (default: "impl_fn") + + Closure notation in type and chaining inlay hints. + + +**rust-analyzer.inlayHints.discriminantHints.enable** (default: "never") + + Whether to show enum variant discriminant hints. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.enable** (default: "never") + + Whether to show inlay hints for type adjustments. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe** (default: false) + + Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + + +**rust-analyzer.inlayHints.expressionAdjustmentHints.mode** (default: "prefix") + + Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + + +**rust-analyzer.inlayHints.genericParameterHints.const.enable** (default: true) + + Whether to show const generic parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.lifetime.enable** (default: false) + + Whether to show generic lifetime parameter name inlay hints. + + +**rust-analyzer.inlayHints.genericParameterHints.type.enable** (default: false) + + Whether to show generic type parameter name inlay hints. + + +**rust-analyzer.inlayHints.implicitDrops.enable** (default: false) + + Whether to show implicit drop hints. + + +**rust-analyzer.inlayHints.implicitSizedBoundHints.enable** (default: false) + + Whether to show inlay hints for the implied type parameter `Sized` bound. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.enable** (default: "never") + + Whether to show inlay type hints for elided lifetimes in function signatures. + + +**rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames** (default: false) + + Whether to prefer using parameter names as the name for elided lifetime hints if possible. + + +**rust-analyzer.inlayHints.maxLength** (default: 25) + + Maximum length for inlay hints. Set to null to have an unlimited length. + + +**rust-analyzer.inlayHints.parameterHints.enable** (default: true) + + Whether to show function parameter name inlay hints at the call +site. + + +**rust-analyzer.inlayHints.rangeExclusiveHints.enable** (default: false) + + Whether to show exclusive range inlay hints. + + +**rust-analyzer.inlayHints.reborrowHints.enable** (default: "never") + + Whether to show inlay hints for compiler inserted reborrows. +This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + + +**rust-analyzer.inlayHints.renderColons** (default: true) + + Whether to render leading colons for type hints, and trailing colons for parameter hints. + + +**rust-analyzer.inlayHints.typeHints.enable** (default: true) + + Whether to show inlay type hints for variables. + + +**rust-analyzer.inlayHints.typeHints.hideClosureInitialization** (default: false) + + Whether to hide inlay type hints for `let` statements that initialize to a closure. +Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + + +**rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false) + + Whether to hide inlay type hints for constructors. + + +**rust-analyzer.interpret.tests** (default: false) + + Enables the experimental support for interpreting tests. + + +**rust-analyzer.joinLines.joinAssignments** (default: true) + + Join lines merges consecutive declaration and initialization of an assignment. + + +**rust-analyzer.joinLines.joinElseIf** (default: true) + + Join lines inserts else between consecutive ifs. + + +**rust-analyzer.joinLines.removeTrailingComma** (default: true) + + Join lines removes trailing commas. + + +**rust-analyzer.joinLines.unwrapTrivialBlock** (default: true) + + Join lines unwraps trivial blocks. + + +**rust-analyzer.lens.debug.enable** (default: true) + + Whether to show `Debug` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.enable** (default: true) + + Whether to show CodeLens in Rust files. + + +**rust-analyzer.lens.implementations.enable** (default: true) + + Whether to show `Implementations` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.location** (default: "above_name") + + Where to render annotations. + + +**rust-analyzer.lens.references.adt.enable** (default: false) + + Whether to show `References` lens for Struct, Enum, and Union. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.enumVariant.enable** (default: false) + + Whether to show `References` lens for Enum Variants. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.method.enable** (default: false) + + Whether to show `Method References` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.references.trait.enable** (default: false) + + Whether to show `References` lens for Trait. +Only applies when `#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.run.enable** (default: true) + + Whether to show `Run` lens. Only applies when +`#rust-analyzer.lens.enable#` is set. + + +**rust-analyzer.lens.updateTest.enable** (default: true) + + Whether to show `Update Test` lens. Only applies when +`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. + + +**rust-analyzer.linkedProjects** (default: []) + + Disable project auto-discovery in favor of explicitly specified set +of projects. + +Elements must be paths pointing to `Cargo.toml`, +`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON +objects in `rust-project.json` format. + + +**rust-analyzer.lru.capacity** (default: null) + + Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + + +**rust-analyzer.lru.query.capacities** (default: {}) + + Sets the LRU capacity of the specified queries. + + +**rust-analyzer.notifications.cargoTomlNotFound** (default: true) + + Whether to show `can't find Cargo.toml` error message. + + +**rust-analyzer.numThreads** (default: null) + + How many worker threads in the main loop. The default `null` means to pick automatically. + + +**rust-analyzer.procMacro.attributes.enable** (default: true) + + Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + + +**rust-analyzer.procMacro.enable** (default: true) + + Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + + +**rust-analyzer.procMacro.ignored** (default: {}) + + These proc-macros will be ignored when trying to expand them. + +This config takes a map of crate names with the exported proc-macro names to ignore as values. + + +**rust-analyzer.procMacro.server** (default: null) + + Internal config, path to proc-macro server executable. + + +**rust-analyzer.references.excludeImports** (default: false) + + Exclude imports from find-all-references. + + +**rust-analyzer.references.excludeTests** (default: false) + + Exclude tests from find-all-references and call-hierarchy. + + +**rust-analyzer.runnables.command** (default: null) + + Command to be executed instead of 'cargo' for runnables. + + +**rust-analyzer.runnables.extraArgs** (default: []) + + Additional arguments to be passed to cargo for runnables such as +tests or binaries. For example, it may be `--release`. + + + **rust-analyzer.runnables.extraTestBinaryArgs** + +Default: + +```[ + "--show-output" +] + +``` + + Additional arguments to be passed through Cargo to launched tests, benchmarks, or +doc-tests. + +Unless the launched target uses a +[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), +they will end up being interpreted as options to +[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + + + **rust-analyzer.rustc.source** (default: null) + + Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private +projects, or "discover" to try to automatically find it if the `rustc-dev` component +is installed. + +Any project which uses rust-analyzer with the rustcPrivate +crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + +This option does not take effect until rust-analyzer is restarted. + + +**rust-analyzer.rustfmt.extraArgs** (default: []) + + Additional arguments to `rustfmt`. + + +**rust-analyzer.rustfmt.overrideCommand** (default: null) + + Advanced option, fully override the command rust-analyzer uses for +formatting. This should be the equivalent of `rustfmt` here, and +not that of `cargo fmt`. The file contents will be passed on the +standard input and the formatted result will be read from the +standard output. + + +**rust-analyzer.rustfmt.rangeFormatting.enable** (default: false) + + Enables the use of rustfmt's unstable range formatting command for the +`textDocument/rangeFormatting` request. The rustfmt option is unstable and only +available on a nightly build. + + +**rust-analyzer.semanticHighlighting.doc.comment.inject.enable** (default: true) + + Inject additional highlighting into doc comments. + +When enabled, rust-analyzer will highlight rust source in doc comments as well as intra +doc links. + + +**rust-analyzer.semanticHighlighting.nonStandardTokens** (default: true) + + Whether the server is allowed to emit non-standard tokens and modifiers. + + +**rust-analyzer.semanticHighlighting.operator.enable** (default: true) + + Use semantic tokens for operators. + +When disabled, rust-analyzer will emit semantic tokens only for operator tokens when +they are tagged with modifiers. + + +**rust-analyzer.semanticHighlighting.operator.specialization.enable** (default: false) + + Use specialized semantic tokens for operators. + +When enabled, rust-analyzer will emit special token types for operator tokens instead +of the generic `operator` token type. + + +**rust-analyzer.semanticHighlighting.punctuation.enable** (default: false) + + Use semantic tokens for punctuation. + +When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when +they are tagged with modifiers or have a special role. + + +**rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang** (default: false) + + When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro +calls. + + +**rust-analyzer.semanticHighlighting.punctuation.specialization.enable** (default: false) + + Use specialized semantic tokens for punctuation. + +When enabled, rust-analyzer will emit special token types for punctuation tokens instead +of the generic `punctuation` token type. + + +**rust-analyzer.semanticHighlighting.strings.enable** (default: true) + + Use semantic tokens for strings. + +In some editors (e.g. vscode) semantic tokens override other highlighting grammars. +By disabling semantic tokens for strings, other grammars can be used to highlight +their contents. + + +**rust-analyzer.signatureInfo.detail** (default: "full") + + Show full signature of the callable. Only shows parameters if disabled. + + +**rust-analyzer.signatureInfo.documentation.enable** (default: true) + + Show documentation. + + +**rust-analyzer.typing.triggerChars** (default: "=.") + + Specify the characters allowed to invoke special on typing triggers. +- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression +- typing `=` between two expressions adds `;` when in statement position +- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position +- typing `.` in a chain method call auto-indents +- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression +- typing `{` in a use item adds a closing `}` in the right place +- typing `>` to complete a return type `->` will insert a whitespace after it +- typing `<` in a path or type position inserts a closing `>` after the path or type. + + +**rust-analyzer.vfs.extraIncludes** (default: []) + + Additional paths to include in the VFS. Generally for code that is +generated or otherwise managed by a build system outside of Cargo, +though Cargo might be the eventual consumer. + + +**rust-analyzer.workspace.discoverConfig** (default: null) + + Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. + +[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. +`progress_label` is used for the title in progress indicators, whereas `files_to_watch` +is used to determine which build system-specific files should be watched in order to +reload rust-analyzer. + +Below is an example of a valid configuration: +```json +"rust-analyzer.workspace.discoverConfig": { + "command": [ + "rust-project", + "develop-json" + ], + "progressLabel": "rust-analyzer", + "filesToWatch": [ + "BUCK" + ] +} +``` + +## On `DiscoverWorkspaceConfig::command` + +**Warning**: This format is provisional and subject to change. + +[`DiscoverWorkspaceConfig::command`] *must* return a JSON object +corresponding to `DiscoverProjectData::Finished`: + +```norun +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(tag = "kind")] +#[serde(rename_all = "snake_case")] +enum DiscoverProjectData { + Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, + Error { error: String, source: Option<String> }, + Progress { message: String }, +} +``` + +As JSON, `DiscoverProjectData::Finished` is: + +```json +{ + // the internally-tagged representation of the enum. + "kind": "finished", + // the file used by a non-Cargo build system to define + // a package or target. + "buildfile": "rust-analyzer/BUILD", + // the contents of a rust-project.json, elided for brevity + "project": { + "sysroot": "foo", + "crates": [] + } +} +``` + +It is encouraged, but not required, to use the other variants on +`DiscoverProjectData` to provide a more polished end-user experience. + +`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, +which will be substituted with the JSON-serialized form of the following +enum: + +```norun +#[derive(PartialEq, Clone, Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub enum DiscoverArgument { + Path(AbsPathBuf), + Buildfile(AbsPathBuf), +} +``` + +The JSON representation of `DiscoverArgument::Path` is: + +```json +{ + "path": "src/main.rs" +} +``` + +Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: + +``` +{ + "buildfile": "BUILD" +} +``` + +`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, +and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to +to update an existing workspace. As a reference for implementors, +buck2's `rust-project` will likely be useful: +https://github.com/facebook/buck2/tree/main/integrations/rust-project. + + +**rust-analyzer.workspace.symbol.search.kind** (default: "only_types") + + Workspace symbol search kind. + + +**rust-analyzer.workspace.symbol.search.limit** (default: 128) + + Limits the number of items returned from a workspace symbol search (Defaults to 128). +Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. +Other clients requires all results upfront and might require a higher limit. + + +**rust-analyzer.workspace.symbol.search.scope** (default: "workspace") + + Workspace symbol search scope. + + diff --git a/src/tools/rust-analyzer/docs/book/src/diagnostics.md b/src/tools/rust-analyzer/docs/book/src/diagnostics.md new file mode 100644 index 0000000000000..60685c98da711 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/diagnostics.md @@ -0,0 +1,16 @@ +# Diagnostics + +While most errors and warnings provided by rust-analyzer come from the +`cargo check` integration, there’s a growing number of diagnostics +implemented using rust-analyzer’s own analysis. Some of these +diagnostics don’t respect `#[allow]` or `#[deny]` attributes yet, but +can be turned off using the `rust-analyzer.diagnostics.enable`, +`rust-analyzer.diagnostics.experimental.enable` or +`rust-analyzer.diagnostics.disabled` settings. + +## Clippy + +To run `cargo clippy` instead of `cargo check`, you can set +`"rust-analyzer.check.command": "clippy"`. + +{{#include diagnostics_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/editor_features.md b/src/tools/rust-analyzer/docs/book/src/editor_features.md new file mode 100644 index 0000000000000..73fe9f49a96e4 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/editor_features.md @@ -0,0 +1,204 @@ +# Editor Features + + +## VS Code + +### Color configurations + +It is possible to change the foreground/background color and font +family/size of inlay hints. Just add this to your `settings.json`: + +```json +{ + "editor.inlayHints.fontFamily": "Courier New", + "editor.inlayHints.fontSize": 11, + + "workbench.colorCustomizations": { + // Name of the theme you are currently using + "[Default Dark+]": { + "editorInlayHint.foreground": "#868686f0", + "editorInlayHint.background": "#3d3d3d48", + + // Overrides for specific kinds of inlay hints + "editorInlayHint.typeForeground": "#fdb6fdf0", + "editorInlayHint.parameterForeground": "#fdb6fdf0", + } + } +} +``` + +### Semantic style customizations + +You can customize the look of different semantic elements in the source +code. For example, mutable bindings are underlined by default and you +can override this behavior by adding the following section to your +`settings.json`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "*.mutable": { + "fontStyle": "", // underline is the default + }, + } + }, +} +``` + +Most themes doesn’t support styling unsafe operations differently yet. +You can fix this by adding overrides for the rules `operator.unsafe`, +`function.unsafe`, and `method.unsafe`: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600", + "function.unsafe": "#ff6600", + "method.unsafe": "#ff6600" + } + }, +} +``` + +In addition to the top-level rules you can specify overrides for +specific themes. For example, if you wanted to use a darker text color +on a specific light theme, you might write: + +```json +{ + "editor.semanticTokenColorCustomizations": { + "rules": { + "operator.unsafe": "#ff6600" + }, + "[Ayu Light]": { + "rules": { + "operator.unsafe": "#572300" + } + } + }, +} +``` + +Make sure you include the brackets around the theme name. For example, +use `"[Ayu Light]"` to customize the theme Ayu Light. + +### Special `when` clause context for keybindings. + +You may use `inRustProject` context to configure keybindings for rust +projects only. For example: + +```json +{ + "key": "ctrl+alt+d", + "command": "rust-analyzer.openDocs", + "when": "inRustProject" +} +``` + +More about `when` clause contexts +[here](https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts). + +### Setting runnable environment variables + +You can use "rust-analyzer.runnables.extraEnv" setting to define +runnable environment-specific substitution variables. The simplest way +for all runnables in a bunch: + +```json +"rust-analyzer.runnables.extraEnv": { + "RUN_SLOW_TESTS": "1" +} +``` + +Or it is possible to specify vars more granularly: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + // "mask": null, // null mask means that this rule will be applied for all runnables + env: { + "APP_ID": "1", + "APP_DATA": "asdf" + } + }, + { + "mask": "test_name", + "env": { + "APP_ID": "2", // overwrites only APP_ID + } + } +] +``` + +You can use any valid regular expression as a mask. Also note that a +full runnable name is something like **run bin\_or\_example\_name**, +**test some::mod::test\_name** or **test-mod some::mod**, so it is +possible to distinguish binaries, single tests, and test modules with +this masks: `"^run"`, `"^test "` (the trailing space matters!), and +`"^test-mod"` respectively. + +If needed, you can set different values for different platforms: + +```json +"rust-analyzer.runnables.extraEnv": [ + { + "platform": "win32", // windows only + env: { + "APP_DATA": "windows specific data" + } + }, + { + "platform": ["linux"], + "env": { + "APP_DATA": "linux data", + } + }, + { // for all platforms + "env": { + "APP_COMMON_DATA": "xxx", + } + } +] +``` + +### Compiler feedback from external commands + +Instead of relying on the built-in `cargo check`, you can configure Code +to run a command in the background and use the `$rustc-watch` problem +matcher to generate inline error markers from its output. + +To do this you need to create a new [VS Code +Task](https://code.visualstudio.com/docs/editor/tasks) and set +`"rust-analyzer.checkOnSave": false` in preferences. + +For example, if you want to run +[`cargo watch`](https://crates.io/crates/cargo-watch) instead, you might +add the following to `.vscode/tasks.json`: + +```json +{ + "label": "Watch", + "group": "build", + "type": "shell", + "command": "cargo watch", + "problemMatcher": "$rustc-watch", + "isBackground": true +} +``` + +### Live Share + +VS Code Live Share has partial support for rust-analyzer. + +Live Share *requires* the official Microsoft build of VS Code, OSS +builds will not work correctly. + +The host’s rust-analyzer instance will be shared with all guests joining +the session. The guests do not have to have the rust-analyzer extension +installed for this to work. + +If you are joining a Live Share session and *do* have rust-analyzer +installed locally, commands from the command palette will not work +correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/docs/book/src/features.md b/src/tools/rust-analyzer/docs/book/src/features.md new file mode 100644 index 0000000000000..0829a0213b76f --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/features.md @@ -0,0 +1,3 @@ +# Features + +{{#include features_generated.md:2:}} diff --git a/src/tools/rust-analyzer/docs/book/src/installation.md b/src/tools/rust-analyzer/docs/book/src/installation.md new file mode 100644 index 0000000000000..5b697e9bc33d4 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/installation.md @@ -0,0 +1,644 @@ +# Installation + +In theory, one should be able to just install the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary) and have it automatically +work with any editor. We are not there yet, so some editor specific +setup is required. + +Additionally, rust-analyzer needs the sources of the standard library. +If the source code is not present, rust-analyzer will attempt to install +it automatically. + +To add the sources manually, run the following command: + + $ rustup component add rust-src + +## Toolchain + +Only the latest stable standard library source is officially supported +for use with rust-analyzer. If you are using an older toolchain or have +an override set, rust-analyzer may fail to understand the Rust source. +You will either need to update your toolchain or use an older version of +rust-analyzer that is compatible with your toolchain. + +If you are using an override in your project, you can still force +rust-analyzer to use the stable toolchain via the environment variable +`RUSTUP_TOOLCHAIN`. For example, with VS Code or coc-rust-analyzer: + + { "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } + +## VS Code + +This is the best supported editor at the moment. The rust-analyzer +plugin for VS Code is maintained [in +tree](https://github.com/rust-lang/rust-analyzer/tree/master/editors/code). + +You can install the latest release of the plugin from [the +marketplace](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer). + +Note that the plugin may cause conflicts with the [previous official +Rust +plugin](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust). +The latter is no longer maintained and should be uninstalled. + +The server binary is stored in the extension install directory, which +starts with `rust-lang.rust-analyzer-` and is located under: + +- Linux: `~/.vscode/extensions` + +- Linux (Remote, such as WSL): `~/.vscode-server/extensions` + +- macOS: `~/.vscode/extensions` + +- Windows: `%USERPROFILE%\.vscode\extensions` + +As an exception, on NixOS, the extension makes a copy of the server and +stores it under +`~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. + +Note that we only support the two most recent versions of VS Code. + +### Updates + +The extension will be updated automatically as new versions become +available. It will ask your permission to download the matching language +server version binary if needed. + +#### Nightly + +We ship nightly releases for VS Code. To help us out by testing the +newest code, you can enable pre-release versions in the Code extension +page. + +### Manual installation + +Alternatively, download a VSIX corresponding to your platform from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. + +Install the extension with the `Extensions: Install from VSIX` command +within VS Code, or from the command line via: + + $ code --install-extension /path/to/rust-analyzer.vsix + +If you are running an unsupported platform, you can install +`rust-analyzer-no-server.vsix` and compile or obtain a server binary. +Copy the server anywhere, then add the path to your settings.json, for +example: + + { "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } + +### Building From Source + +Both the server and the Code plugin can be installed from source: + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install + +You’ll need Cargo, nodejs (matching a supported version of VS Code) and +npm for this. + +Note that installing via `xtask install` does not work for VS Code +Remote, instead you’ll need to install the `.vsix` manually. + +If you’re not using Code, you can compile and install only the LSP +server: + + $ cargo xtask install --server + +Make sure that `.cargo/bin` is in `$PATH` and precedes paths where +`rust-analyzer` may also be installed. Specifically, `rustup` includes a +proxy called `rust-analyzer`, which can cause problems if you’re +planning to use a source build or even a downloaded binary. + +## rust-analyzer Language Server Binary + +Other editors generally require the `rust-analyzer` binary to be in +`$PATH`. You can download pre-built binaries from the +[releases](https://github.com/rust-lang/rust-analyzer/releases) page. +You will need to uncompress and rename the binary for your platform, +e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to +`rust-analyzer`, make it executable, then move it into a directory in +your `$PATH`. + +On Linux to install the `rust-analyzer` binary into `~/.local/bin`, +these commands should work: + + $ mkdir -p ~/.local/bin + $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer + $ chmod +x ~/.local/bin/rust-analyzer + +Make sure that `~/.local/bin` is listed in the `$PATH` variable and use +the appropriate URL if you’re not on a `x86-64` system. + +You don’t have to use `~/.local/bin`, any other path like `~/.cargo/bin` +or `/usr/local/bin` will work just as well. + +Alternatively, you can install it from source using the command below. +You’ll need the latest stable version of the Rust toolchain. + + $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer + $ cargo xtask install --server + +If your editor can’t find the binary even though the binary is on your +`$PATH`, the likely explanation is that it doesn’t see the same `$PATH` +as the shell, see [this +issue](https://github.com/rust-lang/rust-analyzer/issues/1811). On Unix, +running the editor from a shell or changing the `.desktop` file to set +the environment should help. + +### rustup + +`rust-analyzer` is available in `rustup`: + + $ rustup component add rust-analyzer + +### Arch Linux + +The `rust-analyzer` binary can be installed from the repos or AUR (Arch +User Repository): + +- [`rust-analyzer`](https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/) + (built from latest tagged source) + +- [`rust-analyzer-git`](https://aur.archlinux.org/packages/rust-analyzer-git) + (latest Git version) + +Install it with pacman, for example: + + $ pacman -S rust-analyzer + +### Gentoo Linux + +`rust-analyzer` is installed when the `rust-analyzer` use flag is set for dev-lang/rust or dev-lang/rust-bin. You also need to set the `rust-src` use flag. + +### macOS + +The `rust-analyzer` binary can be installed via +[Homebrew](https://brew.sh/). + + $ brew install rust-analyzer + +### Windows + +It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. +Download links can be found +[here](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist). + +## VS Code or VSCodium in Flatpak + +Setting up `rust-analyzer` with a Flatpak version of Code is not trivial +because of the Flatpak sandbox. While the sandbox can be disabled for +some directories, `/usr/bin` will always be mounted under +`/run/host/usr/bin`. This prevents access to the system’s C compiler, a +system-wide installation of Rust, or any other libraries you might want +to link to. Some compilers and libraries can be acquired as Flatpak +SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or +`org.freedesktop.Sdk.Extension.llvm15`. + +If you use a Flatpak SDK for Rust, it must be in your `PATH`: + + * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` + * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) + +If you want to use Flatpak in combination with `rustup`, the following +steps might help: + +- both Rust and `rustup` have to be installed using + <https://rustup.rs>. Distro packages *will not* work. + +- you need to launch Code, open a terminal and run `echo $PATH` + +- using + [Flatseal](https://flathub.org/apps/details/com.github.tchx84.Flatseal), + you must add an environment variable called `PATH`. Set its value to + the output from above, appending `:~/.cargo/bin`, where `~` is the + path to your home directory. You must replace `~`, as it won’t be + expanded otherwise. + +- while Flatseal is open, you must enable access to "All user files" + +A C compiler should already be available via `org.freedesktop.Sdk`. Any +other tools or libraries you will need to acquire from Flatpak. + +## Emacs + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +To use `rust-analyzer`, you need to install and enable one of the two +popular LSP client implementations for Emacs, +[Eglot](https://github.com/joaotavora/eglot) or [LSP +Mode](https://github.com/emacs-lsp/lsp-mode). Both enable +`rust-analyzer` by default in rust buffers if it is available. + +### Eglot + +Eglot is the more minimalistic and lightweight LSP client for Emacs, +integrates well with existing Emacs functionality and is built into +Emacs starting from release 29. + +After installing Eglot, e.g. via `M-x package-install` (not needed from +Emacs 29), you can enable it via the `M-x eglot` command or load it +automatically in `rust-mode` via + + (add-hook 'rust-mode-hook 'eglot-ensure) + +To enable clippy, you will need to configure the initialization options +to pass the `check.command` setting. + + (add-to-list 'eglot-server-programs + '((rust-ts-mode rust-mode) . + ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) + +For more detailed instructions and options see the [Eglot +manual](https://joaotavora.github.io/eglot) (also available from Emacs +via `M-x info`) and the [Eglot +readme](https://github.com/joaotavora/eglot/blob/master/README.md). + +Eglot does not support the rust-analyzer extensions to the +language-server protocol and does not aim to do so in the future. The +[eglot-x](https://github.com/nemethf/eglot-x#rust-analyzer-extensions) +package adds experimental support for those LSP extensions. + +### LSP Mode + +LSP-mode is the original LSP-client for emacs. Compared to Eglot it has +a larger codebase and supports more features, like LSP protocol +extensions. With extension packages like [LSP +UI](https://github.com/emacs-lsp/lsp-mode) it offers a lot of visual +eyecandy. Further it integrates well with [DAP +mode](https://github.com/emacs-lsp/dap-mode) for support of the Debug +Adapter Protocol. + +You can install LSP-mode via `M-x package-install` and then run it via +the `M-x lsp` command or load it automatically in rust buffers with + + (add-hook 'rust-mode-hook 'lsp-deferred) + +For more information on how to set up LSP mode and its extension package +see the instructions in the [LSP mode +manual](https://emacs-lsp.github.io/lsp-mode/page/installation). Also +see the [rust-analyzer +section](https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/) +for `rust-analyzer` specific options and commands, which you can +optionally bind to keys. + +Note the excellent +[guide](https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/) from +[@rksm](https://github.com/rksm) on how to set-up Emacs for Rust +development with LSP mode and several other packages. + +## Vim/Neovim + +Prerequisites: You have installed the [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). Not needed if the +extension can install/update it on its own, coc-rust-analyzer is one +example. + +There are several LSP client implementations for Vim or Neovim: + +### coc-rust-analyzer + +1. Install coc.nvim by following the instructions at + [coc.nvim](https://github.com/neoclide/coc.nvim) (Node.js required) + +2. Run `:CocInstall coc-rust-analyzer` to install + [coc-rust-analyzer](https://github.com/fannheyward/coc-rust-analyzer), + this extension implements *most* of the features supported in the + VSCode extension: + + - automatically install and upgrade stable/nightly releases + + - same configurations as VSCode extension, + `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. + + - same commands too, `rust-analyzer.analyzerStatus`, + `rust-analyzer.ssr` etc. + + - inlay hints for variables and method chaining, *Neovim Only* + +Note: for code actions, use `coc-codeaction-cursor` and +`coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` +are unlikely to be useful. + +### LanguageClient-neovim + +1. Install LanguageClient-neovim by following the instructions + [here](https://github.com/autozimu/LanguageClient-neovim) + + - The GitHub project wiki has extra tips on configuration + +2. Configure by adding this to your Vim/Neovim config file (replacing + the existing Rust-specific line if it exists): + + let g:LanguageClient_serverCommands = { + \ 'rust': ['rust-analyzer'], + \ } + +### YouCompleteMe + +Install YouCompleteMe by following the instructions +[here](https://github.com/ycm-core/YouCompleteMe#installation). + +rust-analyzer is the default in ycm, it should work out of the box. + +### ALE + +To use the LSP server in [ale](https://github.com/dense-analysis/ale): + + let g:ale_linters = {'rust': ['analyzer']} + +### nvim-lsp + +Neovim 0.5 has built-in language server support. For a quick start +configuration of rust-analyzer, use +[neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig#rust_analyzer). +Once `neovim/nvim-lspconfig` is installed, use +`lua require'lspconfig'.rust_analyzer.setup({})` in your `init.vim`. + +You can also pass LSP settings to the server: + + lua << EOF + local lspconfig = require'lspconfig' + + local on_attach = function(client) + require'completion'.on_attach(client) + end + + lspconfig.rust_analyzer.setup({ + on_attach = on_attach, + settings = { + ["rust-analyzer"] = { + imports = { + granularity = { + group = "module", + }, + prefix = "self", + }, + cargo = { + buildScripts = { + enable = true, + }, + }, + procMacro = { + enable = true + }, + } + } + }) + EOF + +If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: + +```vim +lspconfig.rust_analyzer.setup({ + on_attach = function(client, bufnr) + vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) + end +}) +``` + +Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to +edit the file to trigger a re-render. + +See <https://sharksforarms.dev/posts/neovim-rust/> for more tips on +getting started. + +Check out <https://github.com/mrcjkb/rustaceanvim> for a batteries +included rust-analyzer setup for Neovim. + +### vim-lsp + +vim-lsp is installed by following [the plugin +instructions](https://github.com/prabirshrestha/vim-lsp). It can be as +simple as adding this line to your `.vimrc`: + + Plug 'prabirshrestha/vim-lsp' + +Next you need to register the `rust-analyzer` binary. If it is avim.lspvailable +in `$PATH`, you may want to add this to your `.vimrc`: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ }) + endif + +There is no dedicated UI for the server configuration, so you would need +to send any options as a value of the `initialization_options` field, as +described in the [Configuration](#configuration) section. Here is an +example of how to enable the proc-macro support: + + if executable('rust-analyzer') + au User lsp_setup call lsp#register_server({ + \ 'name': 'Rust Language Server', + \ 'cmd': {server_info->['rust-analyzer']}, + \ 'whitelist': ['rust'], + \ 'initialization_options': { + \ 'cargo': { + \ 'buildScripts': { + \ 'enable': v:true, + \ }, + \ }, + \ 'procMacro': { + \ 'enable': v:true, + \ }, + \ }, + \ }) + endif + +## Sublime Text + +### Sublime Text 4: + +- Follow the instructions in + [LSP-rust-analyzer](https://github.com/sublimelsp/LSP-rust-analyzer). + +Install +[LSP-file-watcher-chokidar](https://packagecontrol.io/packages/LSP-file-watcher-chokidar) +to enable file watching (`workspace/didChangeWatchedFiles`). + +### Sublime Text 3: + +- Install the [`rust-analyzer` + binary](#rust-analyzer-language-server-binary). + +- Install the [LSP package](https://packagecontrol.io/packages/LSP). + +- From the command palette, run `LSP: Enable Language Server Globally` + and select `rust-analyzer`. + +If it worked, you should see "rust-analyzer, Line X, Column Y" on the +left side of the status bar, and after waiting a bit, functionalities +like tooltips on hovering over variables should become available. + +If you get an error saying `No such file or directory: 'rust-analyzer'`, +see the [`rust-analyzer` binary](#rust-analyzer-language-server-binary) +section on installing the language server binary. + +## GNOME Builder + +GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. If +the LSP binary is not available, GNOME Builder can install it when +opening a Rust file. + +## Eclipse IDE + +Support for Rust development in the Eclipse IDE is provided by [Eclipse +Corrosion](https://github.com/eclipse/corrosion). If available in PATH +or in some standard location, `rust-analyzer` is detected and powers +editing of Rust files without further configuration. If `rust-analyzer` +is not detected, Corrosion will prompt you for configuration of your +Rust toolchain and language server with a link to the *Window > +Preferences > Rust* preference page; from here a button allows to +download and configure `rust-analyzer`, but you can also reference +another installation. You’ll need to close and reopen all .rs and Cargo +files, or to restart the IDE, for this change to take effect. + +## Kate Text Editor + +Support for the language server protocol is built into Kate through the +LSP plugin, which is included by default. It is preconfigured to use +rust-analyzer for Rust sources since Kate 21.12. + +To change rust-analyzer config options, start from the following example +and put it into Kate’s "User Server Settings" tab (located under the LSP +Client settings): + + { + "servers": { + "rust": { + "initializationOptions": { + "cachePriming": { + "enable": false + }, + "check": { + "allTargets": false + }, + "checkOnSave": false + } + } + } + } + +Then click on apply, and restart the LSP server for your rust project. + +## juCi++ + +[juCi++](https://gitlab.com/cppit/jucipp) has built-in support for the +language server protocol, and since version 1.7.0 offers installation of +both Rust and rust-analyzer when opening a Rust file. + +## Kakoune + +[Kakoune](https://kakoune.org/) supports LSP with the help of +[`kak-lsp`](https://github.com/kak-lsp/kak-lsp). Follow the +[instructions](https://github.com/kak-lsp/kak-lsp#installation) to +install `kak-lsp`. To configure `kak-lsp`, refer to the [configuration +section](https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp) which +is basically about copying the [configuration +file](https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml) in +the right place (latest versions should use `rust-analyzer` by default). + +Finally, you need to configure Kakoune to talk to `kak-lsp` (see [Usage +section](https://github.com/kak-lsp/kak-lsp#usage)). A basic +configuration will only get you LSP but you can also activate inlay +diagnostics and auto-formatting on save. The following might help you +get all of this. + + eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. + hook global WinSetOption filetype=rust %{ + # Enable LSP + lsp-enable-window + + # Auto-formatting on save + hook window BufWritePre .* lsp-formatting-sync + + # Configure inlay hints (only on save) + hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints + hook -once -always window WinSetOption filetype=.* %{ + remove-hooks window rust-inlay-hints + } + } + +## Helix + +[Helix](https://docs.helix-editor.com/) supports LSP by default. +However, it won’t install `rust-analyzer` automatically. You can follow +instructions for installing [`rust-analyzer` +binary](#rust-analyzer-language-server-binary). + +## Visual Studio 2022 + +There are multiple rust-analyzer extensions for Visual Studio 2022 on +Windows: + +### rust-analyzer.vs + +(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 +International) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) + +[GitHub](https://github.com/kitamstudios/rust-analyzer/) + +Support for Rust development in the Visual Studio IDE is enabled by the +[rust-analyzer](https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer) +package. Either click on the download link or install from IDE’s +extension manager. For now [Visual Studio +2022](https://visualstudio.microsoft.com/downloads/) is required. All +editions are supported viz. Community, Professional & Enterprise. The +package aims to provide 0-friction installation and therefore comes +loaded with most things required including rust-analyzer binary. If +anything it needs is missing, appropriate errors / warnings will guide +the user. E.g. cargo.exe needs to be in path and the package will tell +you as much. This package is under rapid active development. So if you +encounter any issues please file it at +[rust-analyzer.vs](https://github.com/kitamstudios/rust-analyzer/). + +### VS\_RustAnalyzer + +(License: GPL) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer) + +[GitHub](https://github.com/cchharris/VS-RustAnalyzer) + +### SourceGear Rust + +(License: closed source) + +[Visual Studio +Marketplace](https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust) + +[GitHub (docs, issues, +discussions)](https://github.com/sourcegear/rust-vs-extension) + +- Free (no-cost) + +- Supports all editions of Visual Studio 2022 on Windows: Community, + Professional, or Enterprise + +## Lapce + +[Lapce](https://lapce.dev/) has a Rust plugin which you can install +directly. Unfortunately, it downloads an old version of `rust-analyzer`, +but you can set the server path under Settings. + +## Crates + +There is a package named `ra_ap_rust_analyzer` available on +[crates.io](https://crates.io/crates/ra_ap_rust-analyzer), for someone +who wants to use it programmatically. + +For more details, see [the publish +workflow](https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml). + +## Zed + +[Zed](https://zed.dev) has native `rust-analyzer` support. If the LSP +binary is not available, Zed can install it when opening a Rust file. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md new file mode 100644 index 0000000000000..151f8758a176c --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -0,0 +1,246 @@ +# Non-Cargo Based Projects + +rust-analyzer does not require Cargo. However, if you use some other +build system, you’ll have to describe the structure of your project for +rust-analyzer in the `rust-project.json` format: + +```typescript +interface JsonProject { + /// Path to the sysroot directory. + /// + /// The sysroot is where rustc looks for the + /// crates that are built-in to rust, such as + /// std. + /// + /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root + /// + /// To see the current value of sysroot, you + /// can query rustc: + /// + /// ``` + /// $ rustc --print sysroot + /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin + /// ``` + sysroot?: string; + /// Path to the directory with *source code* of + /// sysroot crates. + /// + /// By default, this is `lib/rustlib/src/rust/library` + /// relative to the sysroot. + /// + /// It should point to the directory where std, + /// core, and friends can be found: + /// + /// https://github.com/rust-lang/rust/tree/master/library. + /// + /// If provided, rust-analyzer automatically adds + /// dependencies on sysroot crates. Conversely, + /// if you omit this path, you can specify sysroot + /// dependencies yourself and, for example, have + /// several different "sysroots" in one graph of + /// crates. + sysroot_src?: string; + /// List of groups of common cfg values, to allow + /// sharing them between crates. + /// + /// Maps from group name to its cfgs. Cfg follow + /// the same format as `Crate.cfg`. + cfg_groups?: { [key: string]: string[]; }; + /// The set of crates comprising the current + /// project. Must include all transitive + /// dependencies as well as sysroot crate (libstd, + /// libcore and such). + crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; +} + +interface Crate { + /// Optional crate name used for display purposes, + /// without affecting semantics. See the `deps` + /// key for semantically-significant crate names. + display_name?: string; + /// Path to the root module of the crate. + root_module: string; + /// Edition of the crate. + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; + /// Dependencies + deps: Dep[]; + /// Should this crate be treated as a member of + /// current "workspace". + /// + /// By default, inferred from the `root_module` + /// (members are the crates which reside inside + /// the directory opened in the editor). + /// + /// Set this to `false` for things like standard + /// library and 3rd party crates to enable + /// performance optimizations (rust-analyzer + /// assumes that non-member crates don't change). + is_workspace_member?: boolean; + /// Optionally specify the (super)set of `.rs` + /// files comprising this crate. + /// + /// By default, rust-analyzer assumes that only + /// files under `root_module.parent` can belong + /// to a crate. `include_dirs` are included + /// recursively, unless a subdirectory is in + /// `exclude_dirs`. + /// + /// Different crates can share the same `source`. + /// + /// If two crates share an `.rs` file in common, + /// they *must* have the same `source`. + /// rust-analyzer assumes that files from one + /// source can't refer to files in another source. + source?: { + include_dirs: string[]; + exclude_dirs: string[]; + }; + /// List of cfg groups this crate inherits. + /// + /// All cfg in these groups will be concatenated to + /// `cfg`. It is impossible to replace a value from + /// the groups. + cfg_groups?: string[]; + /// The set of cfgs activated for a given crate, like + /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. + cfg: string[]; + /// Target tuple for this Crate. + /// + /// Used when running `rustc --print cfg` + /// to get target-specific cfgs. + target?: string; + /// Environment variables, used for + /// the `env!` macro + env: { [key: string]: string; }; + + /// Whether the crate is a proc-macro crate. + is_proc_macro: boolean; + /// For proc-macro crates, path to compiled + /// proc-macro (.so file). + proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; +} + +interface Dep { + /// Index of a crate in the `crates` array. + crate: number; + /// Name as should appear in the (implicit) + /// `extern crate name` declaration. + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; +} +``` + +This format is provisional and subject to change. Specifically, the +`roots` setup will be different eventually. + +There are three ways to feed `rust-project.json` to rust-analyzer: + +- Place `rust-project.json` file at the root of the project, and + rust-analyzer will discover it. + +- Specify + `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in + the settings (and make sure that your LSP client sends settings as a + part of initialize request). + +- Specify + `"rust-analyzer.linkedProjects": [ { "roots": […], "crates": […] }]` + inline. + +Relative paths are interpreted relative to `rust-project.json` file +location or (for inline JSON) relative to `rootUri`. + +You can set the `RA_LOG` environment variable to `rust_analyzer=info` to +inspect how rust-analyzer handles config and project loading. + +Note that calls to `cargo check` are disabled when using +`rust-project.json` by default, so compilation errors and warnings will +no longer be sent to your LSP client. To enable these compilation errors +you will need to specify explicitly what command rust-analyzer should +run to perform the checks using the +`rust-analyzer.check.overrideCommand` configuration. As an example, the +following configuration explicitly sets `cargo check` as the `check` +command. + + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + +`check.overrideCommand` requires the command specified to output json +error messages for rust-analyzer to consume. The `--message-format=json` +flag does this for `cargo check` so whichever command you use must also +output errors in this format. See the [Configuration](#_configuration) +section for more information. diff --git a/src/tools/rust-analyzer/docs/book/src/privacy.md b/src/tools/rust-analyzer/docs/book/src/privacy.md new file mode 100644 index 0000000000000..602c68d6f67d8 --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/privacy.md @@ -0,0 +1,15 @@ +# Privacy + +The LSP server performs no network access in itself, but runs +`cargo metadata` which will update or download the crate registry and +the source code of the project dependencies. If enabled (the default), +build scripts and procedural macros can do anything. + +The Code extension does not access the network. + +Any other editor plugins are not under the control of the +`rust-analyzer` developers. For any privacy concerns, you should check +with their respective developers. + +For `rust-analyzer` developers, `cargo xtask release` uses the GitHub +API to put together the release notes. diff --git a/src/tools/rust-analyzer/docs/book/src/security.md b/src/tools/rust-analyzer/docs/book/src/security.md new file mode 100644 index 0000000000000..1444af03248da --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/security.md @@ -0,0 +1,19 @@ +# Security + +At the moment, rust-analyzer assumes that all code is trusted. Here is a +**non-exhaustive** list of ways to make rust-analyzer execute arbitrary +code: + +- proc macros and build scripts are executed by default + +- `.cargo/config` can override `rustc` with an arbitrary executable + +- `rust-toolchain.toml` can override `rustc` with an arbitrary + executable + +- VS Code plugin reads configuration from project directory, and that + can be used to override paths to various executables, like `rustfmt` + or `rust-analyzer` itself. + +- rust-analyzer’s syntax trees library uses a lot of `unsafe` and + hasn’t been properly audited for memory safety. \ No newline at end of file diff --git a/src/tools/rust-analyzer/docs/book/src/troubleshooting.md b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md new file mode 100644 index 0000000000000..4092b9de990cc --- /dev/null +++ b/src/tools/rust-analyzer/docs/book/src/troubleshooting.md @@ -0,0 +1,50 @@ +# Troubleshooting + +Start with looking at the rust-analyzer version. Try **rust-analyzer: +Show RA Version** in VS Code (using **Command Palette** feature +typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the +command line. If the date is more than a week ago, it’s better to update +rust-analyzer version. + +The next thing to check would be panic messages in rust-analyzer’s log. +Log messages are printed to stderr, in VS Code you can see them in the +`Output > Rust Analyzer Language Server` tab of the panel. To see more +logs, set the `RA_LOG=info` environment variable, this can be done +either by setting the environment variable manually or by using +`rust-analyzer.server.extraEnv`, note that both of these approaches +require the server to be restarted. + +To fully capture LSP messages between the editor and the server, run +the `rust-analyzer: Toggle LSP Logs` command and check `Output > Rust +Analyzer Language Server Trace`. + +The root cause for many "nothing works" problems is that rust-analyzer +fails to understand the project structure. To debug that, first note the +`rust-analyzer` section in the status bar. If it has an error icon and +red, that’s the problem (hover will have somewhat helpful error +message). **rust-analyzer: Status** prints dependency information for +the current file. Finally, `RA_LOG=project_model=debug` enables verbose +logs during project loading. + +If rust-analyzer outright crashes, try running +`rust-analyzer analysis-stats /path/to/project/directory/` on the +command line. This command type checks the whole project in batch mode +bypassing LSP machinery. + +When filing issues, it is useful (but not necessary) to try to minimize +examples. An ideal bug reproduction looks like this: + +```shell +$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash +$ rust-analyzer --version +rust-analyzer dd12184e4 2021-05-08 dev +$ rust-analyzer analysis-stats . +💀 💀 💀 +``` + +It is especially useful when the `repo` doesn’t use external crates or +the standard library. + +If you want to go as far as to modify the source code to debug the +problem, be sure to take a look at the [dev +docs](https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev)! diff --git a/src/tools/rust-analyzer/docs/dev/style.md b/src/tools/rust-analyzer/docs/dev/style.md index 4c5299bde3e91..51b60ab2ebb23 100644 --- a/src/tools/rust-analyzer/docs/dev/style.md +++ b/src/tools/rust-analyzer/docs/dev/style.md @@ -873,7 +873,7 @@ Use `anyhow::format_err!` rather than `anyhow::anyhow`. **Rationale:** consistent, boring, avoids stuttering. There's no specific guidance on the formatting of error messages, see [anyhow/#209](https://github.com/dtolnay/anyhow/issues/209). -Do not end error and context messages with `.` though. +Do not end error and context messages with `.` though. ## Early Returns @@ -1172,7 +1172,7 @@ MergeBehavior::Last => { **Rationale:** writing a sentence (or maybe even a paragraph) rather just "a comment" creates a more appropriate frame of mind. It tricks you into writing down more of the context you keep in your head while coding. -For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. +For `.md` files prefer a sentence-per-line format, don't wrap lines. If the line is too long, you want to split the sentence in two :-) **Rationale:** much easier to edit the text and read the diff, see [this link](https://asciidoctor.org/docs/asciidoc-recommended-practices/#one-sentence-per-line). diff --git a/src/tools/rust-analyzer/docs/user/.gitignore b/src/tools/rust-analyzer/docs/user/.gitignore deleted file mode 100644 index c32b1bcec2eef..0000000000000 --- a/src/tools/rust-analyzer/docs/user/.gitignore +++ /dev/null @@ -1 +0,0 @@ -manual.html diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc deleted file mode 100644 index b33de1956b8db..0000000000000 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ /dev/null @@ -1,1197 +0,0 @@ -[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: -+ --- -Whether to insert #[must_use] when generating `as_` methods -for enum variants. --- -[[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: -+ --- -Placeholder expression to use for missing expressions in assists. --- -[[rust-analyzer.assist.termSearch.borrowcheck]]rust-analyzer.assist.termSearch.borrowcheck (default: `true`):: -+ --- -Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. --- -[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `1800`):: -+ --- -Term search fuel in "units of work" for assists (Defaults to 1800). --- -[[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`):: -+ --- -Warm up caches on project load. --- -[[rust-analyzer.cachePriming.numThreads]]rust-analyzer.cachePriming.numThreads (default: `"physical"`):: -+ --- -How many worker threads to handle priming caches. The default `0` means to pick automatically. --- -[[rust-analyzer.cargo.allTargets]]rust-analyzer.cargo.allTargets (default: `true`):: -+ --- -Pass `--all-targets` to cargo invocation. --- -[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: -+ --- -Automatically refresh project info via `cargo metadata` on -`Cargo.toml` or `.cargo/config.toml` changes. --- -[[rust-analyzer.cargo.buildScripts.enable]]rust-analyzer.cargo.buildScripts.enable (default: `true`):: -+ --- -Run build scripts (`build.rs`) for more precise code analysis. --- -[[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 Rust workspace with the -workspace as the working directory. -If `once` is set, the command will be executed once with the opened project as the -working directory. -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`):: -+ --- -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 -option. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - -By default, a cargo invocation will be constructed for the configured -targets and features, with the following base command line: - -```bash -cargo check --quiet --workspace --message-format=json --all-targets --keep-going -``` -. --- -[[rust-analyzer.cargo.buildScripts.rebuildOnSave]]rust-analyzer.cargo.buildScripts.rebuildOnSave (default: `true`):: -+ --- -Rerun proc-macros building/build-scripts running when proc-macro -or build-script sources change and are saved. --- -[[rust-analyzer.cargo.buildScripts.useRustcWrapper]]rust-analyzer.cargo.buildScripts.useRustcWrapper (default: `true`):: -+ --- -Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to -avoid checking unnecessary things. --- -[[rust-analyzer.cargo.cfgs]]rust-analyzer.cargo.cfgs:: -+ --- -Default: ----- -[ - "debug_assertions", - "miri" -] ----- -List of cfg options to enable with the given values. - --- -[[rust-analyzer.cargo.extraArgs]]rust-analyzer.cargo.extraArgs (default: `[]`):: -+ --- -Extra arguments that are passed to every cargo invocation. --- -[[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: `[]`):: -+ --- -List of features to activate. - -Set this to `"all"` to pass `--all-features` to cargo. --- -[[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: -+ --- -Whether to pass `--no-default-features` to cargo. --- -[[rust-analyzer.cargo.sysroot]]rust-analyzer.cargo.sysroot (default: `"discover"`):: -+ --- -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.sysrootSrc]]rust-analyzer.cargo.sysrootSrc (default: `null`):: -+ --- -Relative path to the sysroot library sources. If left unset, this will default to -`{cargo.sysroot}/lib/rustlib/src/rust/library`. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: -+ --- -Compilation target override (target tuple). --- -[[rust-analyzer.cargo.targetDir]]rust-analyzer.cargo.targetDir (default: `null`):: -+ --- -Optional path to a rust-analyzer specific target directory. -This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro -building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - -Set to `true` to use a subdirectory of the existing target directory or -set to a path relative to the workspace to use that path. --- -[[rust-analyzer.cfg.setTest]]rust-analyzer.cfg.setTest (default: `true`):: -+ --- -Set `cfg(test)` for local crates. Defaults to true. --- -[[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`):: -+ --- -Run the check command for diagnostics on save. --- -[[rust-analyzer.check.allTargets]]rust-analyzer.check.allTargets (default: `null`):: -+ --- -Check all targets and tests (`--all-targets`). Defaults to -`#rust-analyzer.cargo.allTargets#`. --- -[[rust-analyzer.check.command]]rust-analyzer.check.command (default: `"check"`):: -+ --- -Cargo command to use for `cargo check`. --- -[[rust-analyzer.check.extraArgs]]rust-analyzer.check.extraArgs (default: `[]`):: -+ --- -Extra arguments for `cargo check`. --- -[[rust-analyzer.check.extraEnv]]rust-analyzer.check.extraEnv (default: `{}`):: -+ --- -Extra environment variables that will be set when running `cargo check`. -Extends `#rust-analyzer.cargo.extraEnv#`. --- -[[rust-analyzer.check.features]]rust-analyzer.check.features (default: `null`):: -+ --- -List of features to activate. Defaults to -`#rust-analyzer.cargo.features#`. - -Set to `"all"` to pass `--all-features` to Cargo. --- -[[rust-analyzer.check.ignore]]rust-analyzer.check.ignore (default: `[]`):: -+ --- -List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - -For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... --- -[[rust-analyzer.check.invocationStrategy]]rust-analyzer.check.invocationStrategy (default: `"per_workspace"`):: -+ --- -Specifies the invocation strategy to use when running the check command. -If `per_workspace` is set, the command will be executed for each workspace. -If `once` is set, the command will be executed once. -This config only has an effect when `#rust-analyzer.check.overrideCommand#` -is set. --- -[[rust-analyzer.check.noDefaultFeatures]]rust-analyzer.check.noDefaultFeatures (default: `null`):: -+ --- -Whether to pass `--no-default-features` to Cargo. Defaults to -`#rust-analyzer.cargo.noDefaultFeatures#`. --- -[[rust-analyzer.check.overrideCommand]]rust-analyzer.check.overrideCommand (default: `null`):: -+ --- -Override the command rust-analyzer uses instead of `cargo check` for -diagnostics on save. The command is required to output json and -should therefore include `--message-format=json` or a similar option -(if your client supports the `colorDiagnosticOutput` experimental -capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - -If you're changing this because you're using some tool wrapping -Cargo, you might also want to change -`#rust-analyzer.cargo.buildScripts.overrideCommand#`. - -If there are multiple linked projects/workspaces, this command is invoked for -each of them, with the working directory being the workspace root -(i.e., the folder containing the `Cargo.toml`). This can be overwritten -by changing `#rust-analyzer.check.invocationStrategy#`. - -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. - -An example command would be: - -```bash -cargo check --workspace --message-format=json --all-targets -``` -. --- -[[rust-analyzer.check.targets]]rust-analyzer.check.targets (default: `null`):: -+ --- -Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - -Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. -`["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - -Aliased as `"checkOnSave.targets"`. --- -[[rust-analyzer.check.workspace]]rust-analyzer.check.workspace (default: `true`):: -+ --- -Whether `--workspace` should be passed to `cargo check`. -If false, `-p <package>` will be passed instead if applicable. In case it is not, no -check will be performed. --- -[[rust-analyzer.completion.addSemicolonToUnit]]rust-analyzer.completion.addSemicolonToUnit (default: `true`):: -+ --- -Whether to automatically add a semicolon when completing unit-returning functions. - -In `match` arms it completes a comma instead. --- -[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. --- -[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. --- -[[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically add imports when completed. -Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. --- -[[rust-analyzer.completion.autoimport.exclude]]rust-analyzer.completion.autoimport.exclude:: -+ --- -Default: ----- -[ - { - "path": "core::borrow::Borrow", - "type": "methods" - }, - { - "path": "core::borrow::BorrowMut", - "type": "methods" - } -] ----- -A list of full paths to items to exclude from auto-importing completions. - -Traits in this list won't have their methods suggested in completions unless the trait -is in scope. - -You can either specify a string path which defaults to type "always" or use the more verbose -form `{ "path": "path::to::item", type: "always" }`. - -For traits the type "methods" can be used to only exclude the methods but not the trait itself. - -This setting also inherits `#rust-analyzer.completion.excludeTraits#`. - --- -[[rust-analyzer.completion.autoself.enable]]rust-analyzer.completion.autoself.enable (default: `true`):: -+ --- -Toggles the additional completions that automatically show method calls and field accesses -with `self` prefixed to them when inside a method. --- -[[rust-analyzer.completion.callable.snippets]]rust-analyzer.completion.callable.snippets (default: `"fill_arguments"`):: -+ --- -Whether to add parenthesis and argument snippets when completing function. --- -[[rust-analyzer.completion.excludeTraits]]rust-analyzer.completion.excludeTraits (default: `[]`):: -+ --- -A list of full paths to traits whose methods to exclude from completion. - -Methods from these traits won't be completed, even if the trait is in scope. However, they will still be suggested on expressions whose type is `dyn Trait`, `impl Trait` or `T where T: Trait`. - -Note that the trait themselves can still be completed. --- -[[rust-analyzer.completion.fullFunctionSignatures.enable]]rust-analyzer.completion.fullFunctionSignatures.enable (default: `false`):: -+ --- -Whether to show full function/method signatures in completion docs. --- -[[rust-analyzer.completion.hideDeprecated]]rust-analyzer.completion.hideDeprecated (default: `false`):: -+ --- -Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. --- -[[rust-analyzer.completion.limit]]rust-analyzer.completion.limit (default: `null`):: -+ --- -Maximum number of completions to return. If `None`, the limit is infinite. --- -[[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: -+ --- -Whether to show postfix snippets like `dbg`, `if`, `not`, etc. --- -[[rust-analyzer.completion.privateEditable.enable]]rust-analyzer.completion.privateEditable.enable (default: `false`):: -+ --- -Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. --- -[[rust-analyzer.completion.snippets.custom]]rust-analyzer.completion.snippets.custom:: -+ --- -Default: ----- -{ - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - } -} ----- -Custom completion snippets. - --- -[[rust-analyzer.completion.termSearch.enable]]rust-analyzer.completion.termSearch.enable (default: `false`):: -+ --- -Whether to enable term search based snippets like `Some(foo.bar().baz())`. --- -[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `1000`):: -+ --- -Term search fuel in "units of work" for autocompletion (Defaults to 1000). --- -[[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: -+ --- -List of rust-analyzer diagnostics to disable. --- -[[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: -+ --- -Whether to show native rust-analyzer diagnostics. --- -[[rust-analyzer.diagnostics.experimental.enable]]rust-analyzer.diagnostics.experimental.enable (default: `false`):: -+ --- -Whether to show experimental rust-analyzer diagnostics that might -have more false positives than usual. --- -[[rust-analyzer.diagnostics.remapPrefix]]rust-analyzer.diagnostics.remapPrefix (default: `{}`):: -+ --- -Map of prefixes to be substituted when parsing diagnostic file paths. -This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. --- -[[rust-analyzer.diagnostics.styleLints.enable]]rust-analyzer.diagnostics.styleLints.enable (default: `false`):: -+ --- -Whether to run additional style lints. --- -[[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: -+ --- -List of warnings that should be displayed with hint severity. - -The warnings will be indicated by faded text or three dots in code -and will not show up in the `Problems Panel`. --- -[[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: -+ --- -List of warnings that should be displayed with info severity. - -The warnings will be indicated by a blue squiggly underline in code -and a blue icon in the `Problems Panel`. --- -[[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: -+ --- -These directories will be ignored by rust-analyzer. They are -relative to the workspace root, and globs are not supported. You may -also need to add the folders to Code's `files.watcherExclude`. --- -[[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: -+ --- -Controls file watching implementation. --- -[[rust-analyzer.highlightRelated.breakPoints.enable]]rust-analyzer.highlightRelated.breakPoints.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. --- -[[rust-analyzer.highlightRelated.closureCaptures.enable]]rust-analyzer.highlightRelated.closureCaptures.enable (default: `true`):: -+ --- -Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. --- -[[rust-analyzer.highlightRelated.exitPoints.enable]]rust-analyzer.highlightRelated.exitPoints.enable (default: `true`):: -+ --- -Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). --- -[[rust-analyzer.highlightRelated.references.enable]]rust-analyzer.highlightRelated.references.enable (default: `true`):: -+ --- -Enables highlighting of related references while the cursor is on any identifier. --- -[[rust-analyzer.highlightRelated.yieldPoints.enable]]rust-analyzer.highlightRelated.yieldPoints.enable (default: `true`):: -+ --- -Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. --- -[[rust-analyzer.hover.actions.debug.enable]]rust-analyzer.hover.actions.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.enable]]rust-analyzer.hover.actions.enable (default: `true`):: -+ --- -Whether to show HoverActions in Rust files. --- -[[rust-analyzer.hover.actions.gotoTypeDef.enable]]rust-analyzer.hover.actions.gotoTypeDef.enable (default: `true`):: -+ --- -Whether to show `Go to Type Definition` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.implementations.enable]]rust-analyzer.hover.actions.implementations.enable (default: `true`):: -+ --- -Whether to show `Implementations` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.references.enable]]rust-analyzer.hover.actions.references.enable (default: `false`):: -+ --- -Whether to show `References` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.run.enable]]rust-analyzer.hover.actions.run.enable (default: `true`):: -+ --- -Whether to show `Run` action. Only applies when -`#rust-analyzer.hover.actions.enable#` is set. --- -[[rust-analyzer.hover.actions.updateTest.enable]]rust-analyzer.hover.actions.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` action. Only applies when -`#rust-analyzer.hover.actions.enable#` and `#rust-analyzer.hover.actions.run.enable#` are set. --- -[[rust-analyzer.hover.documentation.enable]]rust-analyzer.hover.documentation.enable (default: `true`):: -+ --- -Whether to show documentation on hover. --- -[[rust-analyzer.hover.documentation.keywords.enable]]rust-analyzer.hover.documentation.keywords.enable (default: `true`):: -+ --- -Whether to show keyword hover popups. Only applies when -`#rust-analyzer.hover.documentation.enable#` is set. --- -[[rust-analyzer.hover.links.enable]]rust-analyzer.hover.links.enable (default: `true`):: -+ --- -Use markdown syntax for links on hover. --- -[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`):: -+ --- -Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis. - -This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters. - -The default is 20 characters. --- -[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`):: -+ --- -How to render the align information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.enable]]rust-analyzer.hover.memoryLayout.enable (default: `true`):: -+ --- -Whether to show memory layout data on hover. --- -[[rust-analyzer.hover.memoryLayout.niches]]rust-analyzer.hover.memoryLayout.niches (default: `false`):: -+ --- -How to render the niche information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.offset]]rust-analyzer.hover.memoryLayout.offset (default: `"hexadecimal"`):: -+ --- -How to render the offset information in a memory layout hover. --- -[[rust-analyzer.hover.memoryLayout.size]]rust-analyzer.hover.memoryLayout.size (default: `"both"`):: -+ --- -How to render the size information in a memory layout hover. --- -[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`):: -+ --- -How many variants of an enum to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`):: -+ --- -How many fields of a struct, variant or union to display when hovering on. Show none if empty. --- -[[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: -+ --- -How many associated items of a trait to display when hovering a trait. --- -[[rust-analyzer.imports.granularity.enforce]]rust-analyzer.imports.granularity.enforce (default: `false`):: -+ --- -Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. --- -[[rust-analyzer.imports.granularity.group]]rust-analyzer.imports.granularity.group (default: `"crate"`):: -+ --- -How imports should be grouped into use statements. --- -[[rust-analyzer.imports.group.enable]]rust-analyzer.imports.group.enable (default: `true`):: -+ --- -Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. --- -[[rust-analyzer.imports.merge.glob]]rust-analyzer.imports.merge.glob (default: `true`):: -+ --- -Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. --- -[[rust-analyzer.imports.preferNoStd]]rust-analyzer.imports.preferNoStd (default: `false`):: -+ --- -Prefer to unconditionally use imports of the core and alloc crate, over the std crate. --- -[[rust-analyzer.imports.preferPrelude]]rust-analyzer.imports.preferPrelude (default: `false`):: -+ --- -Whether to prefer import paths containing a `prelude` module. --- -[[rust-analyzer.imports.prefix]]rust-analyzer.imports.prefix (default: `"plain"`):: -+ --- -The path structure for newly inserted paths to use. --- -[[rust-analyzer.imports.prefixExternPrelude]]rust-analyzer.imports.prefixExternPrelude (default: `false`):: -+ --- -Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". --- -[[rust-analyzer.inlayHints.bindingModeHints.enable]]rust-analyzer.inlayHints.bindingModeHints.enable (default: `false`):: -+ --- -Whether to show inlay type hints for binding modes. --- -[[rust-analyzer.inlayHints.chainingHints.enable]]rust-analyzer.inlayHints.chainingHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for method chains. --- -[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`):: -+ --- -Whether to show inlay hints after a closing `}` to indicate what item it belongs to. --- -[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`):: -+ --- -Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 -to always show them). --- -[[rust-analyzer.inlayHints.closureCaptureHints.enable]]rust-analyzer.inlayHints.closureCaptureHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for closure captures. --- -[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for return types of closures. --- -[[rust-analyzer.inlayHints.closureStyle]]rust-analyzer.inlayHints.closureStyle (default: `"impl_fn"`):: -+ --- -Closure notation in type and chaining inlay hints. --- -[[rust-analyzer.inlayHints.discriminantHints.enable]]rust-analyzer.inlayHints.discriminantHints.enable (default: `"never"`):: -+ --- -Whether to show enum variant discriminant hints. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for type adjustments. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe]]rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe (default: `false`):: -+ --- -Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. --- -[[rust-analyzer.inlayHints.expressionAdjustmentHints.mode]]rust-analyzer.inlayHints.expressionAdjustmentHints.mode (default: `"prefix"`):: -+ --- -Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). --- -[[rust-analyzer.inlayHints.genericParameterHints.const.enable]]rust-analyzer.inlayHints.genericParameterHints.const.enable (default: `true`):: -+ --- -Whether to show const generic parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.lifetime.enable]]rust-analyzer.inlayHints.genericParameterHints.lifetime.enable (default: `false`):: -+ --- -Whether to show generic lifetime parameter name inlay hints. --- -[[rust-analyzer.inlayHints.genericParameterHints.type.enable]]rust-analyzer.inlayHints.genericParameterHints.type.enable (default: `false`):: -+ --- -Whether to show generic type parameter name inlay hints. --- -[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`):: -+ --- -Whether to show implicit drop hints. --- -[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`):: -+ --- -Whether to show inlay hints for the implied type parameter `Sized` bound. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: -+ --- -Whether to show inlay type hints for elided lifetimes in function signatures. --- -[[rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames]]rust-analyzer.inlayHints.lifetimeElisionHints.useParameterNames (default: `false`):: -+ --- -Whether to prefer using parameter names as the name for elided lifetime hints if possible. --- -[[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`):: -+ --- -Maximum length for inlay hints. Set to null to have an unlimited length. --- -[[rust-analyzer.inlayHints.parameterHints.enable]]rust-analyzer.inlayHints.parameterHints.enable (default: `true`):: -+ --- -Whether to show function parameter name inlay hints at the call -site. --- -[[rust-analyzer.inlayHints.rangeExclusiveHints.enable]]rust-analyzer.inlayHints.rangeExclusiveHints.enable (default: `false`):: -+ --- -Whether to show exclusive range inlay hints. --- -[[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: -+ --- -Whether to show inlay hints for compiler inserted reborrows. -This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. --- -[[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`):: -+ --- -Whether to render leading colons for type hints, and trailing colons for parameter hints. --- -[[rust-analyzer.inlayHints.typeHints.enable]]rust-analyzer.inlayHints.typeHints.enable (default: `true`):: -+ --- -Whether to show inlay type hints for variables. --- -[[rust-analyzer.inlayHints.typeHints.hideClosureInitialization]]rust-analyzer.inlayHints.typeHints.hideClosureInitialization (default: `false`):: -+ --- -Whether to hide inlay type hints for `let` statements that initialize to a closure. -Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. --- -[[rust-analyzer.inlayHints.typeHints.hideNamedConstructor]]rust-analyzer.inlayHints.typeHints.hideNamedConstructor (default: `false`):: -+ --- -Whether to hide inlay type hints for constructors. --- -[[rust-analyzer.interpret.tests]]rust-analyzer.interpret.tests (default: `false`):: -+ --- -Enables the experimental support for interpreting tests. --- -[[rust-analyzer.joinLines.joinAssignments]]rust-analyzer.joinLines.joinAssignments (default: `true`):: -+ --- -Join lines merges consecutive declaration and initialization of an assignment. --- -[[rust-analyzer.joinLines.joinElseIf]]rust-analyzer.joinLines.joinElseIf (default: `true`):: -+ --- -Join lines inserts else between consecutive ifs. --- -[[rust-analyzer.joinLines.removeTrailingComma]]rust-analyzer.joinLines.removeTrailingComma (default: `true`):: -+ --- -Join lines removes trailing commas. --- -[[rust-analyzer.joinLines.unwrapTrivialBlock]]rust-analyzer.joinLines.unwrapTrivialBlock (default: `true`):: -+ --- -Join lines unwraps trivial blocks. --- -[[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`):: -+ --- -Whether to show `Debug` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: -+ --- -Whether to show CodeLens in Rust files. --- -[[rust-analyzer.lens.implementations.enable]]rust-analyzer.lens.implementations.enable (default: `true`):: -+ --- -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`):: -+ --- -Whether to show `References` lens for Struct, Enum, and Union. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.enumVariant.enable]]rust-analyzer.lens.references.enumVariant.enable (default: `false`):: -+ --- -Whether to show `References` lens for Enum Variants. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.method.enable]]rust-analyzer.lens.references.method.enable (default: `false`):: -+ --- -Whether to show `Method References` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.references.trait.enable]]rust-analyzer.lens.references.trait.enable (default: `false`):: -+ --- -Whether to show `References` lens for Trait. -Only applies when `#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.run.enable]]rust-analyzer.lens.run.enable (default: `true`):: -+ --- -Whether to show `Run` lens. Only applies when -`#rust-analyzer.lens.enable#` is set. --- -[[rust-analyzer.lens.updateTest.enable]]rust-analyzer.lens.updateTest.enable (default: `true`):: -+ --- -Whether to show `Update Test` lens. Only applies when -`#rust-analyzer.lens.enable#` and `#rust-analyzer.lens.run.enable#` are set. --- -[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: -+ --- -Disable project auto-discovery in favor of explicitly specified set -of projects. - -Elements must be paths pointing to `Cargo.toml`, -`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON -objects in `rust-project.json` format. --- -[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`):: -+ --- -Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. --- -[[rust-analyzer.lru.query.capacities]]rust-analyzer.lru.query.capacities (default: `{}`):: -+ --- -Sets the LRU capacity of the specified queries. --- -[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: -+ --- -Whether to show `can't find Cargo.toml` error message. --- -[[rust-analyzer.numThreads]]rust-analyzer.numThreads (default: `null`):: -+ --- -How many worker threads in the main loop. The default `null` means to pick automatically. --- -[[rust-analyzer.procMacro.attributes.enable]]rust-analyzer.procMacro.attributes.enable (default: `true`):: -+ --- -Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. --- -[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `true`):: -+ --- -Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. --- -[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`):: -+ --- -These proc-macros will be ignored when trying to expand them. - -This config takes a map of crate names with the exported proc-macro names to ignore as values. --- -[[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: -+ --- -Internal config, path to proc-macro server executable. --- -[[rust-analyzer.references.excludeImports]]rust-analyzer.references.excludeImports (default: `false`):: -+ --- -Exclude imports from find-all-references. --- -[[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`):: -+ --- -Exclude tests from find-all-references and call-hierarchy. --- -[[rust-analyzer.runnables.command]]rust-analyzer.runnables.command (default: `null`):: -+ --- -Command to be executed instead of 'cargo' for runnables. --- -[[rust-analyzer.runnables.extraArgs]]rust-analyzer.runnables.extraArgs (default: `[]`):: -+ --- -Additional arguments to be passed to cargo for runnables such as -tests or binaries. For example, it may be `--release`. --- -[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs:: -+ --- -Default: ----- -[ - "--show-output" -] ----- -Additional arguments to be passed through Cargo to launched tests, benchmarks, or -doc-tests. - -Unless the launched target uses a -[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), -they will end up being interpreted as options to -[`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - --- -[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`):: -+ --- -Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private -projects, or "discover" to try to automatically find it if the `rustc-dev` component -is installed. - -Any project which uses rust-analyzer with the rustcPrivate -crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - -This option does not take effect until rust-analyzer is restarted. --- -[[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: -+ --- -Additional arguments to `rustfmt`. --- -[[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: -+ --- -Advanced option, fully override the command rust-analyzer uses for -formatting. This should be the equivalent of `rustfmt` here, and -not that of `cargo fmt`. The file contents will be passed on the -standard input and the formatted result will be read from the -standard output. --- -[[rust-analyzer.rustfmt.rangeFormatting.enable]]rust-analyzer.rustfmt.rangeFormatting.enable (default: `false`):: -+ --- -Enables the use of rustfmt's unstable range formatting command for the -`textDocument/rangeFormatting` request. The rustfmt option is unstable and only -available on a nightly build. --- -[[rust-analyzer.semanticHighlighting.doc.comment.inject.enable]]rust-analyzer.semanticHighlighting.doc.comment.inject.enable (default: `true`):: -+ --- -Inject additional highlighting into doc comments. - -When enabled, rust-analyzer will highlight rust source in doc comments as well as intra -doc links. --- -[[rust-analyzer.semanticHighlighting.nonStandardTokens]]rust-analyzer.semanticHighlighting.nonStandardTokens (default: `true`):: -+ --- -Whether the server is allowed to emit non-standard tokens and modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.enable]]rust-analyzer.semanticHighlighting.operator.enable (default: `true`):: -+ --- -Use semantic tokens for operators. - -When disabled, rust-analyzer will emit semantic tokens only for operator tokens when -they are tagged with modifiers. --- -[[rust-analyzer.semanticHighlighting.operator.specialization.enable]]rust-analyzer.semanticHighlighting.operator.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for operators. - -When enabled, rust-analyzer will emit special token types for operator tokens instead -of the generic `operator` token type. --- -[[rust-analyzer.semanticHighlighting.punctuation.enable]]rust-analyzer.semanticHighlighting.punctuation.enable (default: `false`):: -+ --- -Use semantic tokens for punctuation. - -When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when -they are tagged with modifiers or have a special role. --- -[[rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang]]rust-analyzer.semanticHighlighting.punctuation.separate.macro.bang (default: `false`):: -+ --- -When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro -calls. --- -[[rust-analyzer.semanticHighlighting.punctuation.specialization.enable]]rust-analyzer.semanticHighlighting.punctuation.specialization.enable (default: `false`):: -+ --- -Use specialized semantic tokens for punctuation. - -When enabled, rust-analyzer will emit special token types for punctuation tokens instead -of the generic `punctuation` token type. --- -[[rust-analyzer.semanticHighlighting.strings.enable]]rust-analyzer.semanticHighlighting.strings.enable (default: `true`):: -+ --- -Use semantic tokens for strings. - -In some editors (e.g. vscode) semantic tokens override other highlighting grammars. -By disabling semantic tokens for strings, other grammars can be used to highlight -their contents. --- -[[rust-analyzer.signatureInfo.detail]]rust-analyzer.signatureInfo.detail (default: `"full"`):: -+ --- -Show full signature of the callable. Only shows parameters if disabled. --- -[[rust-analyzer.signatureInfo.documentation.enable]]rust-analyzer.signatureInfo.documentation.enable (default: `true`):: -+ --- -Show documentation. --- -[[rust-analyzer.typing.triggerChars]]rust-analyzer.typing.triggerChars (default: `"=."`):: -+ --- -Specify the characters allowed to invoke special on typing triggers. -- typing `=` after `let` tries to smartly add `;` if `=` is followed by an existing expression -- typing `=` between two expressions adds `;` when in statement position -- typing `=` to turn an assignment into an equality comparison removes `;` when in expression position -- typing `.` in a chain method call auto-indents -- typing `{` or `(` in front of an expression inserts a closing `}` or `)` after the expression -- typing `{` in a use item adds a closing `}` in the right place -- typing `>` to complete a return type `->` will insert a whitespace after it -- typing `<` in a path or type position inserts a closing `>` after the path or type. --- -[[rust-analyzer.vfs.extraIncludes]]rust-analyzer.vfs.extraIncludes (default: `[]`):: -+ --- -Additional paths to include in the VFS. Generally for code that is -generated or otherwise managed by a build system outside of Cargo, -though Cargo might be the eventual consumer. --- -[[rust-analyzer.workspace.discoverConfig]]rust-analyzer.workspace.discoverConfig (default: `null`):: -+ --- -Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` -is used to determine which build system-specific files should be watched in order to -reload rust-analyzer. - -Below is an example of a valid configuration: -```json -"rust-analyzer.workspace.discoverConfig": { - "command": [ - "rust-project", - "develop-json" - ], - "progressLabel": "rust-analyzer", - "filesToWatch": [ - "BUCK" - ] -} -``` - -## On `DiscoverWorkspaceConfig::command` - -**Warning**: This format is provisional and subject to change. - -[`DiscoverWorkspaceConfig::command`] *must* return a JSON object -corresponding to `DiscoverProjectData::Finished`: - -```norun -#[derive(Debug, Clone, Deserialize, Serialize)] -#[serde(tag = "kind")] -#[serde(rename_all = "snake_case")] -enum DiscoverProjectData { - Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, - Error { error: String, source: Option<String> }, - Progress { message: String }, -} -``` - -As JSON, `DiscoverProjectData::Finished` is: - -```json -{ - // the internally-tagged representation of the enum. - "kind": "finished", - // the file used by a non-Cargo build system to define - // a package or target. - "buildfile": "rust-analyzer/BUILD", - // the contents of a rust-project.json, elided for brevity - "project": { - "sysroot": "foo", - "crates": [] - } -} -``` - -It is encouraged, but not required, to use the other variants on -`DiscoverProjectData` to provide a more polished end-user experience. - -`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, -which will be substituted with the JSON-serialized form of the following -enum: - -```norun -#[derive(PartialEq, Clone, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub enum DiscoverArgument { - Path(AbsPathBuf), - Buildfile(AbsPathBuf), -} -``` - -The JSON representation of `DiscoverArgument::Path` is: - -```json -{ - "path": "src/main.rs" -} -``` - -Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - -``` -{ - "buildfile": "BUILD" -} -``` - -`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, -and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to -to update an existing workspace. As a reference for implementors, -buck2's `rust-project` will likely be useful: -https://github.com/facebook/buck2/tree/main/integrations/rust-project. --- -[[rust-analyzer.workspace.symbol.search.kind]]rust-analyzer.workspace.symbol.search.kind (default: `"only_types"`):: -+ --- -Workspace symbol search kind. --- -[[rust-analyzer.workspace.symbol.search.limit]]rust-analyzer.workspace.symbol.search.limit (default: `128`):: -+ --- -Limits the number of items returned from a workspace symbol search (Defaults to 128). -Some clients like vs-code issue new searches on result filtering and don't require all results to be returned in the initial search. -Other clients requires all results upfront and might require a higher limit. --- -[[rust-analyzer.workspace.symbol.search.scope]]rust-analyzer.workspace.symbol.search.scope (default: `"workspace"`):: -+ --- -Workspace symbol search scope. --- diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc deleted file mode 100644 index 4a2a6f2e36864..0000000000000 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ /dev/null @@ -1,1121 +0,0 @@ -= User Manual -:toc: preamble -:sectanchors: -:page-layout: post -:icons: font -:source-highlighter: rouge -:experimental: - -//// -IMPORTANT: the master copy of this document lives in the https://github.com/rust-lang/rust-analyzer repository -//// - -At its core, rust-analyzer is a *library* for semantic analysis of Rust code as it changes over time. -This manual focuses on a specific usage of the library -- running it as part of a server that implements the -https://microsoft.github.io/language-server-protocol/[Language Server Protocol] (LSP). -The LSP allows various code editors, like VS Code, Emacs or Vim, to implement semantic features like completion or goto definition by talking to an external language server process. - -[TIP] -==== -[.lead] -To improve this document, send a pull request: + -https://github.com/rust-lang/rust-analyzer/blob/master/docs/user/manual.adoc[https://github.com/rust-analyzer/.../manual.adoc] - -The manual is written in https://asciidoc.org[AsciiDoc] and includes some extra files which are generated from the source code. Run `cargo test` and `cargo xtask codegen` to create these and then `asciidoctor manual.adoc` to create an HTML copy. -==== - -If you have questions about using rust-analyzer, please ask them in the https://users.rust-lang.org/c/ide/14["`IDEs and Editors`"] topic of Rust users forum. - -== Installation - -In theory, one should be able to just install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> and have it automatically work with any editor. -We are not there yet, so some editor specific setup is required. - -Additionally, rust-analyzer needs the sources of the standard library. -If the source code is not present, rust-analyzer will attempt to install it automatically. - -To add the sources manually, run the following command: - -```bash -$ rustup component add rust-src -``` - -=== Toolchain - -Only the latest stable standard library source is officially supported for use with rust-analyzer. -If you are using an older toolchain or have an override set, rust-analyzer may fail to understand the Rust source. -You will either need to update your toolchain or use an older version of rust-analyzer that is compatible with your toolchain. - -If you are using an override in your project, you can still force rust-analyzer to use the stable toolchain via the environment variable `RUSTUP_TOOLCHAIN`. -For example, with VS Code or coc-rust-analyzer: - -[source,json] ----- -{ "rust-analyzer.server.extraEnv": { "RUSTUP_TOOLCHAIN": "stable" } } ----- - -=== VS Code - -This is the best supported editor at the moment. -The rust-analyzer plugin for VS Code is maintained -https://github.com/rust-lang/rust-analyzer/tree/master/editors/code[in tree]. - -You can install the latest release of the plugin from -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer[the marketplace]. - -Note that the plugin may cause conflicts with the -https://marketplace.visualstudio.com/items?itemName=rust-lang.rust[previous official Rust plugin]. -The latter is no longer maintained and should be uninstalled. - -The server binary is stored in the extension install directory, which starts with `rust-lang.rust-analyzer-` and is located under: - -* Linux: `~/.vscode/extensions` -* Linux (Remote, such as WSL): `~/.vscode-server/extensions` -* macOS: `~/.vscode/extensions` -* Windows: `%USERPROFILE%\.vscode\extensions` - -As an exception, on NixOS, the extension makes a copy of the server and stores it under `~/.config/Code/User/globalStorage/rust-lang.rust-analyzer`. - -Note that we only support the two most recent versions of VS Code. - -==== Updates - -The extension will be updated automatically as new versions become available. -It will ask your permission to download the matching language server version binary if needed. - -===== Nightly - -We ship nightly releases for VS Code. -To help us out by testing the newest code, you can enable pre-release versions in the Code extension page. - -==== Manual installation - -Alternatively, download a VSIX corresponding to your platform from the -https://github.com/rust-lang/rust-analyzer/releases[releases] page. - -Install the extension with the `Extensions: Install from VSIX` command within VS Code, or from the command line via: -[source] ----- -$ code --install-extension /path/to/rust-analyzer.vsix ----- - -If you are running an unsupported platform, you can install `rust-analyzer-no-server.vsix` and compile or obtain a server binary. -Copy the server anywhere, then add the path to your settings.json, for example: -[source,json] ----- -{ "rust-analyzer.server.path": "~/.local/bin/rust-analyzer-linux" } ----- - -==== Building From Source - -Both the server and the Code plugin can be installed from source: - -[source] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install ----- - -You'll need Cargo, nodejs (matching a supported version of VS Code) and npm for this. - -Note that installing via `xtask install` does not work for VS Code Remote, instead you'll need to install the `.vsix` manually. - -If you're not using Code, you can compile and install only the LSP server: - -[source] ----- -$ cargo xtask install --server ----- - -Make sure that `.cargo/bin` is in `$PATH` and precedes paths where `rust-analyzer` may also be installed. -Specifically, `rustup` includes a proxy called `rust-analyzer`, which can cause problems if you're planning to use a source build or even a downloaded binary. - -=== rust-analyzer Language Server Binary - -Other editors generally require the `rust-analyzer` binary to be in `$PATH`. -You can download pre-built binaries from the https://github.com/rust-lang/rust-analyzer/releases[releases] page. -You will need to uncompress and rename the binary for your platform, e.g. from `rust-analyzer-aarch64-apple-darwin.gz` on Mac OS to `rust-analyzer`, make it executable, then move it into a directory in your `$PATH`. - -On Linux to install the `rust-analyzer` binary into `~/.local/bin`, these commands should work: - -[source,bash] ----- -$ mkdir -p ~/.local/bin -$ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer -$ chmod +x ~/.local/bin/rust-analyzer ----- - -Make sure that `~/.local/bin` is listed in the `$PATH` variable and use the appropriate URL if you're not on a `x86-64` system. - -You don't have to use `~/.local/bin`, any other path like `~/.cargo/bin` or `/usr/local/bin` will work just as well. - -Alternatively, you can install it from source using the command below. -You'll need the latest stable version of the Rust toolchain. - -[source,bash] ----- -$ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer -$ cargo xtask install --server ----- - -If your editor can't find the binary even though the binary is on your `$PATH`, the likely explanation is that it doesn't see the same `$PATH` as the shell, see https://github.com/rust-lang/rust-analyzer/issues/1811[this issue]. -On Unix, running the editor from a shell or changing the `.desktop` file to set the environment should help. - -==== rustup - -`rust-analyzer` is available in `rustup`: - -[source,bash] ----- -$ rustup component add rust-analyzer ----- - -==== Arch Linux - -The `rust-analyzer` binary can be installed from the repos or AUR (Arch User Repository): - -- https://www.archlinux.org/packages/extra/x86_64/rust-analyzer/[`rust-analyzer`] (built from latest tagged source) -- https://aur.archlinux.org/packages/rust-analyzer-git[`rust-analyzer-git`] (latest Git version) - -Install it with pacman, for example: - -[source,bash] ----- -$ pacman -S rust-analyzer ----- - -==== Gentoo Linux - -There are two ways to install `rust-analyzer` under Gentoo: - -- when installing `dev-lang/rust` or `dev-lang/rust-bin`, enable the `rust-analyzer` and `rust-src` USE flags -- use the `rust-analyzer` component in `rustup` (see instructions above) - -Note that in both cases, the version installed lags for a couple of months behind the official releases on GitHub. -To obtain a newer one, you can download a binary from GitHub Releases or building from source. - -==== macOS - -The `rust-analyzer` binary can be installed via https://brew.sh/[Homebrew]. - -[source,bash] ----- -$ brew install rust-analyzer ----- - -==== Windows - -It is recommended to install the latest Microsoft Visual C++ Redistributable prior to installation. -Download links can be found -https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist[here]. - -=== VS Code or VSCodium in Flatpak - -Setting up `rust-analyzer` with a Flatpak version of Code is not trivial because of the Flatpak sandbox. -While the sandbox can be disabled for some directories, `/usr/bin` will always be mounted under `/run/host/usr/bin`. -This prevents access to the system's C compiler, a system-wide installation of Rust, or any other libraries you might want to link to. -Some compilers and libraries can be acquired as Flatpak SDKs, such as `org.freedesktop.Sdk.Extension.rust-stable` or `org.freedesktop.Sdk.Extension.llvm15`. - -If you use a Flatpak SDK for Rust, it must be in your `PATH`: - - * install the SDK extensions with `flatpak install org.freedesktop.Sdk.Extension.{llvm15,rust-stable}//23.08` - * enable SDK extensions in the editor with the environment variable `FLATPAK_ENABLE_SDK_EXT=llvm15,rust-stable` (this can be done using flatseal or `flatpak override`) - -If you want to use Flatpak in combination with `rustup`, the following steps might help: - - - both Rust and `rustup` have to be installed using https://rustup.rs. Distro packages _will not_ work. - - you need to launch Code, open a terminal and run `echo $PATH` - - using https://flathub.org/apps/details/com.github.tchx84.Flatseal[Flatseal], you must add an environment variable called `PATH`. - Set its value to the output from above, appending `:~/.cargo/bin`, where `~` is the path to your home directory. - You must replace `~`, as it won't be expanded otherwise. - - while Flatseal is open, you must enable access to "All user files" - -A C compiler should already be available via `org.freedesktop.Sdk`. -Any other tools or libraries you will need to acquire from Flatpak. - -=== Emacs - -Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. - -To use `rust-analyzer`, you need to install and enable one of the two popular LSP client implementations for Emacs, https://github.com/joaotavora/eglot[Eglot] or https://github.com/emacs-lsp/lsp-mode[LSP Mode]. Both enable `rust-analyzer` by default in rust buffers if it is available. - -==== Eglot - -Eglot is the more minimalistic and lightweight LSP client for Emacs, integrates well with existing Emacs functionality and is built into Emacs starting from release 29. - -After installing Eglot, e.g. via `M-x package-install` (not needed from Emacs 29), you can enable it via the `M-x eglot` command or load it automatically in `rust-mode` via - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'eglot-ensure) ----- - -To enable clippy, you will need to configure the initialization options to pass the `check.command` setting. - -[source,emacs-lisp] ----- -(add-to-list 'eglot-server-programs - '((rust-ts-mode rust-mode) . - ("rust-analyzer" :initializationOptions (:check (:command "clippy"))))) ----- - -For more detailed instructions and options see the https://joaotavora.github.io/eglot[Eglot manual] (also available from Emacs via `M-x info`) and the -https://github.com/joaotavora/eglot/blob/master/README.md[Eglot readme]. - -Eglot does not support the rust-analyzer extensions to the language-server protocol and does not aim to do so in the future. The https://github.com/nemethf/eglot-x#rust-analyzer-extensions[eglot-x] package adds experimental support for those LSP extensions. - -==== LSP Mode - -LSP-mode is the original LSP-client for emacs. Compared to Eglot it has a larger codebase and supports more features, like LSP protocol extensions. -With extension packages like https://github.com/emacs-lsp/lsp-mode[LSP UI] it offers a lot of visual eyecandy. -Further it integrates well with https://github.com/emacs-lsp/dap-mode[DAP mode] for support of the Debug Adapter Protocol. - -You can install LSP-mode via `M-x package-install` and then run it via the `M-x lsp` command or load it automatically in rust buffers with - -[source,emacs-lisp] ----- -(add-hook 'rust-mode-hook 'lsp-deferred) ----- - -For more information on how to set up LSP mode and its extension package see the instructions in the https://emacs-lsp.github.io/lsp-mode/page/installation[LSP mode manual]. -Also see the https://emacs-lsp.github.io/lsp-mode/page/lsp-rust-analyzer/[rust-analyzer section] for `rust-analyzer` specific options and commands, which you can optionally bind to keys. - -Note the excellent https://robert.kra.hn/posts/2021-02-07_rust-with-emacs/[guide] from https://github.com/rksm[@rksm] on how to set-up Emacs for Rust development with LSP mode and several other packages. - -=== Vim/Neovim - -Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. -Not needed if the extension can install/update it on its own, coc-rust-analyzer is one example. - -There are several LSP client implementations for Vim or Neovim: - -==== coc-rust-analyzer - -1. Install coc.nvim by following the instructions at - https://github.com/neoclide/coc.nvim[coc.nvim] - (Node.js required) -2. Run `:CocInstall coc-rust-analyzer` to install - https://github.com/fannheyward/coc-rust-analyzer[coc-rust-analyzer], - this extension implements _most_ of the features supported in the VSCode extension: - * automatically install and upgrade stable/nightly releases - * same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc. - * same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc. - * inlay hints for variables and method chaining, _Neovim Only_ - -Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful. - -==== LanguageClient-neovim - -1. Install LanguageClient-neovim by following the instructions - https://github.com/autozimu/LanguageClient-neovim[here] - * The GitHub project wiki has extra tips on configuration - -2. Configure by adding this to your Vim/Neovim config file (replacing the existing Rust-specific line if it exists): -+ -[source,vim] ----- -let g:LanguageClient_serverCommands = { -\ 'rust': ['rust-analyzer'], -\ } ----- - -==== YouCompleteMe - -Install YouCompleteMe by following the instructions - https://github.com/ycm-core/YouCompleteMe#installation[here]. - -rust-analyzer is the default in ycm, it should work out of the box. - -==== ALE - -To use the LSP server in https://github.com/dense-analysis/ale[ale]: - -[source,vim] ----- -let g:ale_linters = {'rust': ['analyzer']} ----- - -==== nvim-lsp - -Neovim 0.5 has built-in language server support. -For a quick start configuration of rust-analyzer, use https://github.com/neovim/nvim-lspconfig#rust_analyzer[neovim/nvim-lspconfig]. -Once `neovim/nvim-lspconfig` is installed, use `+lua require'lspconfig'.rust_analyzer.setup({})+` in your `init.vim`. - -You can also pass LSP settings to the server: - -[source,vim] ----- -lua << EOF -local lspconfig = require'lspconfig' - -local on_attach = function(client) - require'completion'.on_attach(client) -end - -lspconfig.rust_analyzer.setup({ - on_attach = on_attach, - settings = { - ["rust-analyzer"] = { - imports = { - granularity = { - group = "module", - }, - prefix = "self", - }, - cargo = { - buildScripts = { - enable = true, - }, - }, - procMacro = { - enable = true - }, - } - } -}) -EOF ----- - -If you're running Neovim 0.10 or later, you can enable inlay hints via `on_attach`: - -[source,vim] ----- -lspconfig.rust_analyzer.setup({ - on_attach = function(client, bufnr) - vim.lsp.inlay_hint.enable(true, { bufnr = bufnr }) - end -}) ----- - -Note that the hints are only visible after `rust-analyzer` has finished loading **and** you have to edit the file to trigger a re-render. - -See https://sharksforarms.dev/posts/neovim-rust/ for more tips on getting started. - -Check out https://github.com/mrcjkb/rustaceanvim for a batteries included rust-analyzer setup for Neovim. - -==== vim-lsp - -vim-lsp is installed by following https://github.com/prabirshrestha/vim-lsp[the plugin instructions]. -It can be as simple as adding this line to your `.vimrc`: - -[source,vim] ----- -Plug 'prabirshrestha/vim-lsp' ----- - -Next you need to register the `rust-analyzer` binary. -If it is available in `$PATH`, you may want to add this to your `.vimrc`: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ }) -endif ----- - -There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<configuration,Configuration>> section. -Here is an example of how to enable the proc-macro support: - -[source,vim] ----- -if executable('rust-analyzer') - au User lsp_setup call lsp#register_server({ - \ 'name': 'Rust Language Server', - \ 'cmd': {server_info->['rust-analyzer']}, - \ 'whitelist': ['rust'], - \ 'initialization_options': { - \ 'cargo': { - \ 'buildScripts': { - \ 'enable': v:true, - \ }, - \ }, - \ 'procMacro': { - \ 'enable': v:true, - \ }, - \ }, - \ }) -endif ----- - -=== Sublime Text - -==== Sublime Text 4: -* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer]. - -NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`). - -==== Sublime Text 3: -* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. -* Install the link:https://packagecontrol.io/packages/LSP[LSP package]. -* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`. - -If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available. - -If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary. - -=== GNOME Builder - -GNOME Builder 3.37.1 and newer has native `rust-analyzer` support. -If the LSP binary is not available, GNOME Builder can install it when opening a Rust file. - - -=== Eclipse IDE - -Support for Rust development in the Eclipse IDE is provided by link:https://github.com/eclipse/corrosion[Eclipse Corrosion]. -If available in PATH or in some standard location, `rust-analyzer` is detected and powers editing of Rust files without further configuration. -If `rust-analyzer` is not detected, Corrosion will prompt you for configuration of your Rust toolchain and language server with a link to the __Window > Preferences > Rust__ preference page; from here a button allows to download and configure `rust-analyzer`, but you can also reference another installation. -You'll need to close and reopen all .rs and Cargo files, or to restart the IDE, for this change to take effect. - -=== Kate Text Editor - -Support for the language server protocol is built into Kate through the LSP plugin, which is included by default. -It is preconfigured to use rust-analyzer for Rust sources since Kate 21.12. - -To change rust-analyzer config options, start from the following example and put it into Kate's "User Server Settings" tab (located under the LSP Client settings): -[source,json] ----- -{ - "servers": { - "rust": { - "initializationOptions": { - "cachePriming": { - "enable": false - }, - "check": { - "allTargets": false - }, - "checkOnSave": false - } - } - } -} ----- -Then click on apply, and restart the LSP server for your rust project. - -=== juCi++ - -https://gitlab.com/cppit/jucipp[juCi++] has built-in support for the language server protocol, and since version 1.7.0 offers installation of both Rust and rust-analyzer when opening a Rust file. - -=== Kakoune - -https://kakoune.org/[Kakoune] supports LSP with the help of https://github.com/kak-lsp/kak-lsp[`kak-lsp`]. -Follow the https://github.com/kak-lsp/kak-lsp#installation[instructions] to install `kak-lsp`. -To configure `kak-lsp`, refer to the https://github.com/kak-lsp/kak-lsp#configuring-kak-lsp[configuration section] which is basically about copying the https://github.com/kak-lsp/kak-lsp/blob/master/kak-lsp.toml[configuration file] in the right place (latest versions should use `rust-analyzer` by default). - -Finally, you need to configure Kakoune to talk to `kak-lsp` (see https://github.com/kak-lsp/kak-lsp#usage[Usage section]). -A basic configuration will only get you LSP but you can also activate inlay diagnostics and auto-formatting on save. -The following might help you get all of this. - -[source,txt] ----- -eval %sh{kak-lsp --kakoune -s $kak_session} # Not needed if you load it with plug.kak. -hook global WinSetOption filetype=rust %{ - # Enable LSP - lsp-enable-window - - # Auto-formatting on save - hook window BufWritePre .* lsp-formatting-sync - - # Configure inlay hints (only on save) - hook window -group rust-inlay-hints BufWritePost .* rust-analyzer-inlay-hints - hook -once -always window WinSetOption filetype=.* %{ - remove-hooks window rust-inlay-hints - } -} ----- - -=== Helix - -https://docs.helix-editor.com/[Helix] supports LSP by default. -However, it won't install `rust-analyzer` automatically. -You can follow instructions for installing <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>. - -[#visual-studio] -=== [[visual-studio-2022]]Visual Studio 2022 - -There are multiple rust-analyzer extensions for Visual Studio 2022 on Windows: - -==== rust-analyzer.vs - -(License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International) - -https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[Visual Studio Marketplace] - -https://github.com/kitamstudios/rust-analyzer/[GitHub] - -Support for Rust development in the Visual Studio IDE is enabled by the link:https://marketplace.visualstudio.com/items?itemName=kitamstudios.RustAnalyzer[rust-analyzer] package. Either click on the download link or install from IDE's extension manager. -For now link:https://visualstudio.microsoft.com/downloads/[Visual Studio 2022] is required. All editions are supported viz. Community, Professional & Enterprise. -The package aims to provide 0-friction installation and therefore comes loaded with most things required including rust-analyzer binary. If anything it needs is missing, appropriate errors / warnings will guide the user. E.g. cargo.exe needs to be in path and the package will tell you as much. -This package is under rapid active development. So if you encounter any issues please file it at link:https://github.com/kitamstudios/rust-analyzer/[rust-analyzer.vs]. - -==== VS_RustAnalyzer - -(License: GPL) - -https://marketplace.visualstudio.com/items?itemName=cchharris.vsrustanalyzer[Visual Studio Marketplace] - -https://github.com/cchharris/VS-RustAnalyzer[GitHub] - -==== SourceGear Rust - -(License: closed source) - -https://marketplace.visualstudio.com/items?itemName=SourceGear.SourceGearRust[Visual Studio Marketplace] - -https://github.com/sourcegear/rust-vs-extension[GitHub (docs, issues, discussions)] - -* Free (no-cost) -* Supports all editions of Visual Studio 2022 on Windows: Community, Professional, or Enterprise - -=== Lapce - -https://lapce.dev/[Lapce] has a Rust plugin which you can install directly. -Unfortunately, it downloads an old version of `rust-analyzer`, but you can set the server path under Settings. - -=== Crates - -There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically. - -For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/autopublish.yaml[the publish workflow]. - -=== Zed - -https://zed.dev[Zed] has native `rust-analyzer` support. -If the LSP binary is not available, Zed can install it when opening a Rust file. - -== Troubleshooting - -Start with looking at the rust-analyzer version. -Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line. -If the date is more than a week ago, it's better to update rust-analyzer version. - -The next thing to check would be panic messages in rust-analyzer's log. -Log messages are printed to stderr, in VS Code you can see them in the `Output > Rust Analyzer Language Server` tab of the panel. -To see more logs, set the `RA_LOG=info` environment variable, this can be done either by setting the environment variable manually or by using `rust-analyzer.server.extraEnv`, note that both of these approaches require the server to be restarted. - -To fully capture LSP messages between the editor and the server, run the `rust-analyzer: Toggle LSP Logs` command and check -`Output > Rust Analyzer Language Server Trace`. - -The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure. -To debug that, first note the `rust-analyzer` section in the status bar. -If it has an error icon and red, that's the problem (hover will have somewhat helpful error message). -**rust-analyzer: Status** prints dependency information for the current file. -Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading. - -If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line. -This command type checks the whole project in batch mode bypassing LSP machinery. - -When filing issues, it is useful (but not necessary) to try to minimize examples. -An ideal bug reproduction looks like this: - -```bash -$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash -$ rust-analyzer --version -rust-analyzer dd12184e4 2021-05-08 dev -$ rust-analyzer analysis-stats . -💀 💀 💀 -``` - -It is especially useful when the `repo` doesn't use external crates or the standard library. - -If you want to go as far as to modify the source code to debug the problem, be sure to take a look at the -https://github.com/rust-lang/rust-analyzer/tree/master/docs/dev[dev docs]! - -== Configuration - -**Source:** https://github.com/rust-lang/rust-analyzer/blob/master/crates/rust-analyzer/src/config.rs[config.rs] - -The <<installation,Installation>> section contains details on configuration for some of the editors. -In general `rust-analyzer` is configured via LSP messages, which means that it's up to the editor to decide on the exact format and location of configuration files. - -Some clients, such as <<vs-code,VS Code>> or <<coc-rust-analyzer,COC plugin in Vim>> provide `rust-analyzer` specific configuration UIs. Others may require you to know a bit more about the interaction with `rust-analyzer`. - -For the later category, it might help to know that the initial configuration is specified as a value of the `initializationOptions` field of the https://microsoft.github.io/language-server-protocol/specifications/specification-current/#initialize[`InitializeParams` message, in the LSP protocol]. -The spec says that the field type is `any?`, but `rust-analyzer` is looking for a JSON object that is constructed using settings from the list below. -Name of the setting, ignoring the `rust-analyzer.` prefix, is used as a path, and value of the setting becomes the JSON property value. - -For example, a very common configuration is to enable proc-macro support, can be achieved by sending this JSON: - -[source,json] ----- -{ - "cargo": { - "buildScripts": { - "enable": true, - }, - }, - "procMacro": { - "enable": true, - } -} ----- - -Please consult your editor's documentation to learn more about how to configure https://microsoft.github.io/language-server-protocol/[LSP servers]. - -To verify which configuration is actually used by `rust-analyzer`, set `RA_LOG` environment variable to `rust_analyzer=info` and look for config-related messages. -Logs should show both the JSON that `rust-analyzer` sees as well as the updated config. - -This is the list of config options `rust-analyzer` supports: - -include::./generated_config.adoc[] - -== Non-Cargo Based Projects - -rust-analyzer does not require Cargo. -However, if you use some other build system, you'll have to describe the structure of your project for rust-analyzer in the `rust-project.json` format: - -[source,TypeScript] ----- -interface JsonProject { - /// Path to the sysroot directory. - /// - /// The sysroot is where rustc looks for the - /// crates that are built-in to rust, such as - /// std. - /// - /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root - /// - /// To see the current value of sysroot, you - /// can query rustc: - /// - /// ``` - /// $ rustc --print sysroot - /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin - /// ``` - sysroot?: string; - /// Path to the directory with *source code* of - /// sysroot crates. - /// - /// By default, this is `lib/rustlib/src/rust/library` - /// relative to the sysroot. - /// - /// It should point to the directory where std, - /// core, and friends can be found: - /// - /// https://github.com/rust-lang/rust/tree/master/library. - /// - /// If provided, rust-analyzer automatically adds - /// dependencies on sysroot crates. Conversely, - /// if you omit this path, you can specify sysroot - /// dependencies yourself and, for example, have - /// several different "sysroots" in one graph of - /// crates. - sysroot_src?: string; - /// List of groups of common cfg values, to allow - /// sharing them between crates. - /// - /// Maps from group name to its cfgs. Cfg follow - /// the same format as `Crate.cfg`. - cfg_groups?: { [key: string]: string[]; }; - /// The set of crates comprising the current - /// project. Must include all transitive - /// dependencies as well as sysroot crate (libstd, - /// libcore and such). - crates: Crate[]; - /// Configuration for CLI commands. - /// - /// These are used for running and debugging binaries - /// and tests without encoding build system-specific - /// knowledge into rust-analyzer. - /// - /// # Example - /// - /// Below is an example of a test runnable. `{label}` and `{test_id}` - /// are explained in `Runnable::args`'s documentation below. - /// - /// ```json - /// { - /// "program": "buck", - /// "args": [ - /// "test", - /// "{label}", - /// "--", - /// "{test_id}", - /// "--print-passing-details" - /// ], - /// "cwd": "/home/user/repo-root/", - /// "kind": "testOne" - /// } - /// ``` - runnables?: Runnable[]; -} - -interface Crate { - /// Optional crate name used for display purposes, - /// without affecting semantics. See the `deps` - /// key for semantically-significant crate names. - display_name?: string; - /// Path to the root module of the crate. - root_module: string; - /// Edition of the crate. - edition: '2015' | '2018' | '2021' | '2024'; - /// The version of the crate. Used for calculating - /// the correct docs.rs URL. - version?: string; - /// Dependencies - deps: Dep[]; - /// Should this crate be treated as a member of - /// current "workspace". - /// - /// By default, inferred from the `root_module` - /// (members are the crates which reside inside - /// the directory opened in the editor). - /// - /// Set this to `false` for things like standard - /// library and 3rd party crates to enable - /// performance optimizations (rust-analyzer - /// assumes that non-member crates don't change). - is_workspace_member?: boolean; - /// Optionally specify the (super)set of `.rs` - /// files comprising this crate. - /// - /// By default, rust-analyzer assumes that only - /// files under `root_module.parent` can belong - /// to a crate. `include_dirs` are included - /// recursively, unless a subdirectory is in - /// `exclude_dirs`. - /// - /// Different crates can share the same `source`. - /// - /// If two crates share an `.rs` file in common, - /// they *must* have the same `source`. - /// rust-analyzer assumes that files from one - /// source can't refer to files in another source. - source?: { - include_dirs: string[]; - exclude_dirs: string[]; - }; - /// List of cfg groups this crate inherits. - /// - /// All cfg in these groups will be concatenated to - /// `cfg`. It is impossible to replace a value from - /// the groups. - cfg_groups?: string[]; - /// The set of cfgs activated for a given crate, like - /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. - cfg: string[]; - /// Target tuple for this Crate. - /// - /// Used when running `rustc --print cfg` - /// to get target-specific cfgs. - target?: string; - /// Environment variables, used for - /// the `env!` macro - env: { [key: string]: string; }; - - /// Whether the crate is a proc-macro crate. - is_proc_macro: boolean; - /// For proc-macro crates, path to compiled - /// proc-macro (.so file). - proc_macro_dylib_path?: string; - - /// Repository, matching the URL that would be used - /// in Cargo.toml. - repository?: string; - - /// Build-specific data about this crate. - build?: BuildInfo; -} - -interface Dep { - /// Index of a crate in the `crates` array. - crate: number; - /// Name as should appear in the (implicit) - /// `extern crate name` declaration. - name: string; -} - -interface BuildInfo { - /// The name associated with this crate. - /// - /// This is determined by the build system that produced - /// the `rust-project.json` in question. For instance, if buck were used, - /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. - /// - /// Do not attempt to parse the contents of this string; it is a build system-specific - /// identifier similar to `Crate::display_name`. - label: string; - /// Path corresponding to the build system-specific file defining the crate. - build_file: string; - /// The kind of target. - /// - /// This information is used to determine what sort - /// of runnable codelens to provide, if any. - target_kind: 'bin' | 'lib' | 'test'; -} - -interface Runnable { - /// The program invoked by the runnable. - /// - /// For example, this might be `cargo`, `buck`, or `bazel`. - program: string; - /// The arguments passed to `program`. - args: string[]; - /// The current working directory of the runnable. - cwd: string; - /// Used to decide what code lens to offer. - /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. - /// - /// The args for testOne can contain two template strings: - /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced - /// with the test name. - kind: 'testOne' | string; -} ----- - -This format is provisional and subject to change. -Specifically, the `roots` setup will be different eventually. - -There are three ways to feed `rust-project.json` to rust-analyzer: - -* Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. -* Specify `"rust-analyzer.linkedProjects": [ "path/to/rust-project.json" ]` in the settings (and make sure that your LSP client sends settings as a part of initialize request). -* Specify `"rust-analyzer.linkedProjects": [ { "roots": [...], "crates": [...] }]` inline. - -Relative paths are interpreted relative to `rust-project.json` file location or (for inline JSON) relative to `rootUri`. - -You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. - -Note that calls to `cargo check` are disabled when using `rust-project.json` by default, so compilation errors and warnings will no longer be sent to your LSP client. -To enable these compilation errors you will need to specify explicitly what command rust-analyzer should run to perform the checks using the `rust-analyzer.check.overrideCommand` configuration. -As an example, the following configuration explicitly sets `cargo check` as the `check` command. - -[source,json] ----- -{ "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } ----- - -`check.overrideCommand` requires the command specified to output json error messages for rust-analyzer to consume. -The `--message-format=json` flag does this for `cargo check` so whichever command you use must also output errors in this format. -See the <<Configuration>> section for more information. - -== Security - -At the moment, rust-analyzer assumes that all code is trusted. -Here is a **non-exhaustive** list of ways to make rust-analyzer execute arbitrary code: - -* proc macros and build scripts are executed by default -* `.cargo/config` can override `rustc` with an arbitrary executable -* `rust-toolchain.toml` can override `rustc` with an arbitrary executable -* VS Code plugin reads configuration from project directory, and that can be used to override paths to various executables, like `rustfmt` or `rust-analyzer` itself. -* rust-analyzer's syntax trees library uses a lot of `unsafe` and hasn't been properly audited for memory safety. - -== Privacy - -The LSP server performs no network access in itself, but runs `cargo metadata` which will update or download the crate registry and the source code of the project dependencies. -If enabled (the default), build scripts and procedural macros can do anything. - -The Code extension does not access the network. - -Any other editor plugins are not under the control of the `rust-analyzer` developers. For any privacy concerns, you should check with their respective developers. - -For `rust-analyzer` developers, `cargo xtask release` uses the GitHub API to put together the release notes. - -== Features - -include::./generated_features.adoc[] - -== Assists (Code Actions) - -Assists, or code actions, are small local refactorings, available in a particular context. -They are usually triggered by a shortcut or by clicking a light bulb icon in the editor. -Cursor position or selection is signified by `┃` character. - -include::./generated_assists.adoc[] - -== Diagnostics - -While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis. -Some of these diagnostics don't respect `\#[allow]` or `\#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.experimental.enable` or `rust-analyzer.diagnostics.disabled` settings. - -=== Clippy - -To run `cargo clippy` instead of `cargo check`, you can set `"rust-analyzer.check.command": "clippy"`. - -include::./generated_diagnostic.adoc[] - -== Editor Features -=== VS Code - -==== Color configurations - -It is possible to change the foreground/background color and font family/size of inlay hints. -Just add this to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.inlayHints.fontFamily": "Courier New", - "editor.inlayHints.fontSize": 11, - - "workbench.colorCustomizations": { - // Name of the theme you are currently using - "[Default Dark+]": { - "editorInlayHint.foreground": "#868686f0", - "editorInlayHint.background": "#3d3d3d48", - - // Overrides for specific kinds of inlay hints - "editorInlayHint.typeForeground": "#fdb6fdf0", - "editorInlayHint.parameterForeground": "#fdb6fdf0", - } - } -} ----- - -==== Semantic style customizations - -You can customize the look of different semantic elements in the source code. -For example, mutable bindings are underlined by default and you can override this behavior by adding the following section to your `settings.json`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "*.mutable": { - "fontStyle": "", // underline is the default - }, - } - }, -} ----- - -Most themes doesn't support styling unsafe operations differently yet. You can fix this by adding overrides for the rules `operator.unsafe`, `function.unsafe`, and `method.unsafe`: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600", - "function.unsafe": "#ff6600", - "method.unsafe": "#ff6600" - } - }, -} ----- - -In addition to the top-level rules you can specify overrides for specific themes. For example, if you wanted to use a darker text color on a specific light theme, you might write: - -[source,jsonc] ----- -{ - "editor.semanticTokenColorCustomizations": { - "rules": { - "operator.unsafe": "#ff6600" - }, - "[Ayu Light]": { - "rules": { - "operator.unsafe": "#572300" - } - } - }, -} ----- - -Make sure you include the brackets around the theme name. For example, use `"[Ayu Light]"` to customize the theme Ayu Light. - -==== Special `when` clause context for keybindings. -You may use `inRustProject` context to configure keybindings for rust projects only. -For example: - -[source,json] ----- -{ - "key": "ctrl+alt+d", - "command": "rust-analyzer.openDocs", - "when": "inRustProject" -} ----- -More about `when` clause contexts https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts[here]. - -==== Setting runnable environment variables -You can use "rust-analyzer.runnables.extraEnv" setting to define runnable environment-specific substitution variables. -The simplest way for all runnables in a bunch: -```jsonc -"rust-analyzer.runnables.extraEnv": { - "RUN_SLOW_TESTS": "1" -} -``` - -Or it is possible to specify vars more granularly: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - // "mask": null, // null mask means that this rule will be applied for all runnables - "env": { - "APP_ID": "1", - "APP_DATA": "asdf" - } - }, - { - "mask": "test_name", - "env": { - "APP_ID": "2", // overwrites only APP_ID - } - } -] -``` - -You can use any valid regular expression as a mask. -Also note that a full runnable name is something like *run bin_or_example_name*, *test some::mod::test_name* or *test-mod some::mod*, so it is possible to distinguish binaries, single tests, and test modules with this masks: `"^run"`, `"^test "` (the trailing space matters!), and `"^test-mod"` respectively. - -If needed, you can set different values for different platforms: -```jsonc -"rust-analyzer.runnables.extraEnv": [ - { - "platform": "win32", // windows only - "env": { - "APP_DATA": "windows specific data" - } - }, - { - "platform": ["linux"], - "env": { - "APP_DATA": "linux data", - } - }, - { // for all platforms - "env": { - "APP_COMMON_DATA": "xxx", - } - } -] -``` - -==== Compiler feedback from external commands - -Instead of relying on the built-in `cargo check`, you can configure Code to run a command in the background and use the `$rustc-watch` problem matcher to generate inline error markers from its output. - -To do this you need to create a new https://code.visualstudio.com/docs/editor/tasks[VS Code Task] and set `"rust-analyzer.checkOnSave": false` in preferences. - -For example, if you want to run https://crates.io/crates/cargo-watch[`cargo watch`] instead, you might add the following to `.vscode/tasks.json`: - -```json -{ - "label": "Watch", - "group": "build", - "type": "shell", - "command": "cargo watch", - "problemMatcher": "$rustc-watch", - "isBackground": true -} -``` - -==== Live Share - -VS Code Live Share has partial support for rust-analyzer. - -Live Share _requires_ the official Microsoft build of VS Code, OSS builds will not work correctly. - -The host's rust-analyzer instance will be shared with all guests joining the session. -The guests do not have to have the rust-analyzer extension installed for this to work. - -If you are joining a Live Share session and _do_ have rust-analyzer installed locally, commands from the command palette will not work correctly since they will attempt to communicate with the local server. diff --git a/src/tools/rust-analyzer/xtask/src/codegen.rs b/src/tools/rust-analyzer/xtask/src/codegen.rs index bc04b9474f269..18f49643dc715 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen.rs @@ -24,6 +24,7 @@ impl flags::Codegen { diagnostics_docs::generate(self.check); assists_doc_tests::generate(self.check); parser_inline_tests::generate(self.check); + feature_docs::generate(self.check) // diagnostics_docs::generate(self.check) doesn't generate any tests // lints::generate(self.check) Updating clones the rust repo, so don't run it unless // explicitly asked for @@ -116,13 +117,7 @@ impl fmt::Display for Location { let path = self.file.strip_prefix(project_root()).unwrap().display().to_string(); let path = path.replace('\\', "/"); let name = self.file.file_name().unwrap(); - write!( - f, - "https://github.com/rust-lang/rust-analyzer/blob/master/{}#L{}[{}]", - path, - self.line, - name.to_str().unwrap() - ) + write!(f, " [{}]({}#{}) ", name.to_str().unwrap(), path, self.line) } } @@ -162,7 +157,7 @@ fn reformat(text: String) -> String { } fn add_preamble(cg: CodegenType, mut text: String) -> String { - let preamble = format!("//! Generated by `cargo codegen {cg}`, do not edit by hand.\n\n"); + let preamble = format!("//! Generated by `cargo xtask codegen {cg}`, do not edit by hand.\n\n"); text.insert_str(0, &preamble); text } @@ -186,7 +181,7 @@ fn ensure_file_contents(cg: CodegenType, file: &Path, contents: &str, check: boo file.display(), if std::env::var("CI").is_ok() { format!( - "\n NOTE: run `cargo codegen {cg}` locally and commit the updated files\n" + "\n NOTE: run `cargo xtask codegen {cg}` locally and commit the updated files\n" ) } else { "".to_owned() diff --git a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs index d06c9d65df328..0bb18c73cfc66 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/assists_doc_tests.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `assists_generated.md` documentation. use std::{fmt, fs, path::Path}; @@ -62,7 +62,7 @@ r#####" crate::flags::CodegenType::AssistsDocTests, assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"), ); - let dst = project_root().join("docs/user/generated_assists.adoc"); + let dst = project_root().join("docs/book/src/assists_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -146,7 +146,7 @@ impl fmt::Display for Assist { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let _ = writeln!( f, - "[discrete]\n=== `{}` + "### `{}` **Source:** {}", self.id, self.location, ); @@ -159,11 +159,11 @@ impl fmt::Display for Assist { " {} -.Before +#### Before ```rust {}``` -.After +#### After ```rust {}```", section.doc, diff --git a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs index 4cb8f3f259d75..cf8f97be009ba 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/diagnostics_docs.rs @@ -1,4 +1,4 @@ -//! Generates `assists.md` documentation. +//! Generates `diagnostics_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; @@ -14,7 +14,7 @@ pub(crate) fn generate(check: bool) { let contents = diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); let contents = add_preamble(crate::flags::CodegenType::DiagnosticsDocs, contents); - let dst = project_root().join("docs/user/generated_diagnostic.adoc"); + let dst = project_root().join("docs/book/src/diagnostics_generated.md"); fs::write(dst, contents).unwrap(); } } @@ -73,6 +73,6 @@ fn is_valid_diagnostic_name(diagnostic: &str) -> Result<(), String> { impl fmt::Display for Diagnostic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "#### {}\n\nSource: {}\n\n{}\n\n", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs index c6451d888b03a..51ff13aba81e2 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/feature_docs.rs @@ -1,9 +1,9 @@ -//! Generates `assists.md` documentation. +//! Generates `features_generated.md` documentation. use std::{fmt, fs, io, path::PathBuf}; use crate::{ - codegen::{CommentBlock, Location}, + codegen::{add_preamble, CommentBlock, Location}, project_root, util::list_rust_files, }; @@ -11,14 +11,8 @@ use crate::{ pub(crate) fn generate(_check: bool) { let features = Feature::collect().unwrap(); let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); - let contents = format!( - " -// Generated file, do not edit by hand, see `sourcegen_feature_docs`. -{} -", - contents.trim() - ); - let dst = project_root().join("docs/user/generated_features.adoc"); + let contents = add_preamble(crate::flags::CodegenType::FeatureDocs, contents); + let dst = project_root().join("docs/book/src/features_generated.md"); fs::write(dst, contents).unwrap(); } @@ -80,6 +74,6 @@ fn is_valid_feature_name(feature: &str) -> Result<(), String> { impl fmt::Display for Feature { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "=== {}\n**Source:** {}\n{}", self.id, self.location, self.doc) + writeln!(f, "### {}\n**Source:** {}\n{}", self.id, self.location, self.doc) } } diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs index 1a1364c7d10ee..8e56ce439c55f 100644 --- a/src/tools/rust-analyzer/xtask/src/release.rs +++ b/src/tools/rust-analyzer/xtask/src/release.rs @@ -9,7 +9,7 @@ use directories::ProjectDirs; use stdx::JodChild; use xshell::{cmd, Shell}; -use crate::{codegen, date_iso, flags, is_release_tag, project_root}; +use crate::{date_iso, flags, is_release_tag, project_root}; impl flags::Release { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { @@ -29,11 +29,6 @@ impl flags::Release { cmd!(sh, "git push --force").run()?; } - // Generates bits of manual.adoc. - codegen::diagnostics_docs::generate(false); - codegen::assists_doc_tests::generate(false); - codegen::feature_docs::generate(false); - let website_root = project_root().join("../rust-analyzer.github.io"); { let _dir = sh.push_dir(&website_root); @@ -54,20 +49,6 @@ impl flags::Release { .max() .unwrap_or_default(); - for adoc in [ - "manual.adoc", - "generated_assists.adoc", - "generated_config.adoc", - "generated_diagnostic.adoc", - "generated_features.adoc", - ] { - let src = project_root().join("./docs/user/").join(adoc); - let dst = website_root.join(adoc); - - let contents = sh.read_file(src)?; - sh.write_file(dst, contents)?; - } - let tags = cmd!(sh, "git tag --list").read()?; let prev_tag = tags.lines().filter(|line| is_release_tag(line)).last().unwrap(); From b46775a5f4e48ea740fd34f9d69bc5bce05cb88e Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sat, 25 Jan 2025 14:27:47 +0100 Subject: [PATCH 03/75] Combine `pat_hyigene` and `expr_hygiene` --- .../rust-analyzer/crates/hir-def/src/body.rs | 20 ++++++++----------- .../crates/hir-def/src/body/lower.rs | 6 +++--- .../rust-analyzer/crates/hir-def/src/hir.rs | 2 ++ 3 files changed, 13 insertions(+), 15 deletions(-) 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 de43924930632..139e1ba896ce0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -3,6 +3,7 @@ mod lower; mod pretty; pub mod scope; + #[cfg(test)] mod tests; @@ -92,11 +93,9 @@ pub struct Body { binding_hygiene: FxHashMap<BindingId, HygieneId>, /// A map from an variable usages to their hygiene ID. /// - /// Expressions that can be recorded here are single segment path, although not all single segments path refer + /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer /// to variables and have hygiene (some refer to items, we don't know at this stage). - expr_hygiene: FxHashMap<ExprId, HygieneId>, - /// A map from a destructuring assignment possible variable usages to their hygiene ID. - pat_hygiene: FxHashMap<PatId, HygieneId>, + ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } pub type ExprPtr = AstPtr<ast::Expr>; @@ -317,8 +316,7 @@ impl Body { bindings, binding_owners, binding_hygiene, - expr_hygiene, - pat_hygiene, + ident_hygiene, types, } = self; block_scopes.shrink_to_fit(); @@ -328,8 +326,7 @@ impl Body { bindings.shrink_to_fit(); binding_owners.shrink_to_fit(); binding_hygiene.shrink_to_fit(); - expr_hygiene.shrink_to_fit(); - pat_hygiene.shrink_to_fit(); + ident_hygiene.shrink_to_fit(); types.shrink_to_fit(); } @@ -658,11 +655,11 @@ impl Body { } pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId { - self.expr_hygiene.get(&expr).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId { - self.pat_hygiene.get(&pat).copied().unwrap_or(HygieneId::ROOT) + self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT) } pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId { @@ -686,8 +683,7 @@ impl Default for Body { binding_owners: Default::default(), self_param: Default::default(), binding_hygiene: Default::default(), - expr_hygiene: Default::default(), - pat_hygiene: Default::default(), + ident_hygiene: Default::default(), types: Default::default(), } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 16c7b5ca00a07..583c6ac5e8c7c 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 @@ -480,7 +480,7 @@ impl ExprCollector<'_> { .unwrap_or((Expr::Missing, HygieneId::ROOT)); let expr_id = self.alloc_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.body.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id } @@ -835,7 +835,7 @@ impl ExprCollector<'_> { .unwrap_or((Pat::Missing, HygieneId::ROOT)); let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.pat_hygiene.insert(pat_id, hygiene); + self.body.ident_hygiene.insert(pat_id.into(), hygiene); } pat_id } @@ -2023,7 +2023,7 @@ impl ExprCollector<'_> { ); } if !hygiene.is_root() { - self.body.expr_hygiene.insert(expr_id, hygiene); + self.body.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 8596346943022..9392e8d12d40f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -44,6 +44,8 @@ pub(crate) fn dummy_expr_id() -> ExprId { pub type PatId = Idx<Pat>; +// FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts +// are local to the body. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum ExprOrPatId { ExprId(ExprId), From 8e7a3381650dd7fc86354649a856163bcbceea52 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sat, 25 Jan 2025 14:43:22 +0100 Subject: [PATCH 04/75] BodyCollector --- .../rust-analyzer/crates/hir-def/src/body.rs | 112 ++++++++++-------- .../crates/hir-def/src/body/lower.rs | 49 ++++---- .../rust-analyzer/crates/hir-def/src/hir.rs | 7 +- 3 files changed, 91 insertions(+), 77 deletions(-) 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 139e1ba896ce0..334177751e4c4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -25,8 +25,8 @@ use crate::{ db::DefDatabase, expander::Expander, hir::{ - dummy_expr_id, Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, - LabelId, Pat, PatId, RecordFieldPat, Statement, + Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, + PatId, RecordFieldPat, Statement, }, item_tree::AttrOwner, nameres::DefMap, @@ -81,7 +81,7 @@ pub struct Body { pub body_expr: ExprId, pub types: TypesMap, /// Block expressions in this body that may contain inner items. - block_scopes: Vec<BlockId>, + block_scopes: Box<[BlockId]>, /// A map from binding to its hygiene ID. /// @@ -98,6 +98,64 @@ pub struct Body { ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq, Default)] +pub struct BodyCollector { + pub exprs: Arena<Expr>, + pub pats: Arena<Pat>, + pub bindings: Arena<Binding>, + pub labels: Arena<Label>, + pub binding_owners: FxHashMap<BindingId, ExprId>, + pub types: TypesMap, + block_scopes: Vec<BlockId>, + binding_hygiene: FxHashMap<BindingId, HygieneId>, + ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, +} + +impl BodyCollector { + fn finish( + self, + body_expr: ExprId, + self_param: Option<BindingId>, + params: Box<[PatId]>, + ) -> Body { + let Self { + block_scopes, + mut exprs, + mut labels, + mut pats, + mut bindings, + mut binding_owners, + mut binding_hygiene, + mut ident_hygiene, + mut types, + } = self; + exprs.shrink_to_fit(); + labels.shrink_to_fit(); + pats.shrink_to_fit(); + bindings.shrink_to_fit(); + binding_owners.shrink_to_fit(); + binding_hygiene.shrink_to_fit(); + ident_hygiene.shrink_to_fit(); + types.shrink_to_fit(); + + Body { + exprs, + pats, + bindings, + labels, + binding_owners, + params, + self_param, + body_expr, + types, + block_scopes: block_scopes.into_boxed_slice(), + binding_hygiene, + ident_hygiene, + } + } +} + pub type ExprPtr = AstPtr<ast::Expr>; pub type ExprSource = InFile<ExprPtr>; @@ -242,9 +300,8 @@ impl Body { }; let module = def.module(db); let expander = Expander::new(db, file_id, module); - let (mut body, mut source_map) = + let (body, mut source_map) = Body::new(db, def, expander, params, body, module.krate, is_async_fn); - body.shrink_to_fit(); source_map.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) @@ -304,32 +361,6 @@ impl Body { lower::lower(db, owner, expander, params, body, krate, is_async_fn) } - fn shrink_to_fit(&mut self) { - let Self { - body_expr: _, - params: _, - self_param: _, - block_scopes, - exprs, - labels, - pats, - bindings, - binding_owners, - binding_hygiene, - ident_hygiene, - types, - } = self; - block_scopes.shrink_to_fit(); - exprs.shrink_to_fit(); - labels.shrink_to_fit(); - pats.shrink_to_fit(); - bindings.shrink_to_fit(); - binding_owners.shrink_to_fit(); - binding_hygiene.shrink_to_fit(); - ident_hygiene.shrink_to_fit(); - types.shrink_to_fit(); - } - pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { self.walk_pats(pat_id, &mut |pat| { if let Pat::Bind { id, .. } = &self[pat] { @@ -670,25 +701,6 @@ impl Body { } } -impl Default for Body { - fn default() -> Self { - Self { - body_expr: dummy_expr_id(), - exprs: Default::default(), - pats: Default::default(), - bindings: Default::default(), - labels: Default::default(), - params: Default::default(), - block_scopes: Default::default(), - binding_owners: Default::default(), - self_param: Default::default(), - binding_hygiene: Default::default(), - ident_hygiene: Default::default(), - types: Default::default(), - } - } -} - impl Index<ExprId> for Body { type Output = Expr; 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 583c6ac5e8c7c..2b25dbdd7f24e 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,7 +29,9 @@ use triomphe::Arc; use crate::{ attr::Attrs, - body::{Body, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr}, + body::{ + Body, BodyCollector, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr, + }, builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, @@ -82,7 +84,7 @@ pub(super) fn lower( def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), ast_id_map: db.ast_id_map(expander.current_file_id()), - body: Body::default(), + body: BodyCollector::default(), expander, current_try_block_label: None, is_lowering_coroutine: false, @@ -102,7 +104,7 @@ struct ExprCollector<'a> { def_map: Arc<DefMap>, ast_id_map: Arc<AstIdMap>, krate: CrateId, - body: Body, + body: BodyCollector, source_map: BodySourceMap, is_lowering_coroutine: bool, @@ -214,6 +216,9 @@ impl ExprCollector<'_> { body: Option<ast::Expr>, is_async_fn: bool, ) -> (Body, BodySourceMap) { + let mut self_param = None; + let mut params = vec![]; + let skip_body = match self.owner { DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()), DefWithBodyId::StaticId(it) => self.db.attrs(it.into()), @@ -226,29 +231,32 @@ impl ExprCollector<'_> { // If #[rust_analyzer::skip] annotated, only construct enough information for the signature // and skip the body. if skip_body { - self.body.body_expr = self.missing_expr(); if let Some((param_list, mut attr_enabled)) = param_list { - if let Some(self_param) = + if let Some(self_param_syn) = param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) { - let is_mutable = - self_param.mut_token().is_some() && self_param.amp_token().is_none(); + let is_mutable = self_param_syn.mut_token().is_some() + && self_param_syn.amp_token().is_none(); let binding_id: la_arena::Idx<Binding> = self.alloc_binding( Name::new_symbol_root(sym::self_.clone()), BindingAnnotation::new(is_mutable, false), ); - self.body.self_param = Some(binding_id); + self_param = Some(binding_id); self.source_map.self_param = - Some(self.expander.in_file(AstPtr::new(&self_param))); + Some(self.expander.in_file(AstPtr::new(&self_param_syn))); } - self.body.params = param_list + params = param_list .params() .zip(attr_enabled) .filter(|(_, enabled)| *enabled) .map(|_| self.missing_pat()) .collect(); }; - return (self.body, self.source_map); + let body_expr = self.missing_expr(); + return ( + self.body.finish(body_expr, self_param, params.into_boxed_slice()), + self.source_map, + ); } self.awaitable_context.replace(if is_async_fn { @@ -264,25 +272,25 @@ impl ExprCollector<'_> { } }); if let Some((param_list, mut attr_enabled)) = param_list { - let mut params = vec![]; - if let Some(self_param) = + if let Some(self_param_syn) = param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) { let is_mutable = - self_param.mut_token().is_some() && self_param.amp_token().is_none(); + self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); let binding_id: la_arena::Idx<Binding> = self.alloc_binding( Name::new_symbol_root(sym::self_.clone()), BindingAnnotation::new(is_mutable, false), ); - let hygiene = self_param + let hygiene = self_param_syn .name() .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) .unwrap_or(HygieneId::ROOT); if !hygiene.is_root() { self.body.binding_hygiene.insert(binding_id, hygiene); } - self.body.self_param = Some(binding_id); - self.source_map.self_param = Some(self.expander.in_file(AstPtr::new(&self_param))); + self_param = Some(binding_id); + self.source_map.self_param = + Some(self.expander.in_file(AstPtr::new(&self_param_syn))); } for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) @@ -290,9 +298,8 @@ impl ExprCollector<'_> { let param_pat = self.collect_pat_top(param.pat()); params.push(param_pat); } - self.body.params = params.into_boxed_slice(); }; - self.body.body_expr = self.with_label_rib(RibKind::Closure, |this| { + let body_expr = self.with_label_rib(RibKind::Closure, |this| { if is_async_fn { match body { Some(e) => { @@ -310,7 +317,7 @@ impl ExprCollector<'_> { } }); - (self.body, self.source_map) + (self.body.finish(body_expr, self_param, params.into_boxed_slice()), self.source_map) } fn ctx(&mut self) -> LowerCtx<'_> { @@ -1934,7 +1941,7 @@ impl ExprCollector<'_> { f: impl FnOnce(&mut Self) -> T, ) -> T { self.label_ribs.push(LabelRib::new(RibKind::Normal( - self.body[label].name.clone(), + self.body.labels[label].name.clone(), label, hygiene, ))); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 9392e8d12d40f..0dcddf162b2fa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -19,7 +19,7 @@ use std::fmt; use hir_expand::{name::Name, MacroDefId}; use intern::Symbol; -use la_arena::{Idx, RawIdx}; +use la_arena::Idx; use rustc_apfloat::ieee::{Half as f16, Quad as f128}; use syntax::ast; use type_ref::TypeRefId; @@ -37,11 +37,6 @@ pub type BindingId = Idx<Binding>; pub type ExprId = Idx<Expr>; -/// FIXME: this is a hacky function which should be removed -pub(crate) fn dummy_expr_id() -> ExprId { - ExprId::from_raw(RawIdx::from(u32::MAX)) -} - pub type PatId = Idx<Pat>; // FIXME: Encode this as a single u32, we won't ever reach all 32 bits especially given these counts From d9c58c86367e80b7926a1844317f77b2268121c4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sat, 25 Jan 2025 14:47:16 +0100 Subject: [PATCH 05/75] Cleanup body ast ptr defs --- .../rust-analyzer/crates/hir-def/src/body.rs | 149 +++++++++--------- 1 file changed, 74 insertions(+), 75 deletions(-) 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 334177751e4c4..73c3f421aaf22 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -59,6 +59,27 @@ impl HygieneId { } } +pub type ExprPtr = AstPtr<ast::Expr>; +pub type ExprSource = InFile<ExprPtr>; + +pub type PatPtr = AstPtr<ast::Pat>; +pub type PatSource = InFile<PatPtr>; + +pub type LabelPtr = AstPtr<ast::Label>; +pub type LabelSource = InFile<LabelPtr>; + +pub type FieldPtr = AstPtr<ast::RecordExprField>; +pub type FieldSource = InFile<FieldPtr>; + +pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>; +pub type PatFieldSource = InFile<PatFieldPtr>; + +pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>; +pub type ExprOrPatSource = InFile<ExprOrPatPtr>; + +pub type SelfParamPtr = AstPtr<ast::SelfParam>; +pub type MacroCallPtr = AstPtr<ast::MacroCall>; + /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] pub struct Body { @@ -112,68 +133,6 @@ pub struct BodyCollector { ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } -impl BodyCollector { - fn finish( - self, - body_expr: ExprId, - self_param: Option<BindingId>, - params: Box<[PatId]>, - ) -> Body { - let Self { - block_scopes, - mut exprs, - mut labels, - mut pats, - mut bindings, - mut binding_owners, - mut binding_hygiene, - mut ident_hygiene, - mut types, - } = self; - exprs.shrink_to_fit(); - labels.shrink_to_fit(); - pats.shrink_to_fit(); - bindings.shrink_to_fit(); - binding_owners.shrink_to_fit(); - binding_hygiene.shrink_to_fit(); - ident_hygiene.shrink_to_fit(); - types.shrink_to_fit(); - - Body { - exprs, - pats, - bindings, - labels, - binding_owners, - params, - self_param, - body_expr, - types, - block_scopes: block_scopes.into_boxed_slice(), - binding_hygiene, - ident_hygiene, - } - } -} - -pub type ExprPtr = AstPtr<ast::Expr>; -pub type ExprSource = InFile<ExprPtr>; - -pub type PatPtr = AstPtr<ast::Pat>; -pub type PatSource = InFile<PatPtr>; - -pub type LabelPtr = AstPtr<ast::Label>; -pub type LabelSource = InFile<LabelPtr>; - -pub type FieldPtr = AstPtr<ast::RecordExprField>; -pub type FieldSource = InFile<FieldPtr>; - -pub type PatFieldPtr = AstPtr<Either<ast::RecordExprField, ast::RecordPatField>>; -pub type PatFieldSource = InFile<PatFieldPtr>; - -pub type ExprOrPatPtr = AstPtr<Either<ast::Expr, ast::Pat>>; -pub type ExprOrPatSource = InFile<ExprOrPatPtr>; - /// An item body together with the mapping from syntax nodes to HIR expression /// IDs. This is needed to go from e.g. a position in a file to the HIR /// expression containing it; but for type inference etc., we want to operate on @@ -198,7 +157,7 @@ pub struct BodySourceMap { label_map: FxHashMap<LabelSource, LabelId>, label_map_back: ArenaMap<LabelId, LabelSource>, - self_param: Option<InFile<AstPtr<ast::SelfParam>>>, + self_param: Option<InFile<SelfParamPtr>>, binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>, /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). @@ -210,7 +169,7 @@ pub struct BodySourceMap { template_map: Option<Box<FormatTemplate>>, - expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>, + expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>, /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in /// the source map (since they're just as volatile). @@ -228,19 +187,63 @@ struct FormatTemplate { /// The value stored for each capture is its template literal and offset inside it. The template literal /// is from the `format_args[_nl]!()` macro and so needs to be mapped up once to go to the user-written /// template. - implicit_capture_to_source: FxHashMap<ExprId, InFile<(AstPtr<ast::Expr>, TextRange)>>, + implicit_capture_to_source: FxHashMap<ExprId, InFile<(ExprPtr, TextRange)>>, } #[derive(Debug, Eq, PartialEq)] pub enum BodyDiagnostic { InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, - MacroError { node: InFile<AstPtr<ast::MacroCall>>, err: ExpandError }, - UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, + MacroError { node: InFile<MacroCallPtr>, err: ExpandError }, + UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath }, UnreachableLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, AwaitOutsideOfAsync { node: InFile<AstPtr<ast::AwaitExpr>>, location: String }, UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, } +impl BodyCollector { + fn finish( + self, + body_expr: ExprId, + self_param: Option<BindingId>, + params: Box<[PatId]>, + ) -> Body { + let Self { + block_scopes, + mut exprs, + mut labels, + mut pats, + mut bindings, + mut binding_owners, + mut binding_hygiene, + mut ident_hygiene, + mut types, + } = self; + exprs.shrink_to_fit(); + labels.shrink_to_fit(); + pats.shrink_to_fit(); + bindings.shrink_to_fit(); + binding_owners.shrink_to_fit(); + binding_hygiene.shrink_to_fit(); + ident_hygiene.shrink_to_fit(); + types.shrink_to_fit(); + + Body { + exprs, + pats, + bindings, + labels, + binding_owners, + params, + self_param, + body_expr, + types, + block_scopes: block_scopes.into_boxed_slice(), + binding_hygiene, + ident_hygiene, + } + } +} + impl Body { pub(crate) fn body_with_source_map_query( db: &dyn DefDatabase, @@ -765,9 +768,7 @@ impl BodySourceMap { self.expansions.get(&src).cloned() } - pub fn macro_calls( - &self, - ) -> impl Iterator<Item = (InFile<AstPtr<ast::MacroCall>>, MacroFileId)> + '_ { + pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroFileId)> + '_ { self.expansions.iter().map(|(&a, &b)| (a, b)) } @@ -775,7 +776,7 @@ impl BodySourceMap { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } - pub fn self_param_syntax(&self) -> Option<InFile<AstPtr<ast::SelfParam>>> { + pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> { self.self_param } @@ -809,9 +810,7 @@ impl BodySourceMap { self.expr_map.get(&src).copied() } - pub fn expansions( - &self, - ) -> impl Iterator<Item = (&InFile<AstPtr<ast::MacroCall>>, &MacroFileId)> { + pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroFileId)> { self.expansions.iter() } @@ -831,7 +830,7 @@ impl BodySourceMap { pub fn format_args_implicit_capture( &self, capture_expr: ExprId, - ) -> Option<InFile<(AstPtr<ast::Expr>, TextRange)>> { + ) -> Option<InFile<(ExprPtr, TextRange)>> { self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied() } From c679d562057cfbab1527dc501a1a099dade953d1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sat, 25 Jan 2025 15:52:52 +0100 Subject: [PATCH 06/75] Split out `ExpressionStore` from `Body` --- .../rust-analyzer/crates/hir-def/src/db.rs | 2 +- .../hir-def/src/{body.rs => expr_store.rs} | 226 ++-------- .../crates/hir-def/src/expr_store/body.rs | 175 ++++++++ .../hir-def/src/{body => expr_store}/lower.rs | 389 ++++++++++-------- .../src/{body => expr_store}/lower/asm.rs | 2 +- .../src/{body => expr_store}/pretty.rs | 44 +- .../hir-def/src/{body => expr_store}/scope.rs | 54 +-- .../hir-def/src/{body => expr_store}/tests.rs | 1 + .../src/{body => expr_store}/tests/block.rs | 0 .../rust-analyzer/crates/hir-def/src/lib.rs | 12 +- .../crates/hir-def/src/resolver.rs | 8 +- .../crates/hir-ty/src/consteval.rs | 2 +- .../crates/hir-ty/src/diagnostics/expr.rs | 2 +- .../hir-ty/src/diagnostics/match_check.rs | 3 +- .../hir-ty/src/diagnostics/unsafe_check.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/infer/diagnostics.rs | 2 +- .../crates/hir-ty/src/infer/pat.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/lower.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/mir.rs | 2 +- .../crates/hir-ty/src/mir/eval.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../crates/hir-ty/src/mir/pretty.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 +- .../crates/hir/src/diagnostics.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 26 +- .../crates/hir/src/source_analyzer.rs | 2 +- .../rust-analyzer/src/cli/analysis_stats.rs | 2 +- 28 files changed, 531 insertions(+), 445 deletions(-) rename src/tools/rust-analyzer/crates/hir-def/src/{body.rs => expr_store.rs} (78%) create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/lower.rs (91%) rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/lower/asm.rs (99%) rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/pretty.rs (95%) rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/scope.rs (90%) rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/tests.rs (99%) rename src/tools/rust-analyzer/crates/hir-def/src/{body => expr_store}/tests/block.rs (100%) 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 bf6cc1dcadec5..598a850898bb6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/db.rs @@ -10,12 +10,12 @@ use triomphe::Arc; use crate::{ attr::{Attrs, AttrsWithOwner}, - body::{scope::ExprScopes, Body, BodySourceMap}, data::{ adt::{EnumData, EnumVariantData, StructData, VariantData}, ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData, ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData, }, + expr_store::{scope::ExprScopes, Body, BodySourceMap}, generics::GenericParams, import_map::ImportMap, item_tree::{AttrOwner, ItemTree, ItemTreeSourceMaps}, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs similarity index 78% rename from src/tools/rust-analyzer/crates/hir-def/src/body.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 73c3f421aaf22..9df6eaade757a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -1,5 +1,6 @@ -//! Defines `Body`: a lowered representation of bodies of functions, statics and +//! Defines `ExpressionStore`: a lowered representation of functions, statics and //! consts. +mod body; mod lower; mod pretty; pub mod scope; @@ -9,11 +10,10 @@ mod tests; use std::ops::{Deref, Index}; -use base_db::CrateId; use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_expand::{name::Name, ExpandError, InFile}; -use la_arena::{Arena, ArenaMap, Idx, RawIdx}; +use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; use smallvec::SmallVec; use span::{Edition, MacroFileId, SyntaxContextData}; @@ -23,19 +23,18 @@ use tt::TextRange; use crate::{ db::DefDatabase, - expander::Expander, hir::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, PatId, RecordFieldPat, Statement, }, - item_tree::AttrOwner, nameres::DefMap, path::{ModPath, Path}, - src::HasSource, type_ref::{TypeRef, TypeRefId, TypesMap, TypesSourceMap}, - BlockId, DefWithBodyId, HasModule, Lookup, SyntheticSyntax, + BlockId, DefWithBodyId, Lookup, SyntheticSyntax, }; +pub use self::body::{Body, BodySourceMap}; + /// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HygieneId(span::SyntaxContextId); @@ -80,9 +79,8 @@ pub type ExprOrPatSource = InFile<ExprOrPatPtr>; pub type SelfParamPtr = AstPtr<ast::SelfParam>; pub type MacroCallPtr = AstPtr<ast::MacroCall>; -/// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq)] -pub struct Body { +pub struct ExpressionStore { pub exprs: Arena<Expr>, pub pats: Arena<Pat>, pub bindings: Arena<Binding>, @@ -90,18 +88,8 @@ pub struct Body { /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the /// top level expression, it will not be listed in here. pub binding_owners: FxHashMap<BindingId, ExprId>, - /// The patterns for the function's parameters. While the parameter types are - /// part of the function signature, the patterns are not (they don't change - /// the external type of the function). - /// - /// If this `Body` is for the body of a constant, this will just be - /// empty. - pub params: Box<[PatId]>, - pub self_param: Option<BindingId>, - /// The `ExprId` of the actual body expression. - pub body_expr: ExprId, pub types: TypesMap, - /// Block expressions in this body that may contain inner items. + /// Block expressions in this store that may contain inner items. block_scopes: Box<[BlockId]>, /// A map from binding to its hygiene ID. @@ -119,33 +107,8 @@ pub struct Body { ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } -/// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq, Default)] -pub struct BodyCollector { - pub exprs: Arena<Expr>, - pub pats: Arena<Pat>, - pub bindings: Arena<Binding>, - pub labels: Arena<Label>, - pub binding_owners: FxHashMap<BindingId, ExprId>, - pub types: TypesMap, - block_scopes: Vec<BlockId>, - binding_hygiene: FxHashMap<BindingId, HygieneId>, - ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, -} - -/// An item body together with the mapping from syntax nodes to HIR expression -/// IDs. This is needed to go from e.g. a position in a file to the HIR -/// expression containing it; but for type inference etc., we want to operate on -/// a structure that is agnostic to the actual positions of expressions in the -/// file, so that we don't recompute types whenever some whitespace is typed. -/// -/// One complication here is that, due to macro expansion, a single `Body` might -/// be spread across several files. So, for each ExprId and PatId, we record -/// both the HirFileId and the position inside the file. However, we only store -/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle -/// this properly for macros. -#[derive(Default, Debug, Eq, PartialEq)] -pub struct BodySourceMap { +pub struct ExpressionStoreSourceMap { // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). expr_map: FxHashMap<ExprSource, ExprOrPatId>, @@ -157,7 +120,6 @@ pub struct BodySourceMap { label_map: FxHashMap<LabelSource, LabelId>, label_map_back: ArenaMap<LabelId, LabelSource>, - self_param: Option<InFile<SelfParamPtr>>, binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>, /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). @@ -171,9 +133,23 @@ pub struct BodySourceMap { expansions: FxHashMap<InFile<MacroCallPtr>, MacroFileId>, - /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in + /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in /// the source map (since they're just as volatile). - diagnostics: Vec<BodyDiagnostic>, + diagnostics: Vec<ExpressionStoreDiagnostics>, +} + +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq, Default)] +pub struct ExpressionStoreBuilder { + pub exprs: Arena<Expr>, + pub pats: Arena<Pat>, + pub bindings: Arena<Binding>, + pub labels: Arena<Label>, + pub binding_owners: FxHashMap<BindingId, ExprId>, + pub types: TypesMap, + block_scopes: Vec<BlockId>, + binding_hygiene: FxHashMap<BindingId, HygieneId>, + ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>, } #[derive(Default, Debug, Eq, PartialEq)] @@ -191,7 +167,7 @@ struct FormatTemplate { } #[derive(Debug, Eq, PartialEq)] -pub enum BodyDiagnostic { +pub enum ExpressionStoreDiagnostics { InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, MacroError { node: InFile<MacroCallPtr>, err: ExpandError }, UnresolvedMacroCall { node: InFile<MacroCallPtr>, path: ModPath }, @@ -200,13 +176,8 @@ pub enum BodyDiagnostic { UndeclaredLabel { node: InFile<AstPtr<ast::Lifetime>>, name: Name }, } -impl BodyCollector { - fn finish( - self, - body_expr: ExprId, - self_param: Option<BindingId>, - params: Box<[PatId]>, - ) -> Body { +impl ExpressionStoreBuilder { + fn finish(self) -> ExpressionStore { let Self { block_scopes, mut exprs, @@ -227,15 +198,12 @@ impl BodyCollector { ident_hygiene.shrink_to_fit(); types.shrink_to_fit(); - Body { + ExpressionStore { exprs, pats, bindings, labels, binding_owners, - params, - self_param, - body_expr, types, block_scopes: block_scopes.into_boxed_slice(), binding_hygiene, @@ -244,77 +212,8 @@ impl BodyCollector { } } -impl Body { - pub(crate) fn body_with_source_map_query( - db: &dyn DefDatabase, - def: DefWithBodyId, - ) -> (Arc<Body>, Arc<BodySourceMap>) { - let _p = tracing::info_span!("body_with_source_map_query").entered(); - let mut params = None; - - let mut is_async_fn = false; - let InFile { file_id, value: body } = { - match def { - DefWithBodyId::FunctionId(f) => { - let data = db.function_data(f); - let f = f.lookup(db); - let src = f.source(db); - params = src.value.param_list().map(move |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, - (0..func.params.len()).map(move |idx| { - item_tree - .attrs( - db, - krate, - AttrOwner::Param( - f.id.value, - Idx::from_raw(RawIdx::from(idx as u32)), - ), - ) - .is_cfg_enabled(&crate_graph[krate].cfg_options) - }), - ) - }); - is_async_fn = data.is_async(); - src.map(|it| it.body().map(ast::Expr::from)) - } - DefWithBodyId::ConstId(c) => { - let c = c.lookup(db); - let src = c.source(db); - src.map(|it| it.body()) - } - DefWithBodyId::StaticId(s) => { - let s = s.lookup(db); - let src = s.source(db); - src.map(|it| it.body()) - } - DefWithBodyId::VariantId(v) => { - let s = v.lookup(db); - let src = s.source(db); - src.map(|it| it.expr()) - } - DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), - } - }; - let module = def.module(db); - let expander = Expander::new(db, file_id, module); - let (body, mut source_map) = - Body::new(db, def, expander, params, body, module.krate, is_async_fn); - source_map.shrink_to_fit(); - - (Arc::new(body), Arc::new(source_map)) - } - - pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> { - db.body_with_source_map(def).0 - } - - /// Returns an iterator over all block expressions in this body that define inner items. +impl ExpressionStore { + /// Returns an iterator over all block expressions in this store that define inner items. pub fn blocks<'a>( &'a self, db: &'a dyn DefDatabase, @@ -322,48 +221,6 @@ impl Body { self.block_scopes.iter().map(move |&block| (block, db.block_def_map(block))) } - pub fn pretty_print( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - edition: Edition, - ) -> String { - pretty::print_body_hir(db, self, owner, edition) - } - - pub fn pretty_print_expr( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - expr: ExprId, - edition: Edition, - ) -> String { - pretty::print_expr_hir(db, self, owner, expr, edition) - } - - pub fn pretty_print_pat( - &self, - db: &dyn DefDatabase, - owner: DefWithBodyId, - pat: PatId, - oneline: bool, - edition: Edition, - ) -> String { - pretty::print_pat_hir(db, self, owner, pat, oneline, edition) - } - - fn new( - db: &dyn DefDatabase, - owner: DefWithBodyId, - expander: Expander, - params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, - body: Option<ast::Expr>, - krate: CrateId, - is_async_fn: bool, - ) -> (Body, BodySourceMap) { - lower::lower(db, owner, expander, params, body, krate, is_async_fn) - } - pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) { self.walk_pats(pat_id, &mut |pat| { if let Pat::Bind { id, .. } = &self[pat] { @@ -704,7 +561,7 @@ impl Body { } } -impl Index<ExprId> for Body { +impl Index<ExprId> for ExpressionStore { type Output = Expr; fn index(&self, expr: ExprId) -> &Expr { @@ -712,7 +569,7 @@ impl Index<ExprId> for Body { } } -impl Index<PatId> for Body { +impl Index<PatId> for ExpressionStore { type Output = Pat; fn index(&self, pat: PatId) -> &Pat { @@ -720,7 +577,7 @@ impl Index<PatId> for Body { } } -impl Index<LabelId> for Body { +impl Index<LabelId> for ExpressionStore { type Output = Label; fn index(&self, label: LabelId) -> &Label { @@ -728,7 +585,7 @@ impl Index<LabelId> for Body { } } -impl Index<BindingId> for Body { +impl Index<BindingId> for ExpressionStore { type Output = Binding; fn index(&self, b: BindingId) -> &Binding { @@ -736,7 +593,7 @@ impl Index<BindingId> for Body { } } -impl Index<TypeRefId> for Body { +impl Index<TypeRefId> for ExpressionStore { type Output = TypeRef; fn index(&self, b: TypeRefId) -> &TypeRef { @@ -746,7 +603,7 @@ impl Index<TypeRefId> for Body { // FIXME: Change `node_` prefix to something more reasonable. // Perhaps `expr_syntax` and `expr_id`? -impl BodySourceMap { +impl ExpressionStoreSourceMap { pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> { match id { ExprOrPatId::ExprId(id) => self.expr_syntax(id).map(|it| it.map(AstPtr::wrap_left)), @@ -776,10 +633,6 @@ impl BodySourceMap { self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax) } - pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> { - self.self_param - } - pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { self.pat_map.get(&node.map(AstPtr::new)).cloned() } @@ -844,14 +697,13 @@ impl BodySourceMap { .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref)) } - /// Get a reference to the body source map's diagnostics. - pub fn diagnostics(&self) -> &[BodyDiagnostic] { + /// Get a reference to the source map's diagnostics. + pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] { &self.diagnostics } fn shrink_to_fit(&mut self) { let Self { - self_param: _, expr_map, expr_map_back, pat_map, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs new file mode 100644 index 0000000000000..a55fec4f8b1e1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -0,0 +1,175 @@ +//! Defines `Body`: a lowered representation of functions, statics and +//! consts. +use std::ops; + +use hir_expand::{InFile, Lookup}; +use la_arena::{Idx, RawIdx}; +use span::Edition; +use syntax::ast; +use triomphe::Arc; + +use crate::{ + db::DefDatabase, + expander::Expander, + expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr}, + hir::{BindingId, ExprId, PatId}, + item_tree::AttrOwner, + src::HasSource, + DefWithBodyId, HasModule, +}; + +/// The body of an item (function, const etc.). +#[derive(Debug, Eq, PartialEq)] +pub struct Body { + pub store: ExpressionStore, + /// The patterns for the function's parameters. While the parameter types are + /// part of the function signature, the patterns are not (they don't change + /// the external type of the function). + /// + /// If this `Body` is for the body of a constant, this will just be + /// empty. + pub params: Box<[PatId]>, + pub self_param: Option<BindingId>, + /// The `ExprId` of the actual body expression. + pub body_expr: ExprId, +} + +impl ops::Deref for Body { + type Target = ExpressionStore; + + fn deref(&self) -> &Self::Target { + &self.store + } +} + +/// An item body together with the mapping from syntax nodes to HIR expression +/// IDs. This is needed to go from e.g. a position in a file to the HIR +/// expression containing it; but for type inference etc., we want to operate on +/// a structure that is agnostic to the actual positions of expressions in the +/// file, so that we don't recompute types whenever some whitespace is typed. +/// +/// One complication here is that, due to macro expansion, a single `Body` might +/// be spread across several files. So, for each ExprId and PatId, we record +/// both the HirFileId and the position inside the file. However, we only store +/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle +/// this properly for macros. +#[derive(Default, Debug, Eq, PartialEq)] +pub struct BodySourceMap { + pub self_param: Option<InFile<SelfParamPtr>>, + pub store: ExpressionStoreSourceMap, +} + +impl ops::Deref for BodySourceMap { + type Target = ExpressionStoreSourceMap; + + fn deref(&self) -> &Self::Target { + &self.store + } +} + +impl Body { + pub(crate) fn body_with_source_map_query( + db: &dyn DefDatabase, + def: DefWithBodyId, + ) -> (Arc<Body>, Arc<BodySourceMap>) { + let _p = tracing::info_span!("body_with_source_map_query").entered(); + let mut params = None; + + let mut is_async_fn = false; + let InFile { file_id, value: body } = { + match def { + DefWithBodyId::FunctionId(f) => { + let data = db.function_data(f); + let f = f.lookup(db); + let src = f.source(db); + params = src.value.param_list().map(move |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, + (0..func.params.len()).map(move |idx| { + item_tree + .attrs( + db, + krate, + AttrOwner::Param( + f.id.value, + Idx::from_raw(RawIdx::from(idx as u32)), + ), + ) + .is_cfg_enabled(&crate_graph[krate].cfg_options) + }), + ) + }); + is_async_fn = data.is_async(); + src.map(|it| it.body().map(ast::Expr::from)) + } + DefWithBodyId::ConstId(c) => { + let c = c.lookup(db); + let src = c.source(db); + src.map(|it| it.body()) + } + DefWithBodyId::StaticId(s) => { + let s = s.lookup(db); + let src = s.source(db); + src.map(|it| it.body()) + } + DefWithBodyId::VariantId(v) => { + let s = v.lookup(db); + let src = s.source(db); + src.map(|it| it.expr()) + } + DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), + } + }; + let module = def.module(db); + let expander = Expander::new(db, file_id, module); + let (body, mut source_map) = + lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn); + source_map.store.shrink_to_fit(); + + (Arc::new(body), Arc::new(source_map)) + } + + pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> { + db.body_with_source_map(def).0 + } + + pub fn pretty_print( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + edition: Edition, + ) -> String { + pretty::print_body_hir(db, self, owner, edition) + } + + pub fn pretty_print_expr( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + expr: ExprId, + edition: Edition, + ) -> String { + pretty::print_expr_hir(db, self, owner, expr, edition) + } + + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } +} + +impl BodySourceMap { + pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> { + self.self_param + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs similarity index 91% rename from src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 2b25dbdd7f24e..dfc716eb84944 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -29,13 +29,14 @@ use triomphe::Arc; use crate::{ attr::Attrs, - body::{ - Body, BodyCollector, BodyDiagnostic, BodySourceMap, ExprPtr, HygieneId, LabelPtr, PatPtr, - }, builtin_type::BuiltinUint, data::adt::StructKind, db::DefDatabase, expander::Expander, + expr_store::{ + Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder, + ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, PatPtr, + }, hir::{ format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, @@ -57,11 +58,11 @@ use crate::{ type FxIndexSet<K> = indexmap::IndexSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; -pub(super) fn lower( +pub(super) fn lower_body( db: &dyn DefDatabase, owner: DefWithBodyId, expander: Expander, - params: Option<(ast::ParamList, impl Iterator<Item = bool>)>, + parameters: Option<(ast::ParamList, impl Iterator<Item = bool>)>, body: Option<ast::Expr>, krate: CrateId, is_async_fn: bool, @@ -77,35 +78,146 @@ pub(super) fn lower( }; Arc::clone(span_map) }); - ExprCollector { - db, - owner, - krate, - def_map: expander.module.def_map(db), - source_map: BodySourceMap::default(), - ast_id_map: db.ast_id_map(expander.current_file_id()), - body: BodyCollector::default(), - expander, - current_try_block_label: None, - is_lowering_coroutine: false, - label_ribs: Vec::new(), - current_binding_owner: None, - awaitable_context: None, - current_span_map: span_map, - current_block_legacy_macro_defs_count: FxHashMap::default(), - } - .collect(params, body, is_async_fn) + + let mut self_param = None; + let mut source_map_self_param = None; + let mut params = vec![]; + let mut collector = ExprCollector::new(db, owner, expander, krate, span_map); + + let skip_body = match owner { + DefWithBodyId::FunctionId(it) => db.attrs(it.into()), + DefWithBodyId::StaticId(it) => db.attrs(it.into()), + DefWithBodyId::ConstId(it) => db.attrs(it.into()), + DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, + DefWithBodyId::VariantId(it) => db.attrs(it.into()), + } + .rust_analyzer_tool() + .any(|attr| *attr.path() == tool_path![skip]); + // If #[rust_analyzer::skip] annotated, only construct enough information for the signature + // and skip the body. + if skip_body { + if let Some((param_list, mut attr_enabled)) = parameters { + if let Some(self_param_syn) = + param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) + { + let is_mutable = + self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); + let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( + Name::new_symbol_root(sym::self_.clone()), + BindingAnnotation::new(is_mutable, false), + ); + self_param = Some(binding_id); + source_map_self_param = + Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); + } + params = param_list + .params() + .zip(attr_enabled) + .filter(|(_, enabled)| *enabled) + .map(|_| collector.missing_pat()) + .collect(); + }; + let body_expr = collector.missing_expr(); + return ( + Body { + store: collector.store.finish(), + params: params.into_boxed_slice(), + self_param, + body_expr, + }, + BodySourceMap { self_param: source_map_self_param, store: collector.source_map }, + ); + } + + if let Some((param_list, mut attr_enabled)) = parameters { + if let Some(self_param_syn) = + param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) + { + let is_mutable = + self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); + let binding_id: la_arena::Idx<Binding> = collector.alloc_binding( + Name::new_symbol_root(sym::self_.clone()), + BindingAnnotation::new(is_mutable, false), + ); + let hygiene = self_param_syn + .name() + .map(|name| collector.hygiene_id_for(name.syntax().text_range().start())) + .unwrap_or(HygieneId::ROOT); + if !hygiene.is_root() { + collector.store.binding_hygiene.insert(binding_id, hygiene); + } + self_param = Some(binding_id); + source_map_self_param = Some(collector.expander.in_file(AstPtr::new(&self_param_syn))); + } + + for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) { + let param_pat = collector.collect_pat_top(param.pat()); + params.push(param_pat); + } + }; + + let body_expr = collector.collect( + body, + if is_async_fn { + Awaitable::Yes + } else { + match owner { + DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), + DefWithBodyId::StaticId(..) => Awaitable::No("static"), + DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => { + Awaitable::No("constant") + } + DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), + } + }, + ); + + ( + Body { + store: collector.store.finish(), + params: params.into_boxed_slice(), + self_param, + body_expr, + }, + BodySourceMap { self_param: source_map_self_param, store: collector.source_map }, + ) } +#[allow(dead_code)] +pub(super) fn lower( + db: &dyn DefDatabase, + owner: ExprStoreOwnerId, + expander: Expander, + body: Option<ast::Expr>, + krate: CrateId, +) -> (ExpressionStore, ExpressionStoreSourceMap) { + // We cannot leave the root span map empty and let any identifier from it be treated as root, + // because when inside nested macros `SyntaxContextId`s from the outer macro will be interleaved + // with the inner macro, and that will cause confusion because they won't be the same as `ROOT` + // even though they should be the same. Also, when the body comes from multiple expansions, their + // hygiene is different. + let span_map = expander.current_file_id().macro_file().map(|_| { + let SpanMap::ExpansionSpanMap(span_map) = expander.span_map(db) else { + panic!("in a macro file there should be `ExpansionSpanMap`"); + }; + Arc::clone(span_map) + }); + let mut expr_collector = ExprCollector::new(db, owner, expander, krate, span_map); + expr_collector.collect(body, Awaitable::No("?")); + (expr_collector.store.finish(), expr_collector.source_map) +} + +type ExprStoreOwnerId = DefWithBodyId; + struct ExprCollector<'a> { db: &'a dyn DefDatabase, expander: Expander, - owner: DefWithBodyId, + owner: ExprStoreOwnerId, def_map: Arc<DefMap>, ast_id_map: Arc<AstIdMap>, krate: CrateId, - body: BodyCollector, - source_map: BodySourceMap, + store: ExpressionStoreBuilder, + source_map: ExpressionStoreSourceMap, is_lowering_coroutine: bool, @@ -159,6 +271,7 @@ impl RibKind { } } +#[derive(PartialEq, Eq, Debug, Copy, Clone)] enum Awaitable { Yes, No(&'static str), @@ -182,12 +295,12 @@ impl BindingList { let id = *self.map.entry((name, hygiene)).or_insert_with_key(|(name, _)| { let id = ec.alloc_binding(name.clone(), mode); if !hygiene.is_root() { - ec.body.binding_hygiene.insert(id, hygiene); + ec.store.binding_hygiene.insert(id, hygiene); } id }); - if ec.body.bindings[id].mode != mode { - ec.body.bindings[id].problems = Some(BindingProblems::BoundInconsistently); + if ec.store.bindings[id].mode != mode { + ec.store.bindings[id].problems = Some(BindingProblems::BoundInconsistently); } self.check_is_used(ec, id); id @@ -197,11 +310,11 @@ impl BindingList { match self.is_used.get(&id) { None => { if self.reject_new { - ec.body.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); + ec.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } } Some(true) => { - ec.body.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); + ec.store.bindings[id].problems = Some(BindingProblems::BoundMoreThanOnce); } Some(false) => {} } @@ -210,98 +323,37 @@ impl BindingList { } impl ExprCollector<'_> { - fn collect( - mut self, - param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>, - body: Option<ast::Expr>, - is_async_fn: bool, - ) -> (Body, BodySourceMap) { - let mut self_param = None; - let mut params = vec![]; - - let skip_body = match self.owner { - DefWithBodyId::FunctionId(it) => self.db.attrs(it.into()), - DefWithBodyId::StaticId(it) => self.db.attrs(it.into()), - DefWithBodyId::ConstId(it) => self.db.attrs(it.into()), - DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, - DefWithBodyId::VariantId(it) => self.db.attrs(it.into()), + fn new( + db: &dyn DefDatabase, + owner: ExprStoreOwnerId, + expander: Expander, + krate: CrateId, + span_map: Option<Arc<ExpansionSpanMap>>, + ) -> ExprCollector<'_> { + ExprCollector { + db, + owner, + krate, + def_map: expander.module.def_map(db), + source_map: ExpressionStoreSourceMap::default(), + ast_id_map: db.ast_id_map(expander.current_file_id()), + store: ExpressionStoreBuilder::default(), + expander, + current_try_block_label: None, + is_lowering_coroutine: false, + label_ribs: Vec::new(), + current_binding_owner: None, + awaitable_context: None, + current_span_map: span_map, + current_block_legacy_macro_defs_count: FxHashMap::default(), } - .rust_analyzer_tool() - .any(|attr| *attr.path() == tool_path![skip]); - // If #[rust_analyzer::skip] annotated, only construct enough information for the signature - // and skip the body. - if skip_body { - if let Some((param_list, mut attr_enabled)) = param_list { - if let Some(self_param_syn) = - param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) - { - let is_mutable = self_param_syn.mut_token().is_some() - && self_param_syn.amp_token().is_none(); - let binding_id: la_arena::Idx<Binding> = self.alloc_binding( - Name::new_symbol_root(sym::self_.clone()), - BindingAnnotation::new(is_mutable, false), - ); - self_param = Some(binding_id); - self.source_map.self_param = - Some(self.expander.in_file(AstPtr::new(&self_param_syn))); - } - params = param_list - .params() - .zip(attr_enabled) - .filter(|(_, enabled)| *enabled) - .map(|_| self.missing_pat()) - .collect(); - }; - let body_expr = self.missing_expr(); - return ( - self.body.finish(body_expr, self_param, params.into_boxed_slice()), - self.source_map, - ); - } - - self.awaitable_context.replace(if is_async_fn { - Awaitable::Yes - } else { - match self.owner { - DefWithBodyId::FunctionId(..) => Awaitable::No("non-async function"), - DefWithBodyId::StaticId(..) => Awaitable::No("static"), - DefWithBodyId::ConstId(..) | DefWithBodyId::InTypeConstId(..) => { - Awaitable::No("constant") - } - DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), - } - }); - if let Some((param_list, mut attr_enabled)) = param_list { - if let Some(self_param_syn) = - param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false)) - { - let is_mutable = - self_param_syn.mut_token().is_some() && self_param_syn.amp_token().is_none(); - let binding_id: la_arena::Idx<Binding> = self.alloc_binding( - Name::new_symbol_root(sym::self_.clone()), - BindingAnnotation::new(is_mutable, false), - ); - let hygiene = self_param_syn - .name() - .map(|name| self.hygiene_id_for(name.syntax().text_range().start())) - .unwrap_or(HygieneId::ROOT); - if !hygiene.is_root() { - self.body.binding_hygiene.insert(binding_id, hygiene); - } - self_param = Some(binding_id); - self.source_map.self_param = - Some(self.expander.in_file(AstPtr::new(&self_param_syn))); - } + } - for (param, _) in param_list.params().zip(attr_enabled).filter(|(_, enabled)| *enabled) - { - let param_pat = self.collect_pat_top(param.pat()); - params.push(param_pat); - } - }; - let body_expr = self.with_label_rib(RibKind::Closure, |this| { - if is_async_fn { - match body { + fn collect(&mut self, expr: Option<ast::Expr>, awaitable: Awaitable) -> ExprId { + self.awaitable_context.replace(awaitable); + self.with_label_rib(RibKind::Closure, |this| { + if awaitable == Awaitable::Yes { + match expr { Some(e) => { let syntax_ptr = AstPtr::new(&e); let expr = this.collect_expr(e); @@ -313,15 +365,13 @@ impl ExprCollector<'_> { None => this.missing_expr(), } } else { - this.collect_expr_opt(body) + this.collect_expr_opt(expr) } - }); - - (self.body.finish(body_expr, self_param, params.into_boxed_slice()), self.source_map) + }) } fn ctx(&mut self) -> LowerCtx<'_> { - self.expander.ctx(self.db, &mut self.body.types, &mut self.source_map.types) + self.expander.ctx(self.db, &mut self.store.types, &mut self.source_map.types) } fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { @@ -397,7 +447,7 @@ impl ExprCollector<'_> { parent: this.owner, root: inner_expr, }); - this.body.exprs[result_expr_id] = Expr::Const(it); + this.store.exprs[result_expr_id] = Expr::Const(it); this.current_binding_owner = prev_binding_owner; result_expr_id }) @@ -487,7 +537,7 @@ impl ExprCollector<'_> { .unwrap_or((Expr::Missing, HygieneId::ROOT)); let expr_id = self.alloc_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.ident_hygiene.insert(expr_id.into(), hygiene); + self.store.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id } @@ -569,10 +619,12 @@ impl ExprCollector<'_> { ast::Expr::AwaitExpr(e) => { let expr = self.collect_expr_opt(e.expr()); if let Awaitable::No(location) = self.is_lowering_awaitable_block() { - self.source_map.diagnostics.push(BodyDiagnostic::AwaitOutsideOfAsync { - node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)), - location: location.to_string(), - }); + self.source_map.diagnostics.push( + ExpressionStoreDiagnostics::AwaitOutsideOfAsync { + node: InFile::new(self.expander.current_file_id(), AstPtr::new(&e)), + location: location.to_string(), + }, + ); } self.alloc_expr(Expr::Await { expr }, syntax_ptr) } @@ -653,7 +705,7 @@ impl ExprCollector<'_> { this.is_lowering_coroutine = prev_is_lowering_coroutine; this.current_binding_owner = prev_binding_owner; this.current_try_block_label = prev_try_block_label; - this.body.exprs[result_expr_id] = Expr::Closure { + this.store.exprs[result_expr_id] = Expr::Closure { args: args.into(), arg_types: arg_types.into(), ret_type, @@ -759,7 +811,7 @@ impl ExprCollector<'_> { } fn parse_path(&mut self, path: ast::Path) -> Option<Path> { - self.expander.parse_path(self.db, path, &mut self.body.types, &mut self.source_map.types) + self.expander.parse_path(self.db, path, &mut self.store.types, &mut self.source_map.types) } fn collect_expr_path(&mut self, e: ast::PathExpr) -> Option<(Path, HygieneId)> { @@ -788,7 +840,7 @@ impl ExprCollector<'_> { let src = self.expander.in_file(AstPtr::new(&expr).wrap_left()); let expr = self.collect_expr(expr); // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`. - let id = self.body.pats.alloc(Pat::Expr(expr)); + let id = self.store.pats.alloc(Pat::Expr(expr)); self.source_map.pat_map_back.insert(id, src); id }) @@ -842,7 +894,7 @@ impl ExprCollector<'_> { .unwrap_or((Pat::Missing, HygieneId::ROOT)); let pat_id = self.alloc_pat_from_expr(path, syntax_ptr); if !hygiene.is_root() { - self.body.ident_hygiene.insert(pat_id.into(), hygiene); + self.store.ident_hygiene.insert(pat_id.into(), hygiene); } pat_id } @@ -974,7 +1026,7 @@ impl ExprCollector<'_> { ) -> ExprId { let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr); let tmp = job(self); - self.body.exprs[id] = mem::replace(&mut self.body.exprs[tmp], Expr::Missing); + self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing); self.current_binding_owner = prev_owner; id } @@ -986,8 +1038,9 @@ impl ExprCollector<'_> { let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else { return self.collect_block(e); }; - let label = self - .alloc_label_desugared(Label { name: Name::generate_new_name(self.body.labels.len()) }); + let label = self.alloc_label_desugared(Label { + name: Name::generate_new_name(self.store.labels.len()), + }); let old_label = self.current_try_block_label.replace(label); let ptr = AstPtr::new(&e).upcast(); @@ -1013,7 +1066,7 @@ impl ExprCollector<'_> { ) } }; - let Expr::Block { tail, .. } = &mut self.body.exprs[expr_id] else { + let Expr::Block { tail, .. } = &mut self.store.exprs[expr_id] else { unreachable!("block was lowered to non-block"); }; *tail = Some(next_tail); @@ -1119,7 +1172,7 @@ impl ExprCollector<'_> { this.collect_expr_opt(e.loop_body().map(|it| it.into())) }), }; - let iter_name = Name::generate_new_name(self.body.exprs.len()); + let iter_name = Name::generate_new_name(self.store.exprs.len()); let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr); let iter_expr_mut = self.alloc_expr( Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut }, @@ -1184,7 +1237,7 @@ impl ExprCollector<'_> { let try_branch = self.alloc_expr(Expr::Path(try_branch), syntax_ptr); let expr = self .alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr); - let continue_name = Name::generate_new_name(self.body.bindings.len()); + let continue_name = Name::generate_new_name(self.store.bindings.len()); let continue_binding = self.alloc_binding(continue_name.clone(), BindingAnnotation::Unannotated); let continue_bpat = @@ -1199,7 +1252,7 @@ impl ExprCollector<'_> { guard: None, expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr), }; - let break_name = Name::generate_new_name(self.body.bindings.len()); + let break_name = Name::generate_new_name(self.store.bindings.len()); let break_binding = self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated); let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None }); self.add_definition_to_binding(break_binding, break_bpat); @@ -1268,17 +1321,19 @@ impl ExprCollector<'_> { Ok(res) => res, Err(UnresolvedMacro { path }) => { if record_diagnostics { - self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall { - node: InFile::new(outer_file, syntax_ptr), - path, - }); + self.source_map.diagnostics.push( + ExpressionStoreDiagnostics::UnresolvedMacroCall { + node: InFile::new(outer_file, syntax_ptr), + path, + }, + ); } return collector(self, None); } }; if record_diagnostics { if let Some(err) = res.err { - self.source_map.diagnostics.push(BodyDiagnostic::MacroError { + self.source_map.diagnostics.push(ExpressionStoreDiagnostics::MacroError { node: InFile::new(outer_file, syntax_ptr), err, }); @@ -1471,7 +1526,7 @@ impl ExprCollector<'_> { let (module, def_map) = match block_id.map(|block_id| (self.db.block_def_map(block_id), block_id)) { Some((def_map, block_id)) => { - self.body.block_scopes.push(block_id); + self.store.block_scopes.push(block_id); (def_map.module_id(DefMap::ROOT), def_map) } None => (self.expander.module, self.def_map.clone()), @@ -1628,7 +1683,7 @@ impl ExprCollector<'_> { pats.push(self.collect_pat(rest, binding_list)); for (&id, &is_used) in binding_list.is_used.iter() { if !is_used { - self.body.bindings[id].problems = + self.store.bindings[id].problems = Some(BindingProblems::NotBoundAcrossAll); } } @@ -1832,7 +1887,7 @@ impl ExprCollector<'_> { return Some(()); } - self.source_map.diagnostics.push(BodyDiagnostic::InactiveCode { + self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode { node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())), cfg, opts: self.expander.cfg_options().clone(), @@ -1860,7 +1915,7 @@ impl ExprCollector<'_> { fn resolve_label( &self, lifetime: Option<ast::Lifetime>, - ) -> Result<Option<LabelId>, BodyDiagnostic> { + ) -> Result<Option<LabelId>, ExpressionStoreDiagnostics> { let Some(lifetime) = lifetime else { return Ok(None) }; let (mut hygiene_id, mut hygiene_info) = match &self.current_span_map { None => (HygieneId::ROOT, None), @@ -1884,7 +1939,7 @@ impl ExprCollector<'_> { return if self.is_label_valid_from_rib(rib_idx) { Ok(Some(*id)) } else { - Err(BodyDiagnostic::UnreachableLabel { + Err(ExpressionStoreDiagnostics::UnreachableLabel { name, node: self.expander.in_file(AstPtr::new(&lifetime)), }) @@ -1910,7 +1965,7 @@ impl ExprCollector<'_> { } } - Err(BodyDiagnostic::UndeclaredLabel { + Err(ExpressionStoreDiagnostics::UndeclaredLabel { name, node: self.expander.in_file(AstPtr::new(&lifetime)), }) @@ -1941,7 +1996,7 @@ impl ExprCollector<'_> { f: impl FnOnce(&mut Self) -> T, ) -> T { self.label_ribs.push(LabelRib::new(RibKind::Normal( - self.body.labels[label].name.clone(), + self.store.labels[label].name.clone(), label, hygiene, ))); @@ -2030,7 +2085,7 @@ impl ExprCollector<'_> { ); } if !hygiene.is_root() { - self.body.ident_hygiene.insert(expr_id.into(), hygiene); + self.store.ident_hygiene.insert(expr_id.into(), hygiene); } expr_id }, @@ -2424,7 +2479,7 @@ fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> impl ExprCollector<'_> { fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.in_file(ptr); - let id = self.body.exprs.alloc(expr); + let id = self.store.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src); self.source_map.expr_map.insert(src, id.into()); id @@ -2432,11 +2487,11 @@ impl ExprCollector<'_> { // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed. // Migrate to alloc_expr_desugared_with_ptr and then rename back fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { - self.body.exprs.alloc(expr) + self.store.exprs.alloc(expr) } fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId { let src = self.expander.in_file(ptr); - let id = self.body.exprs.alloc(expr); + let id = self.store.exprs.alloc(expr); self.source_map.expr_map_back.insert(id, src); // We intentionally don't fill this as it could overwrite a non-desugared entry // self.source_map.expr_map.insert(src, id); @@ -2447,45 +2502,45 @@ impl ExprCollector<'_> { } fn alloc_binding(&mut self, name: Name, mode: BindingAnnotation) -> BindingId { - let binding = self.body.bindings.alloc(Binding { name, mode, problems: None }); + let binding = self.store.bindings.alloc(Binding { name, mode, problems: None }); if let Some(owner) = self.current_binding_owner { - self.body.binding_owners.insert(binding, owner); + self.store.binding_owners.insert(binding, owner); } binding } fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId { let src = self.expander.in_file(ptr); - let id = self.body.pats.alloc(pat); + let id = self.store.pats.alloc(pat); self.source_map.expr_map.insert(src, id.into()); self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left)); id } fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { let src = self.expander.in_file(ptr); - let id = self.body.pats.alloc(pat); + let id = self.store.pats.alloc(pat); self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right)); self.source_map.pat_map.insert(src, id); id } // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow. fn alloc_pat_desugared(&mut self, pat: Pat) -> PatId { - self.body.pats.alloc(pat) + self.store.pats.alloc(pat) } fn missing_pat(&mut self) -> PatId { - self.body.pats.alloc(Pat::Missing) + self.store.pats.alloc(Pat::Missing) } fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId { let src = self.expander.in_file(ptr); - let id = self.body.labels.alloc(label); + let id = self.store.labels.alloc(label); self.source_map.label_map_back.insert(id, src); self.source_map.label_map.insert(src, id); id } // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow. fn alloc_label_desugared(&mut self, label: Label) -> LabelId { - self.body.labels.alloc(label) + self.store.labels.alloc(label) } fn is_lowering_awaitable_block(&self) -> &Awaitable { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs similarity index 99% rename from src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs index 68c7173d1e409..ab3d104b27f84 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs @@ -9,7 +9,7 @@ use syntax::{ use tt::TextRange; use crate::{ - body::lower::{ExprCollector, FxIndexSet}, + expr_store::lower::{ExprCollector, FxIndexSet}, hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass}, }; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs similarity index 95% rename from src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 52b91b522a412..6a0b1e5197905 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -60,7 +60,7 @@ pub(super) fn print_body_hir( let mut p = Printer { db, - body, + store: body, buf: header, indent_level: 0, line_format: LineFormat::Newline, @@ -103,14 +103,14 @@ pub(super) fn print_body_hir( pub(super) fn print_expr_hir( db: &dyn DefDatabase, - body: &Body, + store: &ExpressionStore, _owner: DefWithBodyId, expr: ExprId, edition: Edition, ) -> String { let mut p = Printer { db, - body, + store, buf: String::new(), indent_level: 0, line_format: LineFormat::Newline, @@ -122,7 +122,7 @@ pub(super) fn print_expr_hir( pub(super) fn print_pat_hir( db: &dyn DefDatabase, - body: &Body, + store: &ExpressionStore, _owner: DefWithBodyId, pat: PatId, oneline: bool, @@ -130,7 +130,7 @@ pub(super) fn print_pat_hir( ) -> String { let mut p = Printer { db, - body, + store, buf: String::new(), indent_level: 0, line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, @@ -157,7 +157,7 @@ macro_rules! wln { struct Printer<'a> { db: &'a dyn DefDatabase, - body: &'a Body, + store: &'a ExpressionStore, buf: String, indent_level: usize, line_format: LineFormat, @@ -233,7 +233,7 @@ impl Printer<'_> { } fn print_expr(&mut self, expr: ExprId) { - let expr = &self.body[expr]; + let expr = &self.store[expr]; match expr { Expr::Missing => w!(self, "�"), @@ -241,7 +241,7 @@ impl Printer<'_> { Expr::InlineAsm(_) => w!(self, "builtin#asm(_)"), Expr::OffsetOf(offset_of) => { w!(self, "builtin#offset_of("); - self.print_type_ref(offset_of.container, &self.body.types); + self.print_type_ref(offset_of.container, &self.store.types); let edition = self.edition; w!( self, @@ -271,7 +271,7 @@ impl Printer<'_> { } Expr::Loop { body, label } => { if let Some(lbl) = label { - w!(self, "{}: ", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, "{}: ", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } w!(self, "loop "); self.print_expr(*body); @@ -295,7 +295,7 @@ impl Printer<'_> { if let Some(args) = generic_args { w!(self, "::<"); let edition = self.edition; - print_generic_args(self.db, args, &self.body.types, self, edition).unwrap(); + print_generic_args(self.db, args, &self.store.types, self, edition).unwrap(); w!(self, ">"); } w!(self, "("); @@ -330,13 +330,13 @@ impl Printer<'_> { Expr::Continue { label } => { w!(self, "continue"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } } Expr::Break { expr, label } => { w!(self, "break"); if let Some(lbl) = label { - w!(self, " {}", self.body[*lbl].name.display(self.db.upcast(), self.edition)); + w!(self, " {}", self.store[*lbl].name.display(self.db.upcast(), self.edition)); } if let Some(expr) = expr { self.whitespace(); @@ -404,7 +404,7 @@ impl Printer<'_> { Expr::Cast { expr, type_ref } => { self.print_expr(*expr); w!(self, " as "); - self.print_type_ref(*type_ref, &self.body.types); + self.print_type_ref(*type_ref, &self.store.types); } Expr::Ref { expr, rawness, mutability } => { w!(self, "&"); @@ -492,13 +492,13 @@ impl Printer<'_> { self.print_pat(*pat); if let Some(ty) = ty { w!(self, ": "); - self.print_type_ref(*ty, &self.body.types); + self.print_type_ref(*ty, &self.store.types); } } w!(self, "|"); if let Some(ret_ty) = ret_type { w!(self, " -> "); - self.print_type_ref(*ret_ty, &self.body.types); + self.print_type_ref(*ret_ty, &self.store.types); } self.whitespace(); self.print_expr(*body); @@ -534,7 +534,7 @@ impl Printer<'_> { Expr::Literal(lit) => self.print_literal(lit), Expr::Block { id: _, statements, tail, label } => { let label = label.map(|lbl| { - format!("{}: ", self.body[lbl].name.display(self.db.upcast(), self.edition)) + format!("{}: ", self.store[lbl].name.display(self.db.upcast(), self.edition)) }); self.print_block(label.as_deref(), statements, tail); } @@ -581,7 +581,7 @@ impl Printer<'_> { } fn print_pat(&mut self, pat: PatId) { - let pat = &self.body[pat]; + let pat = &self.store[pat]; match pat { Pat::Missing => w!(self, "�"), @@ -623,9 +623,9 @@ impl Printer<'_> { let field_name = arg.name.display(self.db.upcast(), edition).to_string(); let mut same_name = false; - if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] { if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = - &self.body.bindings[*id] + &self.store.bindings[*id] { if name.as_str() == field_name { same_name = true; @@ -734,7 +734,7 @@ impl Printer<'_> { self.print_pat(*pat); if let Some(ty) = type_ref { w!(self, ": "); - self.print_type_ref(*ty, &self.body.types); + self.print_type_ref(*ty, &self.store.types); } if let Some(init) = initializer { w!(self, " = "); @@ -799,11 +799,11 @@ impl Printer<'_> { fn print_path(&mut self, path: &Path) { let edition = self.edition; - print_path(self.db, path, &self.body.types, self, edition).unwrap(); + print_path(self.db, path, &self.store.types, self, edition).unwrap(); } fn print_binding(&mut self, id: BindingId) { - let Binding { name, mode, .. } = &self.body.bindings[id]; + let Binding { name, mode, .. } = &self.store.bindings[id]; let mode = match mode { BindingAnnotation::Unannotated => "", BindingAnnotation::Mutable => "mut ", diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs similarity index 90% rename from src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index 08af470b9654a..859a706177aab 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -4,8 +4,8 @@ use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use triomphe::Arc; use crate::{ - body::{Body, HygieneId}, db::DefDatabase, + expr_store::{Body, ExpressionStore, HygieneId}, hir::{Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement}, BlockId, ConstBlockId, DefWithBodyId, }; @@ -53,7 +53,7 @@ pub struct ScopeData { impl ExprScopes { pub(crate) fn expr_scopes_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<ExprScopes> { let body = db.body(def); - let mut scopes = ExprScopes::new(&body, |const_block| { + let mut scopes = ExprScopes::new_body(&body, |const_block| { db.lookup_intern_anonymous_const(const_block).root }); scopes.shrink_to_fit(); @@ -104,7 +104,7 @@ fn empty_entries(idx: usize) -> IdxRange<ScopeEntry> { } impl ExprScopes { - fn new( + fn new_body( body: &Body, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) -> ExprScopes { @@ -179,28 +179,28 @@ impl ExprScopes { fn add_bindings( &mut self, - body: &Body, + store: &ExpressionStore, scope: ScopeId, binding: BindingId, hygiene: HygieneId, ) { - let Binding { name, .. } = &body.bindings[binding]; + let Binding { name, .. } = &store.bindings[binding]; let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene }); self.scopes[scope].entries = IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry); } - fn add_pat_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) { - let pattern = &body[pat]; + fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) { + let pattern = &store[pat]; if let Pat::Bind { id, .. } = *pattern { - self.add_bindings(body, scope, id, body.binding_hygiene(id)); + self.add_bindings(store, scope, id, store.binding_hygiene(id)); } - pattern.walk_child_pats(|pat| self.add_pat_bindings(body, scope, pat)); + pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat)); } - fn add_params_bindings(&mut self, body: &Body, scope: ScopeId, params: &[PatId]) { - params.iter().for_each(|pat| self.add_pat_bindings(body, scope, *pat)); + fn add_params_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, params: &[PatId]) { + params.iter().for_each(|pat| self.add_pat_bindings(store, scope, *pat)); } fn set_scope(&mut self, node: ExprId, scope: ScopeId) { @@ -218,7 +218,7 @@ impl ExprScopes { fn compute_block_scopes( statements: &[Statement], tail: Option<ExprId>, - body: &Body, + store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, @@ -227,17 +227,17 @@ fn compute_block_scopes( match stmt { Statement::Let { pat, initializer, else_branch, .. } => { if let Some(expr) = initializer { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } if let Some(expr) = else_branch { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, *scope, *pat); + scopes.add_pat_bindings(store, *scope, *pat); } Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(*expr, store, scopes, scope, resolve_const_block); } Statement::Item(Item::MacroDef(macro_id)) => { *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); @@ -246,32 +246,32 @@ fn compute_block_scopes( } } if let Some(expr) = tail { - compute_expr_scopes(expr, body, scopes, scope, resolve_const_block); + compute_expr_scopes(expr, store, scopes, scope, resolve_const_block); } } fn compute_expr_scopes( expr: ExprId, - body: &Body, + store: &ExpressionStore, scopes: &mut ExprScopes, scope: &mut ScopeId, resolve_const_block: impl (Fn(ConstBlockId) -> ExprId) + Copy, ) { let make_label = - |label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone())); + |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone())); let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| { - compute_expr_scopes(expr, body, scopes, scope, resolve_const_block) + compute_expr_scopes(expr, store, scopes, scope, resolve_const_block) }; scopes.set_scope(expr, *scope); - match &body[expr] { + match &store[expr] { Expr::Block { statements, tail, id, label } => { let mut scope = scopes.new_block_scope(*scope, *id, make_label(label)); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); + compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block); } Expr::Const(id) => { let mut scope = scopes.root_scope(); @@ -282,7 +282,7 @@ fn compute_expr_scopes( // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). scopes.set_scope(expr, scope); - compute_block_scopes(statements, *tail, body, scopes, &mut scope, resolve_const_block); + compute_block_scopes(statements, *tail, store, scopes, &mut scope, resolve_const_block); } Expr::Loop { body: body_expr, label } => { let mut scope = scopes.new_labeled_scope(*scope, make_label(label)); @@ -290,14 +290,14 @@ fn compute_expr_scopes( } Expr::Closure { args, body: body_expr, .. } => { let mut scope = scopes.new_scope(*scope); - scopes.add_params_bindings(body, scope, args); + scopes.add_params_bindings(store, scope, args); compute_expr_scopes(scopes, *body_expr, &mut scope); } Expr::Match { expr, arms } => { compute_expr_scopes(scopes, *expr, scope); for arm in arms.iter() { let mut scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, scope, arm.pat); + scopes.add_pat_bindings(store, scope, arm.pat); if let Some(guard) = arm.guard { scope = scopes.new_scope(scope); compute_expr_scopes(scopes, guard, &mut scope); @@ -316,9 +316,9 @@ fn compute_expr_scopes( &Expr::Let { pat, expr } => { compute_expr_scopes(scopes, expr, scope); *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(body, *scope, pat); + scopes.add_pat_bindings(store, *scope, pat); } - _ => body.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)), + _ => store.walk_child_exprs(expr, |e| compute_expr_scopes(scopes, e, scope)), }; } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs similarity index 99% rename from src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs index edc7c4c1f211b..9bf1ddb479380 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests.rs @@ -1,6 +1,7 @@ mod block; use expect_test::{expect, Expect}; +use la_arena::RawIdx; use test_fixture::WithFixture; use crate::{test_db::TestDB, ModuleDefId}; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs similarity index 100% rename from src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs rename to src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/block.rs 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 c78818c642ceb..6700350c82e92 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -42,7 +42,7 @@ pub mod lang_item; pub mod hir; pub use self::hir::type_ref; -pub mod body; +pub mod expr_store; pub mod resolver; pub mod nameres; @@ -922,14 +922,14 @@ impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub enum GenericDefId { - FunctionId(FunctionId), AdtId(AdtId), - TraitId(TraitId), - TraitAliasId(TraitAliasId), - TypeAliasId(TypeAliasId), - ImplId(ImplId), // consts can have type parameters from their parents (i.e. associated consts of traits) ConstId(ConstId), + FunctionId(FunctionId), + ImplId(ImplId), + TraitAliasId(TraitAliasId), + TraitId(TraitId), + TypeAliasId(TypeAliasId), } impl_from!( FunctionId, 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 8c556d8a8c3f4..d907989c24ad4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -10,13 +10,13 @@ use smallvec::{smallvec, SmallVec}; use triomphe::Arc; use crate::{ - body::{ - scope::{ExprScopes, ScopeId}, - HygieneId, - }, builtin_type::BuiltinType, data::ExternCrateDeclData, db::DefDatabase, + expr_store::{ + scope::{ExprScopes, ScopeId}, + HygieneId, + }, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, 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 142766c039b5c..836a236444cd2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -3,7 +3,7 @@ use base_db::{ra_salsa::Cycle, CrateId}; use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ - body::{Body, HygieneId}, + expr_store::{Body, HygieneId}, hir::{Expr, ExprId}, path::Path, resolver::{Resolver, ValueNs}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 7f9f0c0de197b..0b5f1319243f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -36,7 +36,7 @@ use crate::{ }; pub(crate) use hir_def::{ - body::Body, + expr_store::Body, hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement}, LocalFieldId, VariantId, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index c5d8c9566155d..b0f9fc53e29ee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -11,7 +11,8 @@ pub(crate) mod pat_analysis; use chalk_ir::Mutability; use hir_def::{ - body::Body, data::adt::VariantData, hir::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId, + data::adt::VariantData, expr_store::Body, hir::PatId, AdtId, EnumVariantId, LocalFieldId, + VariantId, }; use hir_expand::name::Name; use span::Edition; 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 6bba83fac9886..e9d3797bc865c 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 @@ -5,7 +5,7 @@ use std::mem; use either::Either; use hir_def::{ - body::Body, + expr_store::Body, hir::{Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp}, path::Path, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, 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 25bb3a76de2c5..3c258e3c4cf4b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -34,9 +34,9 @@ use chalk_ir::{ }; use either::Either; use hir_def::{ - body::{Body, HygieneId}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, + expr_store::{Body, HygieneId}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::{LangItem, LangItemTarget}, layout::Integer, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 032dc37899dea..b85378531ad65 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -5,7 +5,7 @@ use std::cell::RefCell; use std::ops::{Deref, DerefMut}; -use hir_def::body::HygieneId; +use hir_def::expr_store::HygieneId; use hir_def::hir::ExprOrPatId; use hir_def::path::{Path, PathSegment, PathSegments}; use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs}; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 00398f019da52..ca8d5bae5e504 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -3,7 +3,7 @@ use std::iter::repeat_with; use hir_def::{ - body::Body, + expr_store::Body, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, path::Path, }; @@ -528,7 +528,7 @@ impl InferenceContext<'_> { self.infer_expr(expr, &Expectation::has_type(expected.clone()), ExprIsRead::Yes) } - fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool { + fn is_non_ref_pat(&mut self, body: &hir_def::expr_store::Body, pat: PatId) -> bool { match &body[pat] { Pat::Tuple { .. } | Pat::TupleStruct { .. } 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 432b8f4d94eac..0a8b35fecc389 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -23,10 +23,10 @@ use chalk_ir::{ use either::Either; use hir_def::{ - body::HygieneId, builtin_type::BuiltinType, data::{adt::StructKind, TraitFlags}, expander::Expander, + expr_store::HygieneId, generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 59c583afb2a85..84d8950b1aa2f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -16,7 +16,7 @@ use base_db::CrateId; use chalk_ir::Mutability; use either::Either; use hir_def::{ - body::Body, + expr_store::Body, hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId}, DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index dcae6877ba807..8e4c4db102064 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -6,9 +6,9 @@ use base_db::CrateId; use chalk_ir::{cast::Cast, Mutability}; use either::Either; use hir_def::{ - body::HygieneId, builtin_type::BuiltinType, data::adt::{StructFlags, VariantData}, + expr_store::HygieneId, lang_item::LangItem, layout::{TagEncoding, Variants}, resolver::{HasResolver, TypeNs, ValueNs}, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 1d1044df6e964..cc6ed122af4bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -5,8 +5,8 @@ use std::{fmt::Write, iter, mem}; use base_db::ra_salsa::Cycle; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ - body::{Body, HygieneId}, data::adt::{StructKind, VariantData}, + expr_store::{Body, HygieneId}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 06765a104cbb4..2a26101ac439f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -6,7 +6,7 @@ use std::{ }; use either::Either; -use hir_def::{body::Body, hir::BindingId}; +use hir_def::{expr_store::Body, hir::BindingId}; use hir_expand::{name::Name, Lookup}; use la_arena::ArenaMap; use span::Edition; 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 00da9b251768e..69ec35f406df4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -18,8 +18,8 @@ use std::sync::LazyLock; use base_db::SourceDatabaseFileInputExt as _; use expect_test::Expect; use hir_def::{ - body::{Body, BodySourceMap}, db::DefDatabase, + expr_store::{Body, BodySourceMap}, hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index fc77d1889c88e..066a322e32ba7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -411,7 +411,7 @@ impl AnyDiagnostic { pub(crate) fn body_validation_diagnostic( db: &dyn HirDatabase, diagnostic: BodyValidationDiagnostic, - source_map: &hir_def::body::BodySourceMap, + source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic> { match diagnostic { BodyValidationDiagnostic::RecordMissingFields { record, variant, missed_fields } => { @@ -547,7 +547,7 @@ impl AnyDiagnostic { def: DefWithBodyId, d: &InferenceDiagnostic, outer_types_source_map: &TypesSourceMap, - source_map: &hir_def::body::BodySourceMap, + source_map: &hir_def::expr_store::BodySourceMap, ) -> Option<AnyDiagnostic> { let expr_syntax = |expr| { source_map.expr_syntax(expr).inspect_err(|_| stdx::never!("synthetic syntax")).ok() diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0cbc75726bf39..60ce8d44452ec 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -42,8 +42,8 @@ use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin}; use either::Either; use hir_def::{ - body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, + expr_store::ExpressionStoreDiagnostics, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, @@ -1892,10 +1892,10 @@ impl DefWithBody { for diag in source_map.diagnostics() { acc.push(match diag { - BodyDiagnostic::InactiveCode { node, cfg, opts } => { + ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => { InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() } - BodyDiagnostic::MacroError { node, err } => { + ExpressionStoreDiagnostics::MacroError { node, err } => { let RenderedExpandError { message, error, kind } = err.render_to_string(db.upcast()); @@ -1919,20 +1919,22 @@ impl DefWithBody { } .into() } - BodyDiagnostic::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { - macro_call: (*node).map(|ast_ptr| ast_ptr.into()), - precise_location: None, - path: path.clone(), - is_bang: true, + ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => { + UnresolvedMacroCall { + macro_call: (*node).map(|ast_ptr| ast_ptr.into()), + precise_location: None, + path: path.clone(), + is_bang: true, + } + .into() } - .into(), - BodyDiagnostic::AwaitOutsideOfAsync { node, location } => { + ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => { AwaitOutsideOfAsync { node: *node, location: location.clone() }.into() } - BodyDiagnostic::UnreachableLabel { node, name } => { + ExpressionStoreDiagnostics::UnreachableLabel { node, name } => { UnreachableLabel { node: *node, name: name.clone() }.into() } - BodyDiagnostic::UndeclaredLabel { node, name } => { + ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { UndeclaredLabel { node: *node, name: name.clone() }.into() } }); 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 b699ccde4128e..496b6566bd1f4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -14,7 +14,7 @@ use crate::{ }; use either::Either; use hir_def::{ - body::{ + expr_store::{ scope::{ExprScopes, ScopeId}, Body, BodySourceMap, HygieneId, }, 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 18c27c844964b..cd709afe091f3 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 @@ -13,7 +13,7 @@ use hir::{ ModuleDef, Name, }; use hir_def::{ - body::BodySourceMap, + expr_store::BodySourceMap, hir::{ExprId, PatId}, SyntheticSyntax, }; From bdebd866fb0469c1955cd9ec2fb0e7094d0a37d4 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sun, 26 Jan 2025 11:01:58 +0100 Subject: [PATCH 07/75] Add GenericDefId::StaticId --- .../crates/hir-def/src/generics.rs | 2 +- .../rust-analyzer/crates/hir-def/src/lib.rs | 25 +++++++++++++------ .../crates/hir-def/src/resolver.rs | 1 + .../crates/hir-ty/src/generics.rs | 3 ++- .../crates/hir-ty/src/infer/path.rs | 9 +++---- .../rust-analyzer/crates/hir-ty/src/lower.rs | 14 +++++------ .../crates/hir-ty/src/variance.rs | 1 + .../rust-analyzer/crates/hir/src/from_id.rs | 2 ++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 6 ++++- .../rust-analyzer/crates/ide-db/src/defs.rs | 1 + .../rust-analyzer/crates/ide-db/src/search.rs | 1 + .../crates/ide/src/hover/render.rs | 1 + .../crates/ide/src/signature_help.rs | 4 ++- 13 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs index 7b3f1d06d21b6..e2b36da79b232 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/generics.rs @@ -433,7 +433,7 @@ impl GenericParams { GenericDefId::TraitAliasId(id) => id_to_generics(db, id, enabled_params), GenericDefId::TypeAliasId(id) => id_to_generics(db, id, enabled_params), GenericDefId::ImplId(id) => id_to_generics(db, id, enabled_params), - GenericDefId::ConstId(_) => ( + GenericDefId::ConstId(_) | GenericDefId::StaticId(_) => ( Arc::new(GenericParams { type_or_consts: Default::default(), lifetimes: Default::default(), 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 6700350c82e92..c8efd9043203b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -693,6 +693,7 @@ impl TypeOwnerId { Some(match self { TypeOwnerId::FunctionId(it) => GenericDefId::FunctionId(it), TypeOwnerId::ConstId(it) => GenericDefId::ConstId(it), + TypeOwnerId::StaticId(it) => GenericDefId::StaticId(it), TypeOwnerId::AdtId(it) => GenericDefId::AdtId(it), TypeOwnerId::TraitId(it) => GenericDefId::TraitId(it), TypeOwnerId::TraitAliasId(it) => GenericDefId::TraitAliasId(it), @@ -701,7 +702,7 @@ impl TypeOwnerId { TypeOwnerId::EnumVariantId(it) => { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } - TypeOwnerId::InTypeConstId(_) | TypeOwnerId::StaticId(_) => return None, + TypeOwnerId::InTypeConstId(_) => return None, }) } } @@ -743,6 +744,7 @@ impl From<GenericDefId> for TypeOwnerId { GenericDefId::TypeAliasId(it) => it.into(), GenericDefId::ImplId(it) => it.into(), GenericDefId::ConstId(it) => it.into(), + GenericDefId::StaticId(it) => it.into(), } } } @@ -851,7 +853,7 @@ impl GeneralConstId { pub fn generic_def(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { GeneralConstId::ConstId(it) => Some(it.into()), - GeneralConstId::StaticId(_) => None, + GeneralConstId::StaticId(it) => Some(it.into()), GeneralConstId::ConstBlockId(it) => it.lookup(db).parent.as_generic_def_id(db), GeneralConstId::InTypeConstId(it) => it.lookup(db).owner.as_generic_def_id(db), } @@ -897,7 +899,7 @@ impl DefWithBodyId { pub fn as_generic_def_id(self, db: &dyn DefDatabase) -> Option<GenericDefId> { match self { DefWithBodyId::FunctionId(f) => Some(f.into()), - DefWithBodyId::StaticId(_) => None, + DefWithBodyId::StaticId(s) => Some(s.into()), DefWithBodyId::ConstId(c) => Some(c.into()), DefWithBodyId::VariantId(c) => Some(c.lookup(db).parent.into()), // FIXME: stable rust doesn't allow generics in constants, but we should @@ -927,18 +929,23 @@ pub enum GenericDefId { ConstId(ConstId), FunctionId(FunctionId), ImplId(ImplId), + // can't actually have generics currently, but they might in the future + // More importantly, this completes the set of items that contain type references + // which is to be used by the signature expression store in the future. + StaticId(StaticId), TraitAliasId(TraitAliasId), TraitId(TraitId), TypeAliasId(TypeAliasId), } impl_from!( - FunctionId, AdtId(StructId, EnumId, UnionId), - TraitId, - TraitAliasId, - TypeAliasId, + ConstId, + FunctionId, ImplId, - ConstId + StaticId, + TraitAliasId, + TraitId, + TypeAliasId for GenericDefId ); @@ -969,6 +976,7 @@ impl GenericDefId { GenericDefId::TraitAliasId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ImplId(it) => file_id_and_params_of_item_loc(db, it), GenericDefId::ConstId(it) => (it.lookup(db).id.file_id(), None), + GenericDefId::StaticId(it) => (it.lookup(db).id.file_id(), None), } } @@ -1350,6 +1358,7 @@ impl HasModule for GenericDefId { GenericDefId::TypeAliasId(it) => it.module(db), GenericDefId::ImplId(it) => it.module(db), GenericDefId::ConstId(it) => it.module(db), + GenericDefId::StaticId(it) => it.module(db), } } } 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 d907989c24ad4..7e13ae2f7a1be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1264,6 +1264,7 @@ impl HasResolver for GenericDefId { GenericDefId::TypeAliasId(inner) => inner.resolver(db), GenericDefId::ImplId(inner) => inner.resolver(db), GenericDefId::ConstId(inner) => inner.resolver(db), + GenericDefId::StaticId(inner) => inner.resolver(db), } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index abbf2a4f2efd4..18cf6e5ce36ef 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -262,7 +262,8 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic GenericDefId::FunctionId(it) => it.lookup(db).container, GenericDefId::TypeAliasId(it) => it.lookup(db).container, GenericDefId::ConstId(it) => it.lookup(db).container, - GenericDefId::AdtId(_) + GenericDefId::StaticId(_) + | GenericDefId::AdtId(_) | GenericDefId::TraitId(_) | GenericDefId::ImplId(_) | GenericDefId::TraitAliasId(_) => return None, 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 73bcefaf2a9d6..36ec60a7a2f69 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 @@ -86,10 +86,9 @@ impl InferenceContext<'_> { } }; - let generic_def_id = value_def.to_generic_def_id(self.db); - let Some(generic_def) = generic_def_id else { - // `value_def` is the kind of item that can never be generic (i.e. statics, at least - // currently). We can just skip the binders to get its type. + let generic_def = value_def.to_generic_def_id(self.db); + if let GenericDefId::StaticId(_) = generic_def { + // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. let (ty, binders) = self.db.value_ty(value_def)?.into_value_and_skipped_binders(); stdx::always!(binders.is_empty(Interner), "non-empty binders for non-generic def",); return Some(ValuePathResolution::NonGeneric(ty)); @@ -122,7 +121,7 @@ impl InferenceContext<'_> { } let parent_substs = self_subst.or_else(|| { - let generics = generics(self.db.upcast(), generic_def_id?); + let generics = generics(self.db.upcast(), generic_def); let parent_params_len = generics.parent_generics()?.len(); let parent_args = &substs[substs.len() - parent_params_len..]; Some(Substitution::from_iter(Interner, parent_args)) 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 0a8b35fecc389..db13e1fd35437 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -2471,14 +2471,14 @@ pub enum ValueTyDefId { impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); impl ValueTyDefId { - pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> Option<GenericDefId> { + pub(crate) fn to_generic_def_id(self, db: &dyn HirDatabase) -> 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.lookup(db.upcast()).parent.into()), - Self::ConstId(id) => Some(id.into()), - Self::StaticId(_) => None, + Self::FunctionId(id) => id.into(), + Self::StructId(id) => id.into(), + Self::UnionId(id) => id.into(), + Self::EnumVariantId(var) => var.lookup(db.upcast()).parent.into(), + Self::ConstId(id) => id.into(), + Self::StaticId(id) => id.into(), } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index afd163fbd96c9..3a22158ce6f1d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -1028,6 +1028,7 @@ struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V); } GenericDefId::ImplId(_) => return None, GenericDefId::ConstId(_) => return None, + GenericDefId::StaticId(_) => return None, }, )) }) 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 2ad39817b2fe2..537401afdc34a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -183,6 +183,7 @@ impl From<GenericDef> for GenericDefId { GenericDef::TypeAlias(it) => GenericDefId::TypeAliasId(it.id), GenericDef::Impl(it) => GenericDefId::ImplId(it.id), GenericDef::Const(it) => GenericDefId::ConstId(it.id), + GenericDef::Static(it) => GenericDefId::StaticId(it.id), } } } @@ -197,6 +198,7 @@ impl From<GenericDefId> for GenericDef { GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()), GenericDefId::ImplId(it) => GenericDef::Impl(it.into()), GenericDefId::ConstId(it) => GenericDef::Const(it.into()), + GenericDefId::StaticId(it) => GenericDef::Static(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 60ce8d44452ec..aceff2b9ae4c0 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -3455,6 +3455,7 @@ pub enum GenericDef { Impl(Impl), // consts can have type parameters from their parents (i.e. associated consts of traits) Const(Const), + Static(Static), } impl_from!( Function, @@ -3463,7 +3464,8 @@ impl_from!( TraitAlias, TypeAlias, Impl, - Const + Const, + Static for GenericDef ); @@ -3513,6 +3515,7 @@ impl GenericDef { GenericDef::TypeAlias(it) => it.id.into(), GenericDef::Impl(it) => it.id.into(), GenericDef::Const(it) => it.id.into(), + GenericDef::Static(it) => it.id.into(), } } @@ -3570,6 +3573,7 @@ impl GenericDef { item_tree_source_maps.impl_(id.value).generics() } GenericDefId::ConstId(_) => return, + GenericDefId::StaticId(_) => return, }, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index d12bda0816fd3..bad536080567f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -986,6 +986,7 @@ impl From<GenericDef> for Definition { GenericDef::TypeAlias(it) => it.into(), GenericDef::Impl(it) => it.into(), GenericDef::Const(it) => it.into(), + GenericDef::Static(it) => it.into(), } } } 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 7fc563a424104..7963e8ae4f78c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -354,6 +354,7 @@ impl Definition { hir::GenericDef::TypeAlias(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Impl(it) => it.source(db).map(|src| src.syntax().cloned()), hir::GenericDef::Const(it) => it.source(db).map(|src| src.syntax().cloned()), + hir::GenericDef::Static(it) => it.source(db).map(|src| src.syntax().cloned()), }; return match def { Some(def) => SearchScope::file_range( 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 40f3406b72d37..c996230c3a1ff 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -434,6 +434,7 @@ fn definition_owner_name(db: &RootDatabase, def: Definition, edition: Edition) - None => it.name(db), } } + hir::GenericDef::Static(it) => Some(it.name(db)), }, Definition::DeriveHelper(derive_helper) => Some(derive_helper.derive().name(db)), d => { diff --git a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs index f8c60418eb014..f99721160041c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/signature_help.rs +++ b/src/tools/rust-analyzer/crates/ide/src/signature_help.rs @@ -321,7 +321,9 @@ fn signature_help_for_generics( format_to!(res.signature, "type {}", it.name(db).display(db, edition)); } // These don't have generic args that can be specified - hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) => return None, + hir::GenericDef::Impl(_) | hir::GenericDef::Const(_) | hir::GenericDef::Static(_) => { + return None + } } let params = generics_def.params(sema.db); From 1360b4c3a1d052425276dacf38fc413cee25e4cd Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Sun, 26 Jan 2025 19:06:29 +0200 Subject: [PATCH 08/75] In completion's expand, consider recursion stop condition (when we're not inside a macro call anymore) *after* the recursive call instead of before it This is because our detection is imperfect, and miss some cases such as an impersonating `test` macro, so we hope we'll expand successfully in this case. --- .../ide-completion/src/context/analysis.rs | 41 ++++++++++++--- .../ide-completion/src/tests/expression.rs | 50 +++++++++++++++++++ 2 files changed, 83 insertions(+), 8 deletions(-) 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 f5a50ae81907f..eecd412bc43e3 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 @@ -59,7 +59,7 @@ pub(super) fn expand_and_analyze( // 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( + let expansion = expand_maybe_stop( sema, original_file.clone(), speculative_file.clone(), @@ -118,7 +118,7 @@ fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Opt /// that we check, we subtract `COMPLETION_MARKER.len()`. This may not be accurate because proc macros /// can insert the text of the completion marker in other places while removing the span, but this is /// the best we can do. -fn expand( +fn expand_maybe_stop( sema: &Semantics<'_, RootDatabase>, original_file: SyntaxNode, speculative_file: SyntaxNode, @@ -126,23 +126,48 @@ fn expand( fake_ident_token: SyntaxToken, relative_offset: TextSize, ) -> Option<ExpansionResult> { - let _p = tracing::info_span!("CompletionContext::expand").entered(); + if let result @ Some(_) = expand( + sema, + original_file.clone(), + speculative_file.clone(), + original_offset, + fake_ident_token.clone(), + relative_offset, + ) { + return result; + } + // This needs to come after the recursive call, because our "inside macro" detection is subtly wrong + // with regard to attribute macros named `test` that are not std's test. So hopefully we will expand + // them successfully above and be able to analyze. // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. - return Some(ExpansionResult { + Some(ExpansionResult { original_file, speculative_file, original_offset, speculative_offset: fake_ident_token.text_range().start(), fake_ident_token, derive_ctx: None, - }); + }) + } else { + None } +} + +fn expand( + sema: &Semantics<'_, RootDatabase>, + original_file: SyntaxNode, + speculative_file: SyntaxNode, + original_offset: TextSize, + fake_ident_token: SyntaxToken, + relative_offset: TextSize, +) -> Option<ExpansionResult> { + let _p = tracing::info_span!("CompletionContext::expand").entered(); let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); @@ -197,7 +222,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), @@ -317,7 +342,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), @@ -386,7 +411,7 @@ fn expand( // stop here to prevent problems from happening return None; } - let result = expand( + let result = expand_maybe_stop( sema, actual_expansion.clone(), fake_expansion.clone(), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index 663a038580d55..375575128377d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1986,3 +1986,53 @@ fn foo() { "#]], ); } + +#[test] +fn non_std_test_attr_macro() { + check( + r#" +//- proc_macros: identity +use proc_macros::identity as test; + +#[test] +fn foo() { + $0 +} + "#, + expect![[r#" + fn foo() fn() + md proc_macros + bt u32 u32 + kw async + kw const + kw crate:: + kw enum + kw extern + kw false + kw fn + kw for + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw self:: + kw static + kw struct + kw trait + kw true + kw type + kw union + kw unsafe + kw use + kw while + kw while let + sn macro_rules + sn pd + sn ppd + "#]], + ); +} From 5de2cd4b1360a72e57bf8eeb2d5770e2e8e263d0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Sun, 26 Jan 2025 23:31:58 +0200 Subject: [PATCH 09/75] Support RFC 2396 AKA. target_feature 1.1, or non unsafe target_feature. --- .../rust-analyzer/crates/hir-def/src/data.rs | 8 ++++ .../crates/hir-def/src/item_tree.rs | 7 ++- .../hir-ty/src/diagnostics/unsafe_check.rs | 13 ++++-- .../rust-analyzer/crates/hir-ty/src/lib.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 44 ++++++++++++++++++- .../rust-analyzer/crates/hir/src/display.rs | 4 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 7 ++- .../rust-analyzer/crates/hir/src/semantics.rs | 7 +++ .../crates/hir/src/term_search/tactics.rs | 12 +++-- .../crates/ide-completion/src/context.rs | 4 ++ .../ide-completion/src/render/function.rs | 18 ++++---- .../src/handlers/missing_unsafe.rs | 20 +++++++++ .../ide/src/syntax_highlighting/highlight.rs | 8 +++- .../crates/intern/src/symbol/symbols.rs | 2 + 14 files changed, 132 insertions(+), 24 deletions(-) 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 12f5f6ad79abe..a52a2369572e4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -101,6 +101,10 @@ impl FunctionData { flags.remove(FnFlags::HAS_UNSAFE_KW); } + if attrs.by_key(&sym::target_feature).exists() { + flags.insert(FnFlags::HAS_TARGET_FEATURE); + } + Arc::new(FunctionData { name: func.name.clone(), params: func @@ -155,6 +159,10 @@ impl FunctionData { pub fn is_varargs(&self) -> bool { self.flags.contains(FnFlags::IS_VARARGS) } + + pub fn has_target_feature(&self) -> bool { + self.flags.contains(FnFlags::HAS_TARGET_FEATURE) + } } fn parse_rustc_legacy_const_generics(tt: &crate::tt::TopSubtree) -> Box<[u32]> { 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 b5bf2feb82a20..79ee3344d256a 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 @@ -937,7 +937,7 @@ pub struct Param { bitflags::bitflags! { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] - pub(crate) struct FnFlags: u8 { + pub(crate) struct FnFlags: u16 { const HAS_SELF_PARAM = 1 << 0; const HAS_BODY = 1 << 1; const HAS_DEFAULT_KW = 1 << 2; @@ -946,6 +946,11 @@ bitflags::bitflags! { const HAS_UNSAFE_KW = 1 << 5; const IS_VARARGS = 1 << 6; const HAS_SAFE_KW = 1 << 7; + /// The `#[target_feature]` attribute is necessary to check safety (with RFC 2396), + /// but keeping it for all functions will consume a lot of memory when there are + /// only very few functions with it. So we only encode its existence here, and lookup + /// it if needed. + const HAS_TARGET_FEATURE = 1 << 8; } } 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 6bba83fac9886..5b0041d47271c 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 @@ -14,7 +14,8 @@ use hir_def::{ }; use crate::{ - db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind, + db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt, + TyKind, }; /// Returns `(unsafe_exprs, fn_is_unsafe)`. @@ -96,6 +97,7 @@ struct UnsafeVisitor<'a> { inside_assignment: bool, inside_union_destructure: bool, unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + def_target_features: TargetFeatures, } impl<'a> UnsafeVisitor<'a> { @@ -107,6 +109,10 @@ impl<'a> UnsafeVisitor<'a> { unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), ) -> Self { let resolver = def.resolver(db.upcast()); + let def_target_features = match def { + DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), + _ => TargetFeatures::default(), + }; Self { db, infer, @@ -117,6 +123,7 @@ impl<'a> UnsafeVisitor<'a> { inside_assignment: false, inside_union_destructure: false, unsafe_expr_cb, + def_target_features, } } @@ -181,7 +188,7 @@ impl<'a> UnsafeVisitor<'a> { match expr { &Expr::Call { callee, .. } => { if let Some(func) = self.infer[callee].as_fn_def(self.db) { - if is_fn_unsafe_to_call(self.db, func) { + if is_fn_unsafe_to_call(self.db, func, &self.def_target_features) { self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); } } @@ -212,7 +219,7 @@ impl<'a> UnsafeVisitor<'a> { if self .infer .method_resolution(current) - .map(|(func, _)| is_fn_unsafe_to_call(self.db, func)) + .map(|(func, _)| is_fn_unsafe_to_call(self.db, func, &self.def_target_features)) .unwrap_or(false) { self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); 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 3c18ea9281655..37de501e98782 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -100,7 +100,7 @@ pub use mapping::{ }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; -pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call}; +pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures}; pub use variance::Variance; pub use chalk_ir::{ 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 bf7892f69bd38..ffd0b5b5e21ae 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -9,16 +9,18 @@ use chalk_ir::{ DebruijnIndex, }; use hir_def::{ + attr::Attrs, db::DefDatabase, generics::{WherePredicate, WherePredicateTypeTarget}, lang_item::LangItem, resolver::{HasResolver, TypeNs}, + tt, type_ref::{TraitBoundModifier, TypeRef}, EnumId, EnumVariantId, FunctionId, Lookup, OpaqueInternableThing, TraitId, TypeAliasId, TypeOrConstParamId, }; use hir_expand::name::Name; -use intern::sym; +use intern::{sym, Symbol}; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; @@ -264,12 +266,50 @@ impl<'a> ClosureSubst<'a> { } } -pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { +#[derive(Debug, Default)] +pub struct TargetFeatures { + enabled: FxHashSet<Symbol>, +} + +impl TargetFeatures { + pub fn from_attrs(attrs: &Attrs) -> Self { + let enabled = attrs + .by_key(&sym::target_feature) + .tt_values() + .filter_map(|tt| { + match tt.token_trees().flat_tokens() { + [ + tt::TokenTree::Leaf(tt::Leaf::Ident(enable_ident)), + tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '=', .. })), + tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { kind: tt::LitKind::Str, symbol: features, .. })), + ] if enable_ident.sym == sym::enable => Some(features), + _ => None, + } + }) + .flat_map(|features| features.as_str().split(',').map(Symbol::intern)) + .collect(); + Self { enabled } + } +} + +pub fn is_fn_unsafe_to_call( + db: &dyn HirDatabase, + func: FunctionId, + caller_target_features: &TargetFeatures, +) -> bool { let data = db.function_data(func); if data.is_unsafe() { return true; } + if data.has_target_feature() { + // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>. + let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into())); + if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { + return true; + } + } + let loc = func.lookup(db.upcast()); match loc.container { hir_def::ItemContainerId::ExternBlockId(block) => { diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index b29c91694d371..6f40497055857 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -80,7 +80,9 @@ impl HirDisplay for Function { if data.is_async() { f.write_str("async ")?; } - if self.is_unsafe_to_call(db) { + // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe + // (they are conditionally unsafe to call). We probably should show something else. + if self.is_unsafe_to_call(db, None) { f.write_str("unsafe ")?; } if let Some(abi) = &data.abi { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 0cbc75726bf39..407b1b94eda19 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2361,8 +2361,11 @@ impl Function { db.attrs(self.id.into()).is_unstable() } - pub fn is_unsafe_to_call(self, db: &dyn HirDatabase) -> bool { - hir_ty::is_fn_unsafe_to_call(db, self.id) + pub fn is_unsafe_to_call(self, db: &dyn HirDatabase, caller: Option<Function>) -> bool { + let target_features = caller + .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()))) + .unwrap_or_default(); + hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features) } /// Whether this function declaration has a definition. diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 09470bed9cfb4..882a27182f015 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -2040,6 +2040,13 @@ impl SemanticsScope<'_> { Crate { id: self.resolver.krate() } } + pub fn containing_function(&self) -> Option<Function> { + self.resolver.body_owner().and_then(|owner| match owner { + DefWithBodyId::FunctionId(id) => Some(id.into()), + _ => None, + }) + } + pub(crate) fn resolver(&self) -> &Resolver { &self.resolver } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 1b0e6f8bd5b0a..1ff9b6dec9c82 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -365,7 +365,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db) + || it.is_unsafe_to_call(db, None) || it.is_unstable(db) || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() @@ -470,7 +470,10 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) + || it.is_unsafe_to_call(db, None) + || it.is_unstable(db) + { return None; } @@ -658,7 +661,10 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( } // Filter out private and unsafe functions - if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) { + if !it.is_visible_from(db, module) + || it.is_unsafe_to_call(db, None) + || it.is_unstable(db) + { return None; } 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 2f1860cbb59af..7862b258789c4 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -442,6 +442,8 @@ pub(crate) struct CompletionContext<'a> { pub(crate) krate: hir::Crate, /// The module of the `scope`. pub(crate) module: hir::Module, + /// The function where we're completing, if inside a function. + pub(crate) containing_function: Option<hir::Function>, /// Whether nightly toolchain is used. Cached since this is looked up a lot. pub(crate) is_nightly: bool, /// The edition of the current crate @@ -760,6 +762,7 @@ impl<'a> CompletionContext<'a> { let krate = scope.krate(); let module = scope.module(); + let containing_function = scope.containing_function(); let edition = krate.edition(db); let toolchain = db.toolchain_channel(krate.into()); @@ -874,6 +877,7 @@ impl<'a> CompletionContext<'a> { token, krate, module, + containing_function, is_nightly, edition, expected_name, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index c3354902c3b78..4931f8d090242 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -144,7 +144,7 @@ fn render( let detail = if ctx.completion.config.full_function_signatures { detail_full(db, func, ctx.completion.edition) } else { - detail(db, func, ctx.completion.edition) + detail(ctx.completion, func, ctx.completion.edition) }; item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) @@ -307,26 +307,26 @@ fn ref_of_param(ctx: &CompletionContext<'_>, arg: &str, ty: &hir::Type) -> &'sta "" } -fn detail(db: &dyn HirDatabase, func: hir::Function, edition: Edition) -> String { - let mut ret_ty = func.ret_type(db); +fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> String { + let mut ret_ty = func.ret_type(ctx.db); let mut detail = String::new(); - if func.is_const(db) { + if func.is_const(ctx.db) { format_to!(detail, "const "); } - if func.is_async(db) { + if func.is_async(ctx.db) { format_to!(detail, "async "); - if let Some(async_ret) = func.async_ret_type(db) { + if let Some(async_ret) = func.async_ret_type(ctx.db) { ret_ty = async_ret; } } - if func.is_unsafe_to_call(db) { + if func.is_unsafe_to_call(ctx.db, ctx.containing_function) { format_to!(detail, "unsafe "); } - format_to!(detail, "fn({})", params_display(db, func, edition)); + format_to!(detail, "fn({})", params_display(ctx.db, func, edition)); if !ret_ty.is_unit() { - format_to!(detail, " -> {}", ret_ty.display(db, edition)); + format_to!(detail, " -> {}", ret_ty.display(ctx.db, edition)); } detail } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 8117401a5342a..cea8bc5e95904 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -812,4 +812,24 @@ fn main() { "#, ) } + + #[test] + fn target_feature() { + check_diagnostics( + r#" +#[target_feature(enable = "avx")] +fn foo() {} + +#[target_feature(enable = "avx,avx2")] +fn bar() { + foo(); +} + +fn baz() { + foo(); + // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + "#, + ); + } } 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 22a2fe4e9eb26..a0c8b2856839a 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 @@ -427,7 +427,11 @@ pub(super) fn highlight_def( } } - if func.is_unsafe_to_call(db) { + // FIXME: Passing `None` here means not-unsafe functions with `#[target_feature]` will be + // highlighted as unsafe, even when the current target features set is a superset (RFC 2396). + // We probably should consider checking the current function, but I found no easy way to do + // that (also I'm worried about perf). There's also an instance below. + if func.is_unsafe_to_call(db, None) { h |= HlMod::Unsafe; } if func.is_async(db) { @@ -589,7 +593,7 @@ fn highlight_method_call( let mut h = SymbolKind::Method.into(); - if func.is_unsafe_to_call(sema.db) || sema.is_unsafe_method_call(method_call) { + if func.is_unsafe_to_call(sema.db, None) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; } if func.is_async(sema.db) { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 9bc78ff87b8aa..1b543ddf8110e 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -457,6 +457,8 @@ define_symbols! { system, sysv64, Target, + target_feature, + enable, termination, test_case, test, From d18afd394ce3342d04f020986bab0d82f51fdbdc Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Thu, 23 Jan 2025 00:27:31 +0900 Subject: [PATCH 10/75] feat: Implement `default-field-values` --- .../crates/hir-def/src/expr_store/body.rs | 3 + .../crates/hir-def/src/expr_store/lower.rs | 2 + .../crates/hir-def/src/expr_store/pretty.rs | 27 ++++++ .../rust-analyzer/crates/hir-def/src/lib.rs | 59 ++++++++++++- .../builtin_derive_macro.rs | 42 +++++++++ .../crates/hir-def/src/resolver.rs | 10 +++ .../hir-expand/src/builtin/derive_macro.rs | 86 +++++++++++++++++-- .../hir-ty/src/diagnostics/unsafe_check.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 16 ++++ .../crates/hir-ty/src/mir/lower.rs | 4 + .../crates/hir-ty/src/mir/pretty.rs | 34 +++++++- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 + .../rust-analyzer/crates/hir/src/from_id.rs | 2 + src/tools/rust-analyzer/crates/hir/src/lib.rs | 64 +++++++++++++- .../rust-analyzer/crates/ide-db/src/defs.rs | 1 + .../rust-analyzer/crates/ide-db/src/search.rs | 6 ++ .../src/handlers/type_mismatch.rs | 15 ++++ .../crates/parser/src/grammar/expressions.rs | 18 +++- .../crates/parser/src/grammar/items/adt.rs | 5 ++ .../parser/test_data/generated/runner.rs | 12 +++ .../comma_after_default_values_syntax.rast | 59 +++++++++++++ .../err/comma_after_default_values_syntax.rs | 4 + ...cord_literal_before_ellipsis_recovery.rast | 70 ++++++++++++--- ...record_literal_before_ellipsis_recovery.rs | 2 + .../ok/record_field_default_values.rast | 28 ++++++ .../inline/ok/record_field_default_values.rs | 1 + .../parser/inline/ok/record_lit.rast | 47 ++++++++++ .../test_data/parser/inline/ok/record_lit.rs | 2 + .../ok/struct_initializer_with_defaults.rast | 39 +++++++++ .../ok/struct_initializer_with_defaults.rs | 3 + .../rust-analyzer/src/cli/analysis_stats.rs | 6 ++ .../rust-analyzer/crates/syntax/rust.ungram | 2 +- .../crates/syntax/src/ast/generated/nodes.rs | 4 + 33 files changed, 647 insertions(+), 31 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index a55fec4f8b1e1..3177b2c74afe3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -122,6 +122,9 @@ impl Body { src.map(|it| it.expr()) } DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), + DefWithBodyId::FieldId(f) => { + f.record_field_source(db).map(|it| it.and_then(|it| it.expr())) + } } }; let module = def.module(db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index dfc716eb84944..0d3a542a4393d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -90,6 +90,7 @@ pub(super) fn lower_body( DefWithBodyId::ConstId(it) => db.attrs(it.into()), DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, DefWithBodyId::VariantId(it) => db.attrs(it.into()), + DefWithBodyId::FieldId(it) => db.attrs(it.into()), } .rust_analyzer_tool() .any(|attr| *attr.path() == tool_path![skip]); @@ -168,6 +169,7 @@ pub(super) fn lower_body( Awaitable::No("constant") } DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), + DefWithBodyId::FieldId(..) => Awaitable::No("field"), } }, ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 6a0b1e5197905..6ba0bbd61c489 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -11,6 +11,7 @@ use crate::{ Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, + VariantId, }; use super::*; @@ -56,6 +57,32 @@ pub(super) fn print_body_hir( loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition), ) } + DefWithBodyId::FieldId(it) => { + let parent_name: String = match it.parent { + VariantId::EnumVariantId(it) => { + let loc = it.lookup(db); + let enum_loc = loc.parent.lookup(db); + format!( + "{}::{}", + enum_loc.id.item_tree(db)[enum_loc.id.value] + .name + .display(db.upcast(), edition), + loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition), + ) + } + VariantId::StructId(it) => it + .lookup(db) + .id + .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()), + VariantId::UnionId(it) => it + .lookup(db) + .id + .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()), + }; + let variant_data = it.parent.variant_data(db); + let field_name = &variant_data.fields()[it.local_id].name; + format!("field {}.{}", parent_name, field_name.display(db.upcast(), edition),) + } }; let mut p = Printer { 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 c8efd9043203b..95700b54db23c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -55,6 +55,7 @@ pub mod visibility; use intern::Interned; pub use rustc_abi as layout; +use src::HasSource; use triomphe::Arc; #[cfg(test)] @@ -77,6 +78,7 @@ use hir_expand::{ builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, eager::expand_eager_macro_input, + files::InFileWrapper, impl_intern_lookup, name::Name, proc_macro::{CustomProcMacroExpander, ProcMacroKind}, @@ -519,6 +521,41 @@ pub struct FieldId { pub local_id: LocalFieldId, } +impl FieldId { + pub fn record_field_source( + &self, + db: &dyn DefDatabase, + ) -> InFileWrapper<HirFileId, Option<ast::RecordField>> { + let field_list = match self.parent { + crate::VariantId::EnumVariantId(it) => { + let s = it.lookup(db); + s.source(db).map(|it| { + it.field_list().and_then(|it| match it { + ast::FieldList::RecordFieldList(it) => Some(it), + _ => None, + }) + }) + } + crate::VariantId::StructId(it) => { + let s = it.lookup(db); + s.source(db).map(|it| { + it.field_list().and_then(|it| match it { + ast::FieldList::RecordFieldList(it) => Some(it), + _ => None, + }) + }) + } + crate::VariantId::UnionId(it) => { + let s = it.lookup(db); + s.source(db).map(|it| it.record_field_list()) + } + }; + field_list.map(|it| { + it.and_then(|it| it.fields().nth(self.local_id.into_raw().into_u32() as usize)) + }) + } +} + pub type LocalFieldId = Idx<data::adt::FieldData>; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -686,6 +723,7 @@ pub enum TypeOwnerId { TypeAliasId(TypeAliasId), ImplId(ImplId), EnumVariantId(EnumVariantId), + FieldId(FieldId), } impl TypeOwnerId { @@ -703,6 +741,11 @@ impl TypeOwnerId { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } TypeOwnerId::InTypeConstId(_) => return None, + TypeOwnerId::FieldId(it) => GenericDefId::AdtId(match it.parent { + VariantId::EnumVariantId(it) => AdtId::EnumId(it.lookup(db).parent), + VariantId::StructId(it) => it.into(), + VariantId::UnionId(it) => it.into(), + }), }) } } @@ -717,7 +760,8 @@ impl_from!( TraitAliasId, TypeAliasId, ImplId, - EnumVariantId + EnumVariantId, + FieldId for TypeOwnerId ); @@ -730,6 +774,7 @@ impl From<DefWithBodyId> for TypeOwnerId { DefWithBodyId::ConstId(it) => it.into(), DefWithBodyId::InTypeConstId(it) => it.into(), DefWithBodyId::VariantId(it) => it.into(), + DefWithBodyId::FieldId(it) => it.into(), } } } @@ -885,6 +930,7 @@ pub enum DefWithBodyId { ConstId(ConstId), InTypeConstId(InTypeConstId), VariantId(EnumVariantId), + FieldId(FieldId), } impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId); @@ -905,6 +951,7 @@ impl DefWithBodyId { // FIXME: stable rust doesn't allow generics in constants, but we should // use `TypeOwnerId::as_generic_def_id` when it does. DefWithBodyId::InTypeConstId(_) => None, + DefWithBodyId::FieldId(_) => None, } } } @@ -1332,6 +1379,11 @@ impl HasModule for TypeOwnerId { TypeOwnerId::ImplId(it) => it.module(db), TypeOwnerId::EnumVariantId(it) => it.module(db), TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), + TypeOwnerId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.module(db), + VariantId::StructId(it) => it.module(db), + VariantId::UnionId(it) => it.module(db), + }, } } } @@ -1344,6 +1396,11 @@ impl HasModule for DefWithBodyId { DefWithBodyId::ConstId(it) => it.module(db), DefWithBodyId::VariantId(it) => it.module(db), DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), + DefWithBodyId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.module(db), + VariantId::StructId(it) => it.module(db), + VariantId::UnionId(it) => it.module(db), + }, } } } 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 c31d322132897..25391c910eaf6 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 @@ -211,6 +211,20 @@ enum Bar { #[default] Bar, } +#[derive(Default)] +struct Baz { + field1: i32 = 2, + field2: bool = { false }, +} +#[derive(Default)] +enum Qux { + #[default] + Foo { + field1: i32, + field2: bool = true, + field3: (), + } +} "#, expect![[r#" #[derive(Default)] @@ -224,6 +238,20 @@ enum Bar { #[default] Bar, } +#[derive(Default)] +struct Baz { + field1: i32 = 2, + field2: bool = { false }, +} +#[derive(Default)] +enum Qux { + #[default] + Foo { + field1: i32, + field2: bool = true, + field3: (), + } +} impl <> $crate::default::Default for Foo< > where { fn default() -> Self { @@ -236,6 +264,20 @@ impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } +} +impl <> $crate::default::Default for Baz< > where { + fn default() -> Self { + Baz { + .. + } + } +} +impl <> $crate::default::Default for Qux< > where { + fn default() -> Self { + Qux::Foo { + field1: $crate::default::Default::default(), field3: $crate::default::Default::default(), .. + } + } }"#]], ); } 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 7e13ae2f7a1be..52998942965d1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1227,6 +1227,11 @@ impl HasResolver for TypeOwnerId { TypeOwnerId::TypeAliasId(it) => it.resolver(db), TypeOwnerId::ImplId(it) => it.resolver(db), TypeOwnerId::EnumVariantId(it) => it.resolver(db), + TypeOwnerId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.resolver(db), + VariantId::StructId(it) => it.resolver(db), + VariantId::UnionId(it) => it.resolver(db), + }, } } } @@ -1239,6 +1244,11 @@ impl HasResolver for DefWithBodyId { DefWithBodyId::StaticId(s) => s.resolver(db), DefWithBodyId::VariantId(v) => v.resolver(db), DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db), + DefWithBodyId::FieldId(f) => match f.parent { + VariantId::EnumVariantId(it) => it.resolver(db), + VariantId::StructId(it) => it.resolver(db), + VariantId::UnionId(it) => it.resolver(db), + }, } } } 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 28b6812139446..f8fb700d55e7b 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 @@ -80,9 +80,15 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> BuiltinDeriveExpander::find_by_name(ident) } +#[derive(Clone, Copy)] +enum HasDefault { + Yes, + No, +} + #[derive(Clone)] enum VariantShape { - Struct(Vec<tt::Ident>), + Struct(Vec<(tt::Ident, HasDefault)>), Tuple(usize), Unit, } @@ -98,7 +104,7 @@ impl VariantShape { fn field_names(&self, span: Span) -> Vec<tt::Ident> { match self { - VariantShape::Struct(s) => s.clone(), + VariantShape::Struct(s) => s.iter().map(|(ident, _)| ident.clone()).collect(), VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(), VariantShape::Unit => vec![], } @@ -112,7 +118,7 @@ impl VariantShape { ) -> tt::TopSubtree { match self { VariantShape::Struct(fields) => { - let fields = fields.iter().map(|it| { + let fields = fields.iter().map(|(it, _)| { let mapped = field_map(it); quote! {span => #it : #mapped , } }); @@ -135,6 +141,63 @@ impl VariantShape { } } + fn default_expand( + &self, + path: tt::TopSubtree, + span: Span, + field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, + ) -> tt::TopSubtree { + match self { + VariantShape::Struct(fields) => { + let contains_default = fields.iter().any(|it| matches!(it.1, HasDefault::Yes)); + let fields = fields + .iter() + .filter_map(|(it, has_default)| match has_default { + HasDefault::Yes => None, + HasDefault::No => Some(it), + }) + .map(|it| { + let mapped = field_map(it); + quote! {span => #it : #mapped , } + }); + if contains_default { + let mut double_dots = + tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span)); + double_dots.push(tt::Leaf::Punct(tt::Punct { + char: '.', + spacing: tt::Spacing::Joint, + span, + })); + double_dots.push(tt::Leaf::Punct(tt::Punct { + char: '.', + spacing: tt::Spacing::Alone, + span, + })); + let double_dots = double_dots.build(); + quote! {span => + #path { ##fields #double_dots } + } + } else { + quote! {span => + #path { ##fields } + } + } + } + &VariantShape::Tuple(n) => { + let fields = tuple_field_iterator(span, n).map(|it| { + let mapped = field_map(&it); + quote! {span => + #mapped , + } + }); + quote! {span => + #path ( ##fields ) + } + } + VariantShape::Unit => path, + } + } + fn from( call_site: Span, tm: &ExpansionSpanMap, @@ -144,8 +207,15 @@ impl VariantShape { None => VariantShape::Unit, Some(FieldList::RecordFieldList(it)) => VariantShape::Struct( it.fields() - .map(|it| it.name()) - .map(|it| name_to_token(call_site, tm, it)) + .map(|it| { + ( + it.name(), + if it.expr().is_some() { HasDefault::Yes } else { HasDefault::No }, + ) + }) + .map(|(it, has_default)| { + name_to_token(call_site, tm, it).map(|ident| (ident, has_default)) + }) .collect::<Result<_, _>>()?, ), Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()), @@ -601,7 +671,7 @@ fn default_expand( let body = match &adt.shape { AdtShape::Struct(fields) => { let name = &adt.name; - fields.as_pattern_map( + fields.default_expand( quote!(span =>#name), span, |_| quote!(span =>#krate::default::Default::default()), @@ -611,7 +681,7 @@ fn default_expand( if let Some(d) = default_variant { let (name, fields) = &variants[*d]; let adt_name = &adt.name; - fields.as_pattern_map( + fields.default_expand( quote!(span =>#adt_name :: #name), span, |_| quote!(span =>#krate::default::Default::default()), @@ -643,7 +713,7 @@ fn debug_expand( expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { - let for_fields = fields.iter().map(|it| { + let for_fields = fields.iter().map(|(it, _)| { let x_string = it.to_string(); quote! {span => .field(#x_string, & #it) 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 99ec1951a37be..03218b4691b86 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 @@ -33,7 +33,8 @@ pub fn missing_unsafe( DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) - | DefWithBodyId::InTypeConstId(_) => false, + | DefWithBodyId::InTypeConstId(_) + | DefWithBodyId::FieldId(_) => false, }; let body = db.body(def); 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 3c258e3c4cf4b..1b2ef2aef3ff1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -134,6 +134,9 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer .unwrap() .0; } + DefWithBodyId::FieldId(f) => { + ctx.collect_field(f); + } } ctx.infer_body(); @@ -910,6 +913,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = return_ty; } + fn collect_field(&mut self, field: FieldId) { + let variant_data = field.parent.variant_data(self.db.upcast()); + let field_data = &variant_data.fields()[field.local_id]; + let types_map = variant_data.types_map(); + let return_ty = + self.make_ty(field_data.type_ref, types_map, InferenceTyDiagnosticSource::Signature); + + // Field default value exprs might be defining usage sites of TAITs. + self.make_tait_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; + } + fn collect_fn(&mut self, func: FunctionId) { let data = self.db.function_data(func); let mut param_tys = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index cc6ed122af4bb..23072011a7b36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2130,6 +2130,10 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi db.enum_variant_data(it).name.display(db.upcast(), edition).to_string() } DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"), + DefWithBodyId::FieldId(it) => it.parent.variant_data(db.upcast()).fields()[it.local_id] + .name + .display(db.upcast(), edition) + .to_string(), }; let _p = tracing::info_span!("mir_body_query", ?detail).entered(); let body = db.body(def); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 2a26101ac439f..193b7bcd97c2a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -6,7 +6,7 @@ use std::{ }; use either::Either; -use hir_def::{expr_store::Body, hir::BindingId}; +use hir_def::{expr_store::Body, hir::BindingId, VariantId}; use hir_expand::{name::Name, Lookup}; use la_arena::ArenaMap; use span::Edition; @@ -79,6 +79,38 @@ impl MirBody { hir_def::DefWithBodyId::InTypeConstId(id) => { w!(this, "in type const {id:?} = "); } + hir_def::DefWithBodyId::FieldId(id) => { + w!(this, "field "); + match id.parent { + VariantId::EnumVariantId(it) => { + let loc = it.lookup(db.upcast()); + let enum_loc = loc.parent.lookup(db.upcast()); + w!( + this, + "{}::{}", + enum_loc.id.item_tree(db.upcast())[enum_loc.id.value] + .name + .display(db.upcast(), Edition::LATEST), + loc.id.item_tree(db.upcast())[loc.id.value] + .name + .display(db.upcast(), Edition::LATEST), + ); + } + VariantId::StructId(id) => { + id.lookup(db.upcast()).id.resolved(db.upcast(), |it| { + w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST)); + }); + } + VariantId::UnionId(id) => { + id.lookup(db.upcast()).id.resolved(db.upcast(), |it| { + w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST)); + }); + } + }; + let variant_data = id.parent.variant_data(db.upcast()); + let field_name = &variant_data.fields()[id.local_id].name; + w!(this, ".{}: _ = ", field_name.display(db.upcast(), Edition::LATEST)); + } }); ctx.result } 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 69ec35f406df4..a5af712b42f28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -160,6 +160,7 @@ fn check_impl( loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), + DefWithBodyId::FieldId(_) => unreachable!(), }); let mut unexpected_type_mismatches = String::new(); for def in defs { @@ -415,6 +416,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), + DefWithBodyId::FieldId(_) => unreachable!(), }); for def in defs { let (body, source_map) = db.body_with_source_map(def); 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 537401afdc34a..dd26e894d7932 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -147,6 +147,7 @@ impl From<DefWithBody> for DefWithBodyId { DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id), DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()), DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id), + DefWithBody::Field(it) => DefWithBodyId::FieldId(it.into()), } } } @@ -159,6 +160,7 @@ impl From<DefWithBodyId> for DefWithBody { DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()), DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()), DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()), + DefWithBodyId::FieldId(it) => DefWithBody::Field(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 27723cbc16a18..8a3aa140474c6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -415,6 +415,19 @@ impl ModuleDef { def.diagnostics(db, &mut acc); } + let fields = match self { + ModuleDef::Adt(Adt::Struct(it)) => Some(it.fields(db)), + ModuleDef::Adt(Adt::Union(it)) => Some(it.fields(db)), + ModuleDef::Variant(it) => Some(it.fields(db)), + _ => None, + }; + if let Some(fields) = fields { + for field in fields { + let def: DefWithBody = field.into(); + def.diagnostics(db, &mut acc, style_lints); + } + } + acc } @@ -1226,6 +1239,12 @@ impl HasVisibility for Module { } } +impl From<&Field> for DefWithBodyId { + fn from(&f: &Field) -> Self { + DefWithBodyId::FieldId(f.into()) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Field { pub(crate) parent: VariantDef, @@ -1291,6 +1310,10 @@ impl AstNode for FieldSource { } impl Field { + pub fn module(self, db: &dyn HirDatabase) -> Module { + self.parent.module(db) + } + pub fn name(&self, db: &dyn HirDatabase) -> Name { self.parent.variant_data(db).fields()[self.id].name.clone() } @@ -1353,6 +1376,14 @@ impl Field { pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { self.parent } + + pub fn default_value_source( + &self, + db: &dyn HirDatabase, + ) -> Option<InFileWrapper<HirFileId, ast::Expr>> { + let id: hir_def::FieldId = (*self).into(); + id.record_field_source(db.upcast()).map(|it| it.and_then(|it| it.expr())).transpose() + } } impl HasVisibility for Field { @@ -1789,8 +1820,9 @@ pub enum DefWithBody { Const(Const), Variant(Variant), InTypeConst(InTypeConst), + Field(Field), } -impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody); +impl_from!(Function, Const, Static, Variant, InTypeConst, Field for DefWithBody); impl DefWithBody { pub fn module(self, db: &dyn HirDatabase) -> Module { @@ -1800,6 +1832,7 @@ impl DefWithBody { DefWithBody::Static(s) => s.module(db), DefWithBody::Variant(v) => v.module(db), DefWithBody::InTypeConst(c) => c.module(db), + DefWithBody::Field(f) => f.module(db), } } @@ -1810,6 +1843,7 @@ impl DefWithBody { DefWithBody::Const(c) => c.name(db), DefWithBody::Variant(v) => Some(v.name(db)), DefWithBody::InTypeConst(_) => None, + DefWithBody::Field(f) => Some(f.name(db)), } } @@ -1825,6 +1859,7 @@ impl DefWithBody { &DefWithBodyId::from(it.id).resolver(db.upcast()), TyKind::Error.intern(Interner), ), + DefWithBody::Field(it) => it.ty(db), } } @@ -1835,6 +1870,7 @@ impl DefWithBody { DefWithBody::Const(it) => it.id.into(), DefWithBody::Variant(it) => it.into(), DefWithBody::InTypeConst(it) => it.id.into(), + DefWithBody::Field(it) => it.into(), } } @@ -1880,6 +1916,23 @@ impl DefWithBody { item_tree_source_maps.konst(konst.value) } DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => &TypesSourceMap::EMPTY, + DefWithBody::Field(field) => match field.parent { + VariantDef::Struct(strukt) => { + let strukt = strukt.id.lookup(db.upcast()).id; + item_tree_source_maps = strukt.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.strukt(strukt.value).item() + } + VariantDef::Union(union) => { + let union = union.id.lookup(db.upcast()).id; + item_tree_source_maps = union.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.union(union.value).item() + } + VariantDef::Variant(variant) => { + let variant = variant.id.lookup(db.upcast()).id; + item_tree_source_maps = variant.item_tree_with_source_map(db.upcast()).1; + item_tree_source_maps.variant(variant.value) + } + }, }; for (_, def_map) in body.blocks(db.upcast()) { @@ -2111,8 +2164,8 @@ impl DefWithBody { DefWithBody::Static(it) => it.into(), DefWithBody::Const(it) => it.into(), DefWithBody::Variant(it) => it.into(), - // FIXME: don't ignore diagnostics for in type const - DefWithBody::InTypeConst(_) => return, + // FIXME: don't ignore diagnostics for in type const and default field value exprs + DefWithBody::InTypeConst(_) | DefWithBody::Field(_) => return, }; for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) { acc.push(diag.into()) @@ -3237,7 +3290,10 @@ impl AsAssocItem for DefWithBody { match self { DefWithBody::Function(it) => it.as_assoc_item(db), DefWithBody::Const(it) => it.as_assoc_item(db), - DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None, + DefWithBody::Static(_) + | DefWithBody::Variant(_) + | DefWithBody::InTypeConst(_) + | DefWithBody::Field(_) => None, } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index bad536080567f..6925880ba996c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -972,6 +972,7 @@ impl TryFrom<DefWithBody> for Definition { DefWithBody::Const(it) => Ok(it.into()), DefWithBody::Variant(it) => Ok(it.into()), DefWithBody::InTypeConst(_) => Err(()), + DefWithBody::Field(it) => Ok(it.into()), } } } 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 7963e8ae4f78c..d2a237a5c003b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -310,6 +310,9 @@ impl Definition { DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), // FIXME: implement DefWithBody::InTypeConst(_) => return SearchScope::empty(), + DefWithBody::Field(f) => { + f.default_value_source(db).map(|src| src.syntax().cloned()) + } }; return match def { Some(def) => SearchScope::file_range( @@ -327,6 +330,9 @@ impl Definition { DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), // FIXME: implement DefWithBody::InTypeConst(_) => return SearchScope::empty(), + DefWithBody::Field(f) => { + f.default_value_source(db).map(|src| src.syntax().cloned()) + } }; return match def { Some(def) => SearchScope::file_range( 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 56afb38cc81af..7bc1be382261f 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 @@ -1232,6 +1232,21 @@ fn f() { let (_, _, _, ..) = (true, 42); // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown}) } +"#, + ); + } + + #[test] + fn diagnostics_inside_field_default_expr() { + check_diagnostics( + r#" +struct Foo { + foo: i32 = { + let x = false; + x + // ^ error: expected i32, found bool + }, +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 389c01933c999..fe1316c9bfde3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -678,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike // S { x }; // S { x, y: 32, }; // S { x, y: 32, ..Default::default() }; +// S { x, y: 32, .. }; +// S { .. }; // S { x: ::default() }; // TupleStruct { 0: 1 }; // } @@ -709,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // fn main() { // S { field ..S::default() } // S { 0 ..S::default() } + // S { field .. } + // S { 0 .. } // } name_ref_or_index(p); p.error("expected `:`"); @@ -739,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { .. } = S {}; // } - // We permit `.. }` on the left-hand side of a destructuring assignment. + // test struct_initializer_with_defaults + // fn foo() { + // let _s = S { .. }; + // } + + // We permit `.. }` on the left-hand side of a destructuring assignment + // or defaults values. if !p.at(T!['}']) { expr(p); @@ -750,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { ..x, a: 0 } // } + // test_err comma_after_default_values_syntax + // fn foo() { + // S { .., }; + // S { .., a: 0 } + // } + // Do not bump, so we can support additional fields after this comma. p.error("cannot use a comma after the base struct"); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index 21078175c0ec0..9a16c9db6daf1 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { name(p); p.expect(T![:]); types::type_(p); + // test record_field_default_values + // struct S { f: f32 = 0.0 } + if p.eat(T![=]) { + expressions::expr(p); + } m.complete(p, RECORD_FIELD); } else { m.abandon(p); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index b9f87b6af2421..c8ea8c547a98b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -482,6 +482,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs"); } #[test] + fn record_field_default_values() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs"); + } + #[test] fn record_field_list() { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs"); } @@ -544,6 +548,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs"); } #[test] + fn struct_initializer_with_defaults() { + run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs"); + } + #[test] fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); } #[test] fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); } @@ -719,6 +727,10 @@ mod err { ); } #[test] + fn comma_after_default_values_syntax() { + run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs"); + } + #[test] fn crate_visibility_empty_recover() { run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast new file mode 100644 index 0000000000000..feb617e1aa2ab --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast @@ -0,0 +1,59 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 21: expected expression +error 36: expected expression +error 37: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs new file mode 100644 index 0000000000000..f1ecdf89fab17 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs @@ -0,0 +1,4 @@ +fn foo() { + S { .., }; + S { .., a: 0 } +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast index 08ae906421c30..12b4e233e3044 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast @@ -44,6 +44,56 @@ SOURCE_FILE WHITESPACE " " R_CURLY "}" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + INT_NUMBER "0" + WHITESPACE " " + DOT2 ".." + CALL_EXPR + PATH_EXPR + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "field" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " RECORD_EXPR PATH PATH_SEGMENT @@ -58,20 +108,6 @@ SOURCE_FILE INT_NUMBER "0" WHITESPACE " " DOT2 ".." - CALL_EXPR - PATH_EXPR - PATH - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "default" - ARG_LIST - L_PAREN "(" - R_PAREN ")" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" @@ -82,3 +118,9 @@ error 25: expected COMMA error 42: expected SEMICOLON error 52: expected `:` error 52: expected COMMA +error 69: expected SEMICOLON +error 83: expected `:` +error 83: expected COMMA +error 88: expected SEMICOLON +error 98: expected `:` +error 98: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs index 65398ccb88e5d..416cd763fdb56 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs @@ -1,4 +1,6 @@ fn main() { S { field ..S::default() } S { 0 ..S::default() } + S { field .. } + S { 0 .. } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast new file mode 100644 index 0000000000000..33088f2cabf39 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast @@ -0,0 +1,28 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_FIELD + NAME + IDENT "f" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "f32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + FLOAT_NUMBER "0.0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs new file mode 100644 index 0000000000000..d7b38944a8aa8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs @@ -0,0 +1 @@ +struct S { f: f32 = 0.0 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast index 00948c322f4c9..b868da55bcea7 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast @@ -120,6 +120,53 @@ SOURCE_FILE R_CURLY "}" SEMICOLON ";" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "y" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "32" + COMMA "," + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " EXPR_STMT RECORD_EXPR PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs index 86411fbb7dc05..42895f759b2be 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs @@ -3,6 +3,8 @@ fn foo() { S { x }; S { x, y: 32, }; S { x, y: 32, ..Default::default() }; + S { x, y: 32, .. }; + S { .. }; S { x: ::default() }; TupleStruct { 0: 1 }; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast new file mode 100644 index 0000000000000..987e219ae822b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast @@ -0,0 +1,39 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "_s" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs new file mode 100644 index 0000000000000..e08204f94c4b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs @@ -0,0 +1,3 @@ +fn foo() { + let _s = S { .. }; +} 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 cd709afe091f3..824f262ca3635 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 @@ -673,6 +673,9 @@ impl flags::AnalysisStats { DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::InTypeConst(_) => unimplemented!(), + DefWithBody::Field(it) => { + it.default_value_source(db).map(|it| it.syntax().cloned()) + } }; if let Some(src) = source { let original_file = src.file_id.original_file(db); @@ -987,6 +990,9 @@ impl flags::AnalysisStats { DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::InTypeConst(_) => unimplemented!(), + DefWithBody::Field(it) => { + it.default_value_source(db).map(|it| it.syntax().cloned()) + } }; if let Some(src) = source { let original_file = src.file_id.original_file(db); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 4e2a70d6cd917..bbb8413cbc080 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -241,7 +241,7 @@ RecordFieldList = RecordField = Attr* Visibility? - Name ':' Type + Name ':' Type ('=' Expr)? TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1b22..8f10ea94645d6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1538,10 +1538,14 @@ impl ast::HasDocComments for RecordField {} impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { + #[inline] + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] From 5ca9f522dd985ac63df6301bb9be391b68929a77 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Fri, 24 Jan 2025 02:54:54 +0900 Subject: [PATCH 11/75] Handle missing fields diagnostics --- .../crates/hir-def/src/data/adt.rs | 2 ++ .../crates/hir-def/src/expr_store/lower.rs | 5 +-- .../crates/hir-def/src/expr_store/pretty.rs | 2 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 1 + .../crates/hir-def/src/item_tree.rs | 1 + .../crates/hir-def/src/item_tree/lower.rs | 5 +-- .../crates/hir-def/src/item_tree/pretty.rs | 8 +++-- .../crates/hir-ty/src/diagnostics/expr.rs | 12 +++++-- .../crates/hir-ty/src/infer/mutability.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../src/handlers/missing_fields.rs | 31 +++++++++++++++++++ 11 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index 8fc19854033cb..aaa260a358b6a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -85,6 +85,7 @@ pub struct FieldData { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibility, + pub has_default: bool, } fn repr_from_value( @@ -478,5 +479,6 @@ fn lower_field( name: field.name.clone(), type_ref: field.type_ref, visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), + has_default: field.has_default, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 0d3a542a4393d..65580bce4dda7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -603,9 +603,10 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - Expr::RecordLit { path, fields, spread } + let ellipsis = nfl.dotdot_token().is_some(); + Expr::RecordLit { path, fields, spread, ellipsis } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None } + Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 6ba0bbd61c489..9a8a8c2cd057e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread } => { + Expr::RecordLit { path, fields, spread, ellipsis: _ } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 0dcddf162b2fa..1e2417ecdf3a8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -252,6 +252,7 @@ pub enum Expr { path: Option<Box<Path>>, fields: Box<[RecordLitField]>, spread: Option<ExprId>, + ellipsis: bool, }, Field { expr: ExprId, 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 79ee3344d256a..09fb5f6fd2d3a 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 @@ -1006,6 +1006,7 @@ pub struct Field { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibilityId, + pub has_default: bool, } #[derive(Debug, Clone, Eq, PartialEq)] 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 71848845a84df..69a1933079cee 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 @@ -319,8 +319,9 @@ impl<'a> Ctx<'a> { }; let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); + let has_default = field.expr().is_some(); - Field { name, type_ref, visibility } + Field { name, type_ref, visibility, has_default } } fn lower_tuple_field( @@ -332,7 +333,7 @@ impl<'a> Ctx<'a> { let name = Name::new_tuple_field(idx); let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); - Field { name, type_ref, visibility } + Field { name, type_ref, visibility, has_default: false } } fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> { 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 70bf2f13c88a1..1e765ac78eb18 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 @@ -135,7 +135,9 @@ impl Printer<'_> { self.whitespace(); w!(self, "{{"); self.indented(|this| { - for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { + for (idx, Field { name, type_ref, visibility, has_default: _ }) in + fields.iter().enumerate() + { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", @@ -151,7 +153,9 @@ impl Printer<'_> { FieldsShape::Tuple => { w!(self, "("); self.indented(|this| { - for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { + for (idx, Field { name, type_ref, visibility, has_default: _ }) in + fields.iter().enumerate() + { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 0b5f1319243f6..d8700e2777749 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -547,8 +547,8 @@ pub fn record_literal_missing_fields( id: ExprId, expr: &Expr, ) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { - let (fields, exhaustive) = match expr { - Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), + let (fields, exhaustive, ellipsis) = match expr { + Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), _ => return None, }; @@ -563,7 +563,13 @@ pub fn record_literal_missing_fields( let missed_fields: Vec<LocalFieldId> = variant_data .fields() .iter() - .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) + .filter_map(|(f, d)| { + if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { + None + } else { + Some(f) + } + }) .collect(); if missed_fields.is_empty() { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index d74a383f44ef4..5b6c3cd152446 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -121,7 +121,7 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread } => { + Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 23072011a7b36..85e8d17203107 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -823,7 +823,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread } => { + Expr::RecordLit { fields, path, spread, ellipsis: _ } => { let spread_place = match spread { &Some(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { 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 938b7182bc946..c495df9daaaf0 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 @@ -846,4 +846,35 @@ pub struct Claims { "#, ); } + + #[test] + fn default_field_values() { + check_diagnostics( + r#" +struct F { + field1: i32 = 4, + field2: bool, +} + +fn f() { + let _f = F { + field2: true, + .. + }; + + let _f = F { + //^ 💡 error: missing structure fields: + //| - field1 + field2: true, + }; + + let _f = F { + //^ 💡 error: missing structure fields: + //| - field2 + .. + }; +} +"#, + ); + } } From 262216079a956988bd6a5280641aae7aa628e578 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Sun, 26 Jan 2025 17:23:23 +0900 Subject: [PATCH 12/75] Merge record lit's ellipsis into pre-existing spread's variant --- .../crates/hir-def/src/expr_store.rs | 6 +++--- .../crates/hir-def/src/expr_store/lower.rs | 12 +++++++----- .../crates/hir-def/src/expr_store/pretty.rs | 18 ++++++++++++------ .../rust-analyzer/crates/hir-def/src/hir.rs | 10 ++++++++-- .../crates/hir-ty/src/diagnostics/expr.rs | 13 ++++++++----- .../crates/hir-ty/src/infer/closure.rs | 4 ++-- .../crates/hir-ty/src/infer/expr.rs | 4 ++-- .../crates/hir-ty/src/infer/mutability.rs | 12 ++++++++---- .../crates/hir-ty/src/mir/lower.rs | 8 ++++---- .../crates/hir/src/source_analyzer.rs | 2 +- 10 files changed, 55 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 9df6eaade757a..d0695d0eac769 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -25,7 +25,7 @@ use crate::{ db::DefDatabase, hir::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, Statement, + PatId, RecordFieldPat, Spread, Statement, }, nameres::DefMap, path::{ModPath, Path}, @@ -362,7 +362,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { f(expr); } } @@ -490,7 +490,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { f(expr); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 65580bce4dda7..3ce00b78a9e82 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -45,7 +45,7 @@ use crate::{ }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Spread, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -602,11 +602,13 @@ impl ExprCollector<'_> { Some(RecordLitField { name, expr }) }) .collect(); - let spread = nfl.spread().map(|s| self.collect_expr(s)); - let ellipsis = nfl.dotdot_token().is_some(); - Expr::RecordLit { path, fields, spread, ellipsis } + let spread = nfl.spread().map(|s| self.collect_expr(s)).map_or_else( + || if nfl.dotdot_token().is_some() { Spread::Yes } else { Spread::No }, + Spread::Base, + ); + Expr::RecordLit { path, fields, spread } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } + Expr::RecordLit { path, fields: Box::default(), spread: Spread::No } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 9a8a8c2cd057e..1b3a1bb4dc009 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -8,7 +8,7 @@ use span::Edition; use crate::{ hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability, - Statement, + Spread, Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, VariantId, @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread, ellipsis: _ } => { + Expr::RecordLit { path, fields, spread } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), @@ -412,10 +412,16 @@ impl Printer<'_> { p.print_expr(field.expr); wln!(p, ","); } - if let Some(spread) = spread { - w!(p, ".."); - p.print_expr(*spread); - wln!(p); + match spread { + Spread::No => {} + Spread::Yes => { + w!(p, ".."); + } + Spread::Base(expr) => { + w!(p, ".."); + p.print_expr(*expr); + wln!(p); + } } }); w!(self, "}}"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 1e2417ecdf3a8..e09ce67a897d1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -251,8 +251,7 @@ pub enum Expr { RecordLit { path: Option<Box<Path>>, fields: Box<[RecordLitField]>, - spread: Option<ExprId>, - ellipsis: bool, + spread: Spread, }, Field { expr: ExprId, @@ -479,6 +478,13 @@ pub struct RecordLitField { pub expr: ExprId, } +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Spread { + No, + Yes, + Base(ExprId), +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Statement { Let { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index d8700e2777749..dd55febbf042d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -8,6 +8,7 @@ use base_db::CrateId; use chalk_solve::rust_ir::AdtKind; use either::Either; use hir_def::{ + hir::Spread, lang_item::LangItem, resolver::{HasResolver, ValueNs}, AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, @@ -546,9 +547,11 @@ pub fn record_literal_missing_fields( infer: &InferenceResult, id: ExprId, expr: &Expr, -) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { - let (fields, exhaustive, ellipsis) = match expr { - Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), +) -> Option<(VariantId, Vec<LocalFieldId>, /*has spread expr*/ bool)> { + let (fields, has_spread_expr, has_ellipsis) = match expr { + Expr::RecordLit { fields, spread, .. } => { + (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) + } _ => return None, }; @@ -564,7 +567,7 @@ pub fn record_literal_missing_fields( .fields() .iter() .filter_map(|(f, d)| { - if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { + if (has_ellipsis && d.has_default) || specified_fields.contains(&d.name) { None } else { Some(f) @@ -574,7 +577,7 @@ pub fn record_literal_missing_fields( if missed_fields.is_empty() { return None; } - Some((variant_def, missed_fields, exhaustive)) + Some((variant_def, missed_fields, has_spread_expr)) } pub fn record_pattern_missing_fields( 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 9283c46d0f611..0e9aed416004d 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,7 +12,7 @@ use hir_def::{ data::adt::VariantData, hir::{ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, - Statement, UnaryOp, + Spread, Statement, UnaryOp, }, lang_item::LangItem, path::Path, @@ -796,7 +796,7 @@ impl InferenceContext<'_> { self.consume_expr(expr); } Expr::RecordLit { fields, spread, .. } => { - if let &Some(expr) = spread { + if let &Spread::Base(expr) = spread { self.consume_expr(expr); } self.consume_exprs(fields.iter().map(|it| it.expr)); 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 b951443897cb0..8b3ec1ff2ad0e 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,7 @@ use either::Either; use hir_def::{ hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId, - LabelId, Literal, Pat, PatId, Statement, UnaryOp, + LabelId, Literal, Pat, PatId, Spread, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -775,7 +775,7 @@ impl InferenceContext<'_> { } } } - if let Some(expr) = spread { + if let Spread::Base(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); } ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 5b6c3cd152446..e95a425498379 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -4,8 +4,8 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ hir::{ - Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, - UnaryOp, + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Spread, + Statement, UnaryOp, }, lang_item::LangItem, }; @@ -121,8 +121,12 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { - self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) + Expr::RecordLit { path: _, fields, spread } => { + let spread_expr = match spread { + Spread::Base(expr) => Some(*expr), + _ => None, + }; + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(spread_expr)) } &Expr::Index { base, index } => { if mutability == Mutability::Mut { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 85e8d17203107..5d89ebd4ef408 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -9,7 +9,7 @@ use hir_def::{ expr_store::{Body, HygieneId}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, - LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, + LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Spread, }, lang_item::{LangItem, LangItemTarget}, path::Path, @@ -823,16 +823,16 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread, ellipsis: _ } => { + Expr::RecordLit { fields, path, spread } => { let spread_place = match spread { - &Some(it) => { + &Spread::Base(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; Some(p) } - None => None, + _ => None, }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { 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 496b6566bd1f4..653c858e58356 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1023,7 +1023,7 @@ impl SourceAnalyzer { let expr_id = self.expr_id(db, &literal.clone().into())?; let substs = infer[expr_id].as_adt()?.1; - let (variant, missing_fields, _exhaustive) = match expr_id { + let (variant, missing_fields, _) = match expr_id { ExprOrPatId::ExprId(expr_id) => { record_literal_missing_fields(db, infer, expr_id, &body[expr_id])? } From faeaf4ad2123558a5356928d4bb6abf43b1f5533 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Sun, 26 Jan 2025 23:33:33 +0900 Subject: [PATCH 13/75] Compute diagnostics of a field body iff it has one --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-ty/src/infer/closure.rs | 5 ++++- .../crates/hir-ty/src/infer/expr.rs | 12 ++++++++---- .../crates/hir-ty/src/mir/lower.rs | 11 ++++++++--- .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++++++++-- .../rust-analyzer/crates/hir/src/display.rs | 4 ++-- .../rust-analyzer/crates/hir/src/from_id.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 15 +++++++++++---- .../hir/src/semantics/child_by_source.rs | 6 +++++- .../crates/hir/src/source_analyzer.rs | 18 +++++++++++++++--- 10 files changed, 64 insertions(+), 22 deletions(-) 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 95700b54db23c..1f06331783df6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -519,6 +519,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, + pub has_default: bool, } impl FieldId { 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 0e9aed416004d..def0cb705d8f5 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 @@ -1167,9 +1167,11 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); + let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, + has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1219,12 +1221,13 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, _)) in it { + for (&arg, (i, d)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, + has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); 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 8b3ec1ff2ad0e..b91f32b09e531 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 @@ -1746,13 +1746,17 @@ impl InferenceContext<'_> { }); } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { - let local_id = self.db.struct_data(*s).variant_data.field(name)?; - let field = FieldId { parent: (*s).into(), local_id }; + let vd = &self.db.struct_data(*s).variant_data; + let local_id = vd.field(name)?; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*s).into(), local_id, has_default }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { - let local_id = self.db.union_data(*u).variant_data.field(name)?; - let field = FieldId { parent: (*u).into(), local_id }; + let vd = &self.db.union_data(*u).variant_data; + let local_id = vd.field(name)?; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*u).into(), local_id, has_default }; (field, parameters.clone()) } _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 5d89ebd4ef408..b254bc8e5495f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -870,12 +870,15 @@ impl<'ctx> MirLowerCtx<'ctx> { .map(|(i, it)| match it { Some(it) => it, None => { + let local_id = + LocalFieldId::from_raw(RawIdx::from(i as u32)); + let has_default = + variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, - local_id: LocalFieldId::from_raw(RawIdx::from( - i as u32, - )), + local_id, + has_default, })), &mut self.result.projection_store, ); @@ -897,10 +900,12 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, + has_default, })), &mut self.result.projection_store, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 2ffea34c85a10..43e6eb88988f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,10 +632,12 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, + has_default, })), x.pat, )) @@ -644,8 +646,12 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, _)| { - PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) + let fields = variant_data.fields().iter().map(|(x, d)| { + PlaceElem::Field(Either::Left(FieldId { + parent: v, + local_id: x, + has_default: d.has_default, + })) }); self.pattern_match_tuple_like( current, diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 6f40497055857..5591af9202703 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; + while let Some((id, d)) = it.next() { + let field = Field { parent: (*self).into(), id, has_default: d.has_default }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { 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 dd26e894d7932..5c12cf21da3ce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From<VariantDef> for VariantId { impl From<Field> for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id } + FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } } } impl From<FieldId> for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id } + Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 8a3aa140474c6..4d8b1f58f63fc 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -423,6 +423,9 @@ impl ModuleDef { }; if let Some(fields) = fields { for field in fields { + if !field.has_default { + continue; + } let def: DefWithBody = field.into(); def.diagnostics(db, &mut acc, style_lints); } @@ -1249,6 +1252,7 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, + pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1414,7 +1418,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1476,7 +1480,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1606,7 +1610,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -5162,10 +5166,13 @@ impl Type { _ => return Vec::new(), }; + let var_data = db.variant_data(variant_id); + let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let def = Field { parent: variant_id.into(), id: local_id }; + let has_default = fields[local_id].has_default; + let def = Field { parent: variant_id.into(), id: local_id, has_default }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index d5dfb98571864..ef10a4e148cda 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -160,7 +160,11 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let id = FieldId { parent, local_id }; + let has_default = match source { + Either::Left(_) => false, + Either::Right(rec) => rec.expr().is_some(), + }; + let id = FieldId { parent, local_id, has_default }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), 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 653c858e58356..65cf572dea4fa 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -630,7 +630,10 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); - let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; + let fields = variant_data.fields(); + let local_id = variant_data.field(&local_name)?; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -651,7 +654,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); - let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; + let fields = variant_data.fields(); + let local_id = variant_data.field(&field_name)?; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1060,11 +1066,17 @@ impl SourceAnalyzer { missing_fields: Vec<LocalFieldId>, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); + let var_data = db.variant_data(variant); + let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { parent: variant, local_id }; + let field = FieldId { + parent: variant, + local_id, + has_default: fields[local_id].has_default, + }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) From c7463fe74355ad3be60e54f6af1973eeee6b9fd6 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Mon, 27 Jan 2025 00:47:31 +0900 Subject: [PATCH 14/75] Implement `HasResolver` and `HasModule` for `FieldId` --- .../rust-analyzer/crates/hir-def/src/lib.rs | 12 +++++---- .../crates/hir-def/src/resolver.rs | 27 +++++++++---------- 2 files changed, 20 insertions(+), 19 deletions(-) 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 1f06331783df6..cf97d27fd1fd4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -1357,6 +1357,12 @@ impl HasModule for VariantId { } } +impl HasModule for FieldId { + fn module(&self, db: &dyn DefDatabase) -> ModuleId { + self.parent.module(db) + } +} + impl HasModule for MacroId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match *self { @@ -1380,11 +1386,7 @@ impl HasModule for TypeOwnerId { TypeOwnerId::ImplId(it) => it.module(db), TypeOwnerId::EnumVariantId(it) => it.module(db), TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), - TypeOwnerId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.module(db), - VariantId::StructId(it) => it.module(db), - VariantId::UnionId(it) => it.module(db), - }, + TypeOwnerId::FieldId(it) => it.module(db), } } } 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 52998942965d1..b415efafa6fbf 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -27,10 +27,11 @@ use crate::{ type_ref::{LifetimeRef, TypesMap}, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, - ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, - ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, - MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, - TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, + ExternBlockId, ExternCrateId, FieldId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, + HasModule, ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, + Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, + TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, + VariantId, }; #[derive(Debug, Clone)] @@ -1227,11 +1228,7 @@ impl HasResolver for TypeOwnerId { TypeOwnerId::TypeAliasId(it) => it.resolver(db), TypeOwnerId::ImplId(it) => it.resolver(db), TypeOwnerId::EnumVariantId(it) => it.resolver(db), - TypeOwnerId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.resolver(db), - VariantId::StructId(it) => it.resolver(db), - VariantId::UnionId(it) => it.resolver(db), - }, + TypeOwnerId::FieldId(it) => it.resolver(db), } } } @@ -1244,11 +1241,7 @@ impl HasResolver for DefWithBodyId { DefWithBodyId::StaticId(s) => s.resolver(db), DefWithBodyId::VariantId(v) => v.resolver(db), DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db), - DefWithBodyId::FieldId(f) => match f.parent { - VariantId::EnumVariantId(it) => it.resolver(db), - VariantId::StructId(it) => it.resolver(db), - VariantId::UnionId(it) => it.resolver(db), - }, + DefWithBodyId::FieldId(f) => f.resolver(db), } } } @@ -1295,6 +1288,12 @@ impl HasResolver for VariantId { } } +impl HasResolver for FieldId { + fn resolver(self, db: &dyn DefDatabase) -> Resolver { + self.parent.resolver(db) + } +} + impl HasResolver for MacroId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { From ec89b7d2930ac9ddb97a7545b79bb2c6ee444c66 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Mon, 27 Jan 2025 00:48:19 +0900 Subject: [PATCH 15/75] Add a test for field default value body as defining usage of TAIT --- .../rust-analyzer/crates/hir-def/src/lib.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 112 ++++++++++++++---- .../src/tests/type_alias_impl_traits.rs | 50 +++++++- 3 files changed, 141 insertions(+), 27 deletions(-) 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 cf97d27fd1fd4..80aa5516602e3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -1399,11 +1399,7 @@ impl HasModule for DefWithBodyId { DefWithBodyId::ConstId(it) => it.module(db), DefWithBodyId::VariantId(it) => it.module(db), DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), - DefWithBodyId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.module(db), - VariantId::StructId(it) => it.module(db), - VariantId::UnionId(it) => it.module(db), - }, + DefWithBodyId::FieldId(it) => it.module(db), } } } 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 a5af712b42f28..5177ed07b1fbf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -16,6 +16,7 @@ use std::env; use std::sync::LazyLock; use base_db::SourceDatabaseFileInputExt as _; +use either::Either; use expect_test::Expect; use hir_def::{ db::DefDatabase, @@ -23,12 +24,14 @@ use hir_def::{ hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, - src::HasSource, - AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, SyntheticSyntax, + src::{HasChildSource, HasSource}, + AdtId, AssocItemId, DefWithBodyId, FieldId, HasModule, LocalModuleId, Lookup, ModuleDefId, + SyntheticSyntax, }; use hir_expand::{db::ExpandDatabase, FileRange, InFile}; use itertools::Itertools; use rustc_hash::FxHashMap; +use span::TextSize; use stdx::format_to; use syntax::{ ast::{self, AstNode, HasName}, @@ -132,14 +135,40 @@ fn check_impl( None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }) + visit_module(&db, &def_map, module.local_id, &mut |it| match it { + ModuleDefId::FunctionId(it) => defs.push(it.into()), + ModuleDefId::EnumVariantId(it) => { + defs.push(it.into()); + let variant_id = it.into(); + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + ModuleDefId::ConstId(it) => defs.push(it.into()), + ModuleDefId::StaticId(it) => defs.push(it.into()), + ModuleDefId::AdtId(it) => { + let variant_id = match it { + AdtId::StructId(it) => it.into(), + AdtId::UnionId(it) => it.into(), + AdtId::EnumId(_) => return, + }; + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + _ => {} }); } defs.sort_by_key(|def| match def { @@ -160,12 +189,20 @@ fn check_impl( loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(_) => unreachable!(), + DefWithBodyId::FieldId(it) => { + let cs = it.parent.child_source(&db); + match cs.value.get(it.local_id) { + Some(Either::Left(it)) => it.syntax().text_range().start(), + Some(Either::Right(it)) => it.syntax().text_range().end(), + None => TextSize::new(u32::MAX), + } + } }); let mut unexpected_type_mismatches = String::new(); for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); + dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { @@ -389,14 +426,40 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<DefWithBodyId> = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| { - defs.push(match it { - ModuleDefId::FunctionId(it) => it.into(), - ModuleDefId::EnumVariantId(it) => it.into(), - ModuleDefId::ConstId(it) => it.into(), - ModuleDefId::StaticId(it) => it.into(), - _ => return, - }) + visit_module(&db, &def_map, module.local_id, &mut |it| match it { + ModuleDefId::FunctionId(it) => defs.push(it.into()), + ModuleDefId::EnumVariantId(it) => { + defs.push(it.into()); + let variant_id = it.into(); + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + ModuleDefId::ConstId(it) => defs.push(it.into()), + ModuleDefId::StaticId(it) => defs.push(it.into()), + ModuleDefId::AdtId(it) => { + let variant_id = match it { + AdtId::StructId(it) => it.into(), + AdtId::UnionId(it) => it.into(), + AdtId::EnumId(_) => return, + }; + let vd = db.variant_data(variant_id); + defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { + if fd.has_default { + let field = FieldId { parent: variant_id, local_id, has_default: true }; + Some(DefWithBodyId::FieldId(field)) + } else { + None + } + })); + } + _ => {} }); defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { @@ -416,7 +479,14 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(_) => unreachable!(), + DefWithBodyId::FieldId(it) => { + let cs = it.parent.child_source(&db); + match cs.value.get(it.local_id) { + Some(Either::Left(it)) => it.syntax().text_range().start(), + Some(Either::Right(it)) => it.syntax().text_range().end(), + None => TextSize::new(u32::MAX), + } + } }); for def in defs { let (body, source_map) = db.body_with_source_map(def); @@ -477,7 +547,7 @@ pub(crate) fn visit_module( let body = db.body(it.into()); visit_body(db, &body, cb); } - ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { + ModuleDefId::AdtId(AdtId::EnumId(it)) => { db.enum_data(it).variants.iter().for_each(|&(it, _)| { let body = db.body(it.into()); cb(it.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs index e2b7bf379cc3b..a2aada57da97d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs @@ -157,5 +157,53 @@ static ALIAS: i32 = { 217..218 '5': i32 205..211: expected impl Trait + ?Sized, got Struct "#]], - ) + ); +} + +#[test] +fn defining_type_alias_impl_trait_from_default_fields() { + check_no_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +struct Foo { + foo: AliasTy = { + let x: AliasTy = Struct; + x + }, +} +"#, + ); + + check_infer_with_mismatches( + r#" +trait Trait {} + +struct Struct; + +impl Trait for Struct {} + +type AliasTy = impl Trait; + +struct Foo { + foo: i32 = { + let x: AliasTy = Struct; + 5 + }, +} +"#, + expect![[r#" + 114..164 '{ ... }': i32 + 128..129 'x': impl Trait + ?Sized + 141..147 'Struct': Struct + 157..158 '5': i32 + 141..147: expected impl Trait + ?Sized, got Struct + "#]], + ); } From e4e7c95048e9625fb33f5264e4ccff149c95d466 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Mon, 27 Jan 2025 00:58:26 +0900 Subject: [PATCH 16/75] Fix a mistake in condition --- src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs | 2 +- src/tools/rust-analyzer/crates/hir-ty/src/tests.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index dd55febbf042d..d744fe64c0d0f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -550,7 +550,7 @@ pub fn record_literal_missing_fields( ) -> Option<(VariantId, Vec<LocalFieldId>, /*has spread expr*/ bool)> { let (fields, has_spread_expr, has_ellipsis) = match expr { Expr::RecordLit { fields, spread, .. } => { - (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) + (fields, !matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) } _ => return None, }; 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 5177ed07b1fbf..56b7e6cba0823 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -202,7 +202,6 @@ fn check_impl( for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); - dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { From c093db0ba16fdc60af9127f2a86bcfe3cec3a99e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Mon, 27 Jan 2025 19:20:26 +0900 Subject: [PATCH 17/75] Remove `has_default` from `FieldId` --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 - .../crates/hir-ty/src/infer/closure.rs | 5 +-- .../crates/hir-ty/src/infer/expr.rs | 6 ++-- .../crates/hir-ty/src/mir/lower.rs | 5 --- .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++---- .../rust-analyzer/crates/hir-ty/src/tests.rs | 8 ++--- .../rust-analyzer/crates/hir/src/display.rs | 4 +-- .../rust-analyzer/crates/hir/src/from_id.rs | 4 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 34 ++++++++++--------- .../hir/src/semantics/child_by_source.rs | 6 +--- .../crates/hir/src/source_analyzer.rs | 16 ++------- 11 files changed, 35 insertions(+), 64 deletions(-) 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 80aa5516602e3..6bc8741c33c57 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -519,7 +519,6 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, - pub has_default: bool, } impl FieldId { 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 def0cb705d8f5..0e9aed416004d 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 @@ -1167,11 +1167,9 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); - let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, - has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1221,13 +1219,12 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, d)) in it { + for (&arg, (i, _)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, - has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); 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 b91f32b09e531..179565440c368 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 @@ -1748,15 +1748,13 @@ impl InferenceContext<'_> { TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { let vd = &self.db.struct_data(*s).variant_data; let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*s).into(), local_id, has_default }; + let field = FieldId { parent: (*s).into(), local_id }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { let vd = &self.db.union_data(*u).variant_data; let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*u).into(), local_id, has_default }; + let field = FieldId { parent: (*u).into(), local_id }; (field, parameters.clone()) } _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index b254bc8e5495f..940d992001d5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -872,13 +872,10 @@ impl<'ctx> MirLowerCtx<'ctx> { None => { let local_id = LocalFieldId::from_raw(RawIdx::from(i as u32)); - let has_default = - variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, local_id, - has_default, })), &mut self.result.projection_store, ); @@ -900,12 +897,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, - has_default, })), &mut self.result.projection_store, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 43e6eb88988f4..2ffea34c85a10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,12 +632,10 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, - has_default, })), x.pat, )) @@ -646,12 +644,8 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, d)| { - PlaceElem::Field(Either::Left(FieldId { - parent: v, - local_id: x, - has_default: d.has_default, - })) + let fields = variant_data.fields().iter().map(|(x, _)| { + PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) }); self.pattern_match_tuple_like( current, 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 56b7e6cba0823..96e7130ecf031 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -143,7 +143,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -161,7 +161,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -433,7 +433,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -451,7 +451,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; + let field = FieldId { parent: variant_id, local_id }; Some(DefWithBodyId::FieldId(field)) } else { None diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 5591af9202703..6f40497055857 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, d)) = it.next() { - let field = Field { parent: (*self).into(), id, has_default: d.has_default }; + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { 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 5c12cf21da3ce..dd26e894d7932 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From<VariantDef> for VariantId { impl From<Field> for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } + FieldId { parent: def.parent.into(), local_id: def.id } } } impl From<FieldId> for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } + Field { parent: def.parent.into(), id: def.local_id } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4d8b1f58f63fc..5b35b0168ac39 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -415,18 +415,24 @@ impl ModuleDef { def.diagnostics(db, &mut acc); } - let fields = match self { - ModuleDef::Adt(Adt::Struct(it)) => Some(it.fields(db)), - ModuleDef::Adt(Adt::Union(it)) => Some(it.fields(db)), - ModuleDef::Variant(it) => Some(it.fields(db)), + let vd: Option<(VariantDef, Arc<VariantData>)> = match self { + ModuleDef::Adt(Adt::Struct(it)) => { + Some((it.into(), db.struct_data(it.id).variant_data.clone())) + } + ModuleDef::Adt(Adt::Union(it)) => { + Some((it.into(), db.union_data(it.id).variant_data.clone())) + } + ModuleDef::Variant(it) => { + Some((it.into(), db.enum_variant_data(it.id).variant_data.clone())) + } _ => None, }; - if let Some(fields) = fields { - for field in fields { - if !field.has_default { + if let Some((parent, vd)) = vd { + for (id, fd) in vd.fields().iter() { + if !fd.has_default { continue; } - let def: DefWithBody = field.into(); + let def: DefWithBody = DefWithBody::Field(Field { parent, id }); def.diagnostics(db, &mut acc, style_lints); } } @@ -1252,7 +1258,6 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, - pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1418,7 +1423,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1480,7 +1485,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1610,7 +1615,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -5166,13 +5171,10 @@ impl Type { _ => return Vec::new(), }; - let var_data = db.variant_data(variant_id); - let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let has_default = fields[local_id].has_default; - let def = Field { parent: variant_id.into(), id: local_id, has_default }; + let def = Field { parent: variant_id.into(), id: local_id }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index ef10a4e148cda..d5dfb98571864 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -160,11 +160,7 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let has_default = match source { - Either::Left(_) => false, - Either::Right(rec) => rec.expr().is_some(), - }; - let id = FieldId { parent, local_id, has_default }; + let id = FieldId { parent, local_id }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), 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 65cf572dea4fa..9f230c225151f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -630,10 +630,8 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); let local_id = variant_data.field(&local_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -654,10 +652,8 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); let local_id = variant_data.field(&field_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1066,17 +1062,11 @@ impl SourceAnalyzer { missing_fields: Vec<LocalFieldId>, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); - let var_data = db.variant_data(variant); - let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { - parent: variant, - local_id, - has_default: fields[local_id].has_default, - }; + let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) From 9a0504b716eea94f9c5f24cedac7617dd1c60a3a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Mon, 27 Jan 2025 14:34:33 +0200 Subject: [PATCH 18/75] Fix #[rustc_deprecated_safe_2024] It should be considered by the edition of the caller, not the callee. Technically we still don't do it correctly - we need the span of the method name (if it comes from a macro), but we don't keep it and this is good enough for now. --- .../rust-analyzer/crates/hir-def/src/data.rs | 6 +- .../crates/hir-def/src/expr_store/lower.rs | 26 ++-- .../crates/hir-def/src/item_tree.rs | 1 + .../hir-ty/src/diagnostics/unsafe_check.rs | 117 ++++++++++++------ .../rust-analyzer/crates/hir-ty/src/lib.rs | 4 +- .../rust-analyzer/crates/hir-ty/src/utils.rs | 38 +++++- .../crates/hir/src/diagnostics.rs | 10 +- .../rust-analyzer/crates/hir/src/display.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/lib.rs | 42 ++++++- .../crates/hir/src/source_analyzer.rs | 13 +- .../crates/hir/src/term_search/tactics.rs | 7 +- .../ide-completion/src/render/function.rs | 2 +- .../src/handlers/missing_unsafe.rs | 56 +++++++-- .../crates/ide/src/syntax_highlighting.rs | 11 +- .../ide/src/syntax_highlighting/format.rs | 4 +- .../ide/src/syntax_highlighting/highlight.rs | 35 ++++-- 16 files changed, 274 insertions(+), 100 deletions(-) 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 a52a2369572e4..c5bbd4edba9e4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data.rs @@ -95,10 +95,10 @@ impl FunctionData { .map(Box::new); let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists(); if flags.contains(FnFlags::HAS_UNSAFE_KW) - && !crate_graph[krate].edition.at_least_2024() && attrs.by_key(&sym::rustc_deprecated_safe_2024).exists() { flags.remove(FnFlags::HAS_UNSAFE_KW); + flags.insert(FnFlags::DEPRECATED_SAFE_2024); } if attrs.by_key(&sym::target_feature).exists() { @@ -152,6 +152,10 @@ impl FunctionData { self.flags.contains(FnFlags::HAS_UNSAFE_KW) } + pub fn is_deprecated_safe_2024(&self) -> bool { + self.flags.contains(FnFlags::DEPRECATED_SAFE_2024) + } + pub fn is_safe(&self) -> bool { self.flags.contains(FnFlags::HAS_SAFE_KW) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 3ce00b78a9e82..811fecf91f494 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -2238,17 +2238,27 @@ impl ExprCollector<'_> { let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new)); let unsafe_arg_new = self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() }); - let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { + let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe { id: None, - // We collect the unused expressions here so that we still infer them instead of - // dropping them out of the expression tree - statements: fmt - .orphans - .into_iter() - .map(|expr| Statement::Expr { expr, has_semi: true }) - .collect(), + statements: Box::new([]), tail: Some(unsafe_arg_new), }); + if !fmt.orphans.is_empty() { + unsafe_arg_new = self.alloc_expr_desugared(Expr::Block { + id: None, + // We collect the unused expressions here so that we still infer them instead of + // dropping them out of the expression tree. We cannot store them in the `Unsafe` + // block because then unsafe blocks within them will get a false "unused unsafe" + // diagnostic (rustc has a notion of builtin unsafe blocks, but we don't). + statements: fmt + .orphans + .into_iter() + .map(|expr| Statement::Expr { expr, has_semi: true }) + .collect(), + tail: Some(unsafe_arg_new), + label: None, + }); + } let idx = self.alloc_expr( Expr::Call { 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 09fb5f6fd2d3a..82e6ff821b76d 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 @@ -951,6 +951,7 @@ bitflags::bitflags! { /// only very few functions with it. So we only encode its existence here, and lookup /// it if needed. const HAS_TARGET_FEATURE = 1 << 8; + const DEPRECATED_SAFE_2024 = 1 << 9; } } 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 03218b4691b86..f6556924297ae 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 @@ -10,24 +10,26 @@ use hir_def::{ path::Path, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, type_ref::Rawness, - AdtId, DefWithBodyId, FieldId, VariantId, + AdtId, DefWithBodyId, FieldId, FunctionId, VariantId, }; +use span::Edition; use crate::{ db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt, TyKind, }; -/// Returns `(unsafe_exprs, fn_is_unsafe)`. -/// -/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. -pub fn missing_unsafe( - db: &dyn HirDatabase, - def: DefWithBodyId, -) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) { +#[derive(Debug, Default)] +pub struct MissingUnsafeResult { + pub unsafe_exprs: Vec<(ExprOrPatId, UnsafetyReason)>, + /// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. + pub fn_is_unsafe: bool, + pub deprecated_safe_calls: Vec<ExprId>, +} + +pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafeResult { let _p = tracing::info_span!("missing_unsafe").entered(); - let mut res = Vec::new(); let is_unsafe = match def { DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(), DefWithBodyId::StaticId(_) @@ -37,11 +39,19 @@ pub fn missing_unsafe( | DefWithBodyId::FieldId(_) => false, }; + let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() }; let body = db.body(def); let infer = db.infer(def); - let mut callback = |node, inside_unsafe_block, reason| { - if inside_unsafe_block == InsideUnsafeBlock::No { - res.push((node, reason)); + let mut callback = |diag| match diag { + UnsafeDiagnostic::UnsafeOperation { node, inside_unsafe_block, reason } => { + if inside_unsafe_block == InsideUnsafeBlock::No { + res.unsafe_exprs.push((node, reason)); + } + } + UnsafeDiagnostic::DeprecatedSafe2024 { node, inside_unsafe_block } => { + if inside_unsafe_block == InsideUnsafeBlock::No { + res.deprecated_safe_calls.push(node) + } } }; let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback); @@ -56,7 +66,7 @@ pub fn missing_unsafe( } } - (res, is_unsafe) + res } #[derive(Debug, Clone, Copy)] @@ -75,15 +85,31 @@ pub enum InsideUnsafeBlock { Yes, } +#[derive(Debug)] +enum UnsafeDiagnostic { + UnsafeOperation { + node: ExprOrPatId, + inside_unsafe_block: InsideUnsafeBlock, + reason: UnsafetyReason, + }, + /// A lint. + DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock }, +} + pub fn unsafe_expressions( db: &dyn HirDatabase, infer: &InferenceResult, def: DefWithBodyId, body: &Body, current: ExprId, - unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + callback: &mut dyn FnMut(InsideUnsafeBlock), ) { - let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb); + let mut visitor_callback = |diag| { + if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag { + callback(inside_unsafe_block); + } + }; + let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback); _ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current); visitor.walk_expr(current); } @@ -97,8 +123,10 @@ struct UnsafeVisitor<'a> { inside_unsafe_block: InsideUnsafeBlock, inside_assignment: bool, inside_union_destructure: bool, - unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + callback: &'a mut dyn FnMut(UnsafeDiagnostic), def_target_features: TargetFeatures, + // FIXME: This needs to be the edition of the span of each call. + edition: Edition, } impl<'a> UnsafeVisitor<'a> { @@ -107,13 +135,14 @@ impl<'a> UnsafeVisitor<'a> { infer: &'a InferenceResult, body: &'a Body, def: DefWithBodyId, - unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason), + unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic), ) -> Self { let resolver = def.resolver(db.upcast()); let def_target_features = match def { DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())), _ => TargetFeatures::default(), }; + let edition = db.crate_graph()[resolver.module().krate()].edition; Self { db, infer, @@ -123,13 +152,34 @@ impl<'a> UnsafeVisitor<'a> { inside_unsafe_block: InsideUnsafeBlock::No, inside_assignment: false, inside_union_destructure: false, - unsafe_expr_cb, + callback: unsafe_expr_cb, def_target_features, + edition, } } - fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) { - (self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason); + fn on_unsafe_op(&mut self, node: ExprOrPatId, reason: UnsafetyReason) { + (self.callback)(UnsafeDiagnostic::UnsafeOperation { + node, + inside_unsafe_block: self.inside_unsafe_block, + reason, + }); + } + + fn check_call(&mut self, node: ExprId, func: FunctionId) { + let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition); + match unsafety { + crate::utils::Unsafety::Safe => {} + crate::utils::Unsafety::Unsafe => { + self.on_unsafe_op(node.into(), UnsafetyReason::UnsafeFnCall) + } + crate::utils::Unsafety::DeprecatedSafe2024 => { + (self.callback)(UnsafeDiagnostic::DeprecatedSafe2024 { + node, + inside_unsafe_block: self.inside_unsafe_block, + }) + } + } } fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) { @@ -154,7 +204,9 @@ impl<'a> UnsafeVisitor<'a> { | Pat::Ref { .. } | Pat::Box { .. } | Pat::Expr(..) - | Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField), + | Pat::ConstBlock(..) => { + self.on_unsafe_op(current.into(), UnsafetyReason::UnionField) + } // `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read. Pat::Missing | Pat::Wild | Pat::Or(_) => {} } @@ -189,9 +241,7 @@ impl<'a> UnsafeVisitor<'a> { match expr { &Expr::Call { callee, .. } => { if let Some(func) = self.infer[callee].as_fn_def(self.db) { - if is_fn_unsafe_to_call(self.db, func, &self.def_target_features) { - self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); - } + self.check_call(current, func); } } Expr::Path(path) => { @@ -217,18 +267,13 @@ impl<'a> UnsafeVisitor<'a> { } } Expr::MethodCall { .. } => { - if self - .infer - .method_resolution(current) - .map(|(func, _)| is_fn_unsafe_to_call(self.db, func, &self.def_target_features)) - .unwrap_or(false) - { - self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall); + if let Some((func, _)) = self.infer.method_resolution(current) { + self.check_call(current, func); } } Expr::UnaryOp { expr, op: UnaryOp::Deref } => { if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) { - self.call_cb(current.into(), UnsafetyReason::RawPtrDeref); + self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref); } } Expr::Unsafe { .. } => { @@ -243,7 +288,7 @@ impl<'a> UnsafeVisitor<'a> { self.walk_pats_top(std::iter::once(target), current); self.inside_assignment = old_inside_assignment; } - Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm), + Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm), // rustc allows union assignment to propagate through field accesses and casts. Expr::Cast { .. } => self.inside_assignment = inside_assignment, Expr::Field { .. } => { @@ -252,7 +297,7 @@ impl<'a> UnsafeVisitor<'a> { if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) = self.infer.field_resolution(current) { - self.call_cb(current.into(), UnsafetyReason::UnionField); + self.on_unsafe_op(current.into(), UnsafetyReason::UnionField); } } } @@ -287,9 +332,9 @@ impl<'a> UnsafeVisitor<'a> { if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { let static_data = self.db.static_data(id); if static_data.mutable { - self.call_cb(node, UnsafetyReason::MutableStatic); + self.on_unsafe_op(node, UnsafetyReason::MutableStatic); } else if static_data.is_extern && !static_data.has_safe_kw { - self.call_cb(node, UnsafetyReason::ExternStatic); + self.on_unsafe_op(node, UnsafetyReason::ExternStatic); } } } 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 37de501e98782..4b159b7541e66 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -100,7 +100,9 @@ pub use mapping::{ }; pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; -pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures}; +pub use utils::{ + all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures, Unsafety, +}; pub use variance::Variance; pub use chalk_ir::{ 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 ffd0b5b5e21ae..c131e97bc4cbe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -24,6 +24,7 @@ use intern::{sym, Symbol}; use rustc_abi::TargetDataLayout; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; +use span::Edition; use stdx::never; use crate::{ @@ -292,21 +293,38 @@ impl TargetFeatures { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Unsafety { + Safe, + Unsafe, + /// A lint. + DeprecatedSafe2024, +} + pub fn is_fn_unsafe_to_call( db: &dyn HirDatabase, func: FunctionId, caller_target_features: &TargetFeatures, -) -> bool { + call_edition: Edition, +) -> Unsafety { let data = db.function_data(func); if data.is_unsafe() { - return true; + return Unsafety::Unsafe; } if data.has_target_feature() { // RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>. let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into())); if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) { - return true; + return Unsafety::Unsafe; + } + } + + if data.is_deprecated_safe_2024() { + if call_edition.at_least_2024() { + return Unsafety::Unsafe; + } else { + return Unsafety::DeprecatedSafe2024; } } @@ -319,14 +337,22 @@ pub fn is_fn_unsafe_to_call( if is_intrinsic_block { // legacy intrinsics // extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute - !db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() + if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } else { // Function in an `extern` block are always unsafe to call, except when // it is marked as `safe`. - !data.is_safe() + if data.is_safe() { + Unsafety::Safe + } else { + Unsafety::Unsafe + } } } - _ => false, + _ => Unsafety::Safe, } } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 066a322e32ba7..64e982c42d7f6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -271,11 +271,17 @@ pub struct PrivateField { pub field: Field, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UnsafeLint { + HardError, + UnsafeOpInUnsafeFn, + DeprecatedSafe2024, +} + #[derive(Debug)] pub struct MissingUnsafe { pub node: InFile<AstPtr<Either<ast::Expr, ast::Pat>>>, - /// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error. - pub only_lint: bool, + pub lint: UnsafeLint, pub reason: UnsafetyReason, } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 6f40497055857..6f4168ab0867d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -82,7 +82,7 @@ impl HirDisplay for Function { } // FIXME: This will show `unsafe` for functions that are `#[target_feature]` but not unsafe // (they are conditionally unsafe to call). We probably should show something else. - if self.is_unsafe_to_call(db, None) { + if self.is_unsafe_to_call(db, None, f.edition()) { f.write_str("unsafe ")?; } if let Some(abi) = &data.abi { diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5b35b0168ac39..b414ef0d71d92 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2040,16 +2040,40 @@ impl DefWithBody { ); } - let (unsafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into()); - for (node, reason) in unsafe_exprs { + let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, self.into()); + for (node, reason) in missing_unsafe.unsafe_exprs { match source_map.expr_or_pat_syntax(node) { - Ok(node) => acc.push(MissingUnsafe { node, only_lint, reason }.into()), + Ok(node) => acc.push( + MissingUnsafe { + node, + lint: if missing_unsafe.fn_is_unsafe { + UnsafeLint::UnsafeOpInUnsafeFn + } else { + UnsafeLint::HardError + }, + reason, + } + .into(), + ), Err(SyntheticSyntax) => { // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. } } } + for node in missing_unsafe.deprecated_safe_calls { + match source_map.expr_syntax(node) { + Ok(node) => acc.push( + MissingUnsafe { + node: node.map(|it| it.wrap_left()), + lint: UnsafeLint::DeprecatedSafe2024, + reason: UnsafetyReason::UnsafeFnCall, + } + .into(), + ), + Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"), + } + } if let Ok(borrowck_results) = db.borrowck(self.into()) { for borrowck_result in borrowck_results.iter() { @@ -2425,11 +2449,19 @@ impl Function { db.attrs(self.id.into()).is_unstable() } - pub fn is_unsafe_to_call(self, db: &dyn HirDatabase, caller: Option<Function>) -> bool { + pub fn is_unsafe_to_call( + self, + db: &dyn HirDatabase, + caller: Option<Function>, + call_edition: Edition, + ) -> bool { let target_features = caller .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()))) .unwrap_or_default(); - hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features) + matches!( + hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition), + hir_ty::Unsafety::Unsafe + ) } /// Whether this function declaration has a definition. 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 9f230c225151f..7d6f110181756 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1107,16 +1107,9 @@ impl SourceAnalyzer { if let Some(expanded_expr) = sm.macro_expansion_expr(macro_expr) { let mut is_unsafe = false; let mut walk_expr = |expr_id| { - unsafe_expressions( - db, - infer, - *def, - body, - expr_id, - &mut |_, inside_unsafe_block, _| { - is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No - }, - ) + unsafe_expressions(db, infer, *def, body, expr_id, &mut |inside_unsafe_block| { + is_unsafe |= inside_unsafe_block == InsideUnsafeBlock::No + }) }; match expanded_expr { ExprOrPatId::ExprId(expanded_expr) => walk_expr(expanded_expr), diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs index 1ff9b6dec9c82..847304d503a84 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs @@ -15,6 +15,7 @@ use hir_ty::mir::BorrowKind; use hir_ty::TyBuilder; use itertools::Itertools; use rustc_hash::FxHashSet; +use span::Edition; use crate::{ Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef, @@ -365,7 +366,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) || it.is_unstable(db) || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() @@ -471,7 +472,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) || it.is_unstable(db) { return None; @@ -662,7 +663,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( // Filter out private and unsafe functions if !it.is_visible_from(db, module) - || it.is_unsafe_to_call(db, None) + || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME) || it.is_unstable(db) { return None; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 4931f8d090242..fd90613964af0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -320,7 +320,7 @@ fn detail(ctx: &CompletionContext<'_>, func: hir::Function, edition: Edition) -> ret_ty = async_ret; } } - if func.is_unsafe_to_call(ctx.db, ctx.containing_function) { + if func.is_unsafe_to_call(ctx.db, ctx.containing_function, ctx.edition) { format_to!(detail, "unsafe "); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index cea8bc5e95904..d48464769c8d6 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -1,5 +1,5 @@ use hir::db::ExpandDatabase; -use hir::{HirFileIdExt, UnsafetyReason}; +use hir::{HirFileIdExt, UnsafeLint, UnsafetyReason}; use ide_db::text_edit::TextEdit; use ide_db::{assists::Assist, source_change::SourceChange}; use syntax::{ast, SyntaxNode}; @@ -11,10 +11,10 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { - let code = if d.only_lint { - DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn") - } else { - DiagnosticCode::RustcHardError("E0133") + let code = match d.lint { + UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"), + UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"), + UnsafeLint::DeprecatedSafe2024 => DiagnosticCode::RustcLint("deprecated_safe_2024"), }; let operation = display_unsafety_reason(d.reason); Diagnostic::new_with_syntax_node_ptr( @@ -585,24 +585,58 @@ fn main() { r#" //- /ed2021.rs crate:ed2021 edition:2021 #[rustc_deprecated_safe_2024] -unsafe fn safe() -> u8 { +unsafe fn deprecated_safe() -> u8 { 0 } + //- /ed2024.rs crate:ed2024 edition:2024 #[rustc_deprecated_safe_2024] -unsafe fn not_safe() -> u8 { +unsafe fn deprecated_safe() -> u8 { 0 } -//- /main.rs crate:main deps:ed2021,ed2024 + +//- /dep1.rs crate:dep1 deps:ed2021,ed2024 edition:2021 +fn main() { + ed2021::deprecated_safe(); + ed2024::deprecated_safe(); +} + +//- /dep2.rs crate:dep2 deps:ed2021,ed2024 edition:2024 +fn main() { + ed2021::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block + ed2024::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + +//- /dep3.rs crate:dep3 deps:ed2021,ed2024 edition:2021 +#![warn(deprecated_safe)] + fn main() { - ed2021::safe(); - ed2024::not_safe(); - //^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block + ed2021::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block + ed2024::deprecated_safe(); + // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block } "#, ) } + #[test] + fn orphan_unsafe_format_args() { + // Checks that we don't place orphan arguments for formatting under an unsafe block. + check_diagnostics( + r#" +//- minicore: fmt +fn foo() { + let p = 0xDEADBEEF as *const i32; + format_args!("", *p); + // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block +} + "#, + ); + } + #[test] fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() { check_diagnostics( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index f53f0aec09842..95f2bf1139504 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -478,7 +478,15 @@ fn traverse( { continue; } - highlight_format_string(hl, sema, krate, &string, &expanded_string, range); + highlight_format_string( + hl, + sema, + krate, + &string, + &expanded_string, + range, + file_id.edition(), + ); if !string.is_raw() { highlight_escape_string(hl, &string, range.start()); @@ -526,6 +534,7 @@ fn traverse( &mut bindings_shadow_count, config.syntactic_name_ref_highlighting, name_like, + file_id.edition(), ), NodeOrToken::Token(token) => { highlight::token(sema, token, file_id.edition()).zip(Some(None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 7234108701a34..43a6bdad7e9bc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -4,6 +4,7 @@ use ide_db::{ syntax_helpers::format_string::{is_format_string, lex_format_specifiers, FormatSpecifier}, SymbolKind, }; +use span::Edition; use syntax::{ast, TextRange}; use crate::{ @@ -18,6 +19,7 @@ pub(super) fn highlight_format_string( string: &ast::String, expanded_string: &ast::String, range: TextRange, + edition: Edition, ) { if is_format_string(expanded_string) { // FIXME: Replace this with the HIR info we have now. @@ -39,7 +41,7 @@ pub(super) fn highlight_format_string( if let Some(res) = res { stack.add(HlRange { range, - highlight: highlight_def(sema, krate, Definition::from(res)), + highlight: highlight_def(sema, krate, Definition::from(res), edition), binding_hash: None, }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index a0c8b2856839a..842b86d2d006d 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 @@ -58,6 +58,7 @@ pub(super) fn name_like( bindings_shadow_count: &mut FxHashMap<hir::Name, u32>, syntactic_name_ref_highlighting: bool, name_like: ast::NameLike, + edition: Edition, ) -> Option<(Highlight, Option<u64>)> { let mut binding_hash = None; let highlight = match name_like { @@ -68,16 +69,17 @@ pub(super) fn name_like( &mut binding_hash, syntactic_name_ref_highlighting, name_ref, + edition, ), ast::NameLike::Name(name) => { - highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name) + highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name, edition) } ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) { Some(IdentClass::NameClass(NameClass::Definition(def))) => { - highlight_def(sema, krate, def) | HlMod::Definition + highlight_def(sema, krate, def, edition) | HlMod::Definition } Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => { - highlight_def(sema, krate, def) + highlight_def(sema, krate, def, edition) } // FIXME: Fallback for 'static and '_, as we do not resolve these yet _ => SymbolKind::LifetimeParam.into(), @@ -234,16 +236,17 @@ fn highlight_name_ref( binding_hash: &mut Option<u64>, syntactic_name_ref_highlighting: bool, name_ref: ast::NameRef, + edition: Edition, ) -> Highlight { let db = sema.db; - if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref) { + if let Some(res) = highlight_method_call_by_name_ref(sema, krate, &name_ref, edition) { return res; } let name_class = match NameRefClass::classify(sema, &name_ref) { Some(name_kind) => name_kind, None if syntactic_name_ref_highlighting => { - return highlight_name_ref_by_syntax(name_ref, sema, krate) + return highlight_name_ref_by_syntax(name_ref, sema, krate, edition) } // FIXME: This is required for helper attributes used by proc-macros, as those do not map down // to anything when used. @@ -267,7 +270,7 @@ fn highlight_name_ref( *binding_hash = Some(calc_binding_hash(&name, *shadow_count)) }; - let mut h = highlight_def(sema, krate, def); + let mut h = highlight_def(sema, krate, def, edition); match def { Definition::Local(local) if is_consumed_lvalue(name_ref.syntax(), &local, db) => { @@ -305,7 +308,7 @@ fn highlight_name_ref( h } NameRefClass::FieldShorthand { field_ref, .. } => { - highlight_def(sema, krate, field_ref.into()) + highlight_def(sema, krate, field_ref.into(), edition) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { let mut h = HlTag::Symbol(SymbolKind::Module).into(); @@ -341,6 +344,7 @@ fn highlight_name( binding_hash: &mut Option<u64>, krate: hir::Crate, name: ast::Name, + edition: Edition, ) -> Highlight { let name_kind = NameClass::classify(sema, &name); if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind { @@ -351,7 +355,7 @@ fn highlight_name( }; match name_kind { Some(NameClass::Definition(def)) => { - let mut h = highlight_def(sema, krate, def) | HlMod::Definition; + let mut h = highlight_def(sema, krate, def, edition) | HlMod::Definition; if let Definition::Trait(trait_) = &def { if trait_.is_unsafe(sema.db) { h |= HlMod::Unsafe; @@ -359,7 +363,7 @@ fn highlight_name( } h } - Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def), + Some(NameClass::ConstReference(def)) => highlight_def(sema, krate, def, edition), Some(NameClass::PatFieldShorthand { field_ref, .. }) => { let mut h = HlTag::Symbol(SymbolKind::Field).into(); if let hir::VariantDef::Union(_) = field_ref.parent_def(sema.db) { @@ -379,6 +383,7 @@ pub(super) fn highlight_def( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, def: Definition, + edition: Edition, ) -> Highlight { let db = sema.db; let mut h = match def { @@ -431,7 +436,8 @@ pub(super) fn highlight_def( // highlighted as unsafe, even when the current target features set is a superset (RFC 2396). // We probably should consider checking the current function, but I found no easy way to do // that (also I'm worried about perf). There's also an instance below. - if func.is_unsafe_to_call(db, None) { + // FIXME: This should be the edition of the call. + if func.is_unsafe_to_call(db, None, edition) { h |= HlMod::Unsafe; } if func.is_async(db) { @@ -579,21 +585,23 @@ fn highlight_method_call_by_name_ref( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, name_ref: &ast::NameRef, + edition: Edition, ) -> Option<Highlight> { let mc = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast)?; - highlight_method_call(sema, krate, &mc) + highlight_method_call(sema, krate, &mc, edition) } fn highlight_method_call( sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, method_call: &ast::MethodCallExpr, + edition: Edition, ) -> Option<Highlight> { let func = sema.resolve_method_call(method_call)?; let mut h = SymbolKind::Method.into(); - if func.is_unsafe_to_call(sema.db, None) || sema.is_unsafe_method_call(method_call) { + if func.is_unsafe_to_call(sema.db, None, edition) || sema.is_unsafe_method_call(method_call) { h |= HlMod::Unsafe; } if func.is_async(sema.db) { @@ -679,6 +687,7 @@ fn highlight_name_ref_by_syntax( name: ast::NameRef, sema: &Semantics<'_, RootDatabase>, krate: hir::Crate, + edition: Edition, ) -> Highlight { let default = HlTag::UnresolvedReference; @@ -689,7 +698,7 @@ fn highlight_name_ref_by_syntax( match parent.kind() { METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) - .and_then(|it| highlight_method_call(sema, krate, &it)) + .and_then(|it| highlight_method_call(sema, krate, &it, edition)) .unwrap_or_else(|| SymbolKind::Method.into()), FIELD_EXPR => { let h = HlTag::Symbol(SymbolKind::Field); From 86d5e295d6fb7ad2c738198a5de08d03bfe7470a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Mon, 27 Jan 2025 15:08:00 +0200 Subject: [PATCH 19/75] Fix a missing standard token in semantic highlighting That was used as a fallback, causing a panic when the fallback was chosen. I also made sure this won't happen again by guaranteeing in the macro generating the tokens that they all exist. --- .../crates/rust-analyzer/src/lsp/semantic_tokens.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs index 991c10743f7a6..3c21e19925257 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/semantic_tokens.rs @@ -24,7 +24,7 @@ macro_rules! define_semantic_token_types { } pub(crate) const SUPPORTED_TYPES: &[SemanticTokenType] = &[ - $(SemanticTokenType::$standard,)* + $(self::types::$standard,)* $(self::types::$custom),* ]; @@ -32,7 +32,7 @@ macro_rules! define_semantic_token_types { use self::types::*; $( if token == $custom { - None $(.or(Some(SemanticTokenType::$fallback)))? + None $(.or(Some(self::types::$fallback)))? } else )* { Some(token )} @@ -60,6 +60,7 @@ define_semantic_token_types![ STRUCT, TYPE_PARAMETER, VARIABLE, + TYPE, } custom { From 5f2072c18803f3993641949c0edc576738e07525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Mon, 27 Jan 2025 15:45:37 +0200 Subject: [PATCH 20/75] Fix syntactic highlighting for renames --- .../crates/ide/src/syntax_highlighting/highlight.rs | 6 ++++++ .../syntax_highlighting/test_data/highlight_strings.html | 4 ++++ .../crates/ide/src/syntax_highlighting/tests.rs | 4 ++++ 3 files changed, 14 insertions(+) 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 a0c8b2856839a..939f0c70b1053 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 @@ -669,6 +669,12 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { STATIC => SymbolKind::Static, IDENT_PAT => SymbolKind::Local, FORMAT_ARGS_ARG => SymbolKind::Local, + RENAME => SymbolKind::Local, + MACRO_RULES => SymbolKind::Macro, + CONST_PARAM => SymbolKind::ConstParam, + SELF_PARAM => SymbolKind::SelfParam, + TRAIT_ALIAS => SymbolKind::TraitAlias, + ASM_OPERAND_NAMED => SymbolKind::Local, _ => return default.into(), }; 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 0a7e273950da6..1794d7dbfe29e 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 @@ -82,6 +82,10 @@ <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> +<span class="keyword">use</span> <span class="unresolved_reference">foo</span><span class="operator">::</span><span class="unresolved_reference">bar</span> <span class="keyword">as</span> <span class="variable declaration">baz</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait_alias declaration">Bar</span> <span class="operator">=</span> <span class="unresolved_reference">Baz</span><span class="semicolon">;</span> +<span class="keyword">trait</span> <span class="trait_alias declaration">Foo</span> <span class="operator">=</span> <span class="trait_alias">Bar</span><span class="semicolon">;</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="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\n</span><span class="char_literal">'</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="char_literal">'</span><span class="escape_sequence">\t</span><span class="char_literal">'</span><span class="semicolon">;</span> diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index af52b33de64bd..b9520ae2bba27 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -466,6 +466,10 @@ macro_rules! reuse_twice { ($literal:literal) => {{stringify!($literal); format_args!($literal)}}; } +use foo::bar as baz; +trait Bar = Baz; +trait Foo = Bar; + fn main() { let a = '\n'; let a = '\t'; From 78feee84c2214d38ab238707cbb2a6a1276d4bd2 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Mon, 27 Jan 2025 18:40:01 +0200 Subject: [PATCH 21/75] Remove duplicate method from `hir::Type` I added it by mistake in #18927. I chose to keep the method as not static, because it's more comfortable, and keep the name `add_reference()` and not `reference()`, because it is clearer and better matches `strip_reference[s]()`. --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 ----------- src/tools/rust-analyzer/crates/hir/src/term_search.rs | 2 +- .../rust-analyzer/crates/ide-assists/src/utils.rs | 4 ++-- .../ide-diagnostics/src/handlers/type_mismatch.rs | 4 ++-- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5b35b0168ac39..9552336a578a6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4698,17 +4698,6 @@ impl Type { Type { env: TraitEnvironment::empty(krate), ty } } - pub fn reference(inner: &Type, m: Mutability) -> Type { - inner.derived( - TyKind::Ref( - if m.is_mut() { hir_ty::Mutability::Mut } else { hir_ty::Mutability::Not }, - hir_ty::error_lifetime(), - inner.ty.clone(), - ) - .intern(Interner), - ) - } - fn new(db: &dyn HirDatabase, lexical_env: impl HasResolver, ty: Ty) -> Type { let resolver = lexical_env.resolver(db.upcast()); let environment = resolver diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index 6f8451370848e..af72179305c8f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -145,7 +145,7 @@ impl LookupTable { self.data .iter() .find(|(t, _)| { - Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, ty) + t.add_reference(Mutability::Shared).could_unify_with_deeply(db, ty) }) .map(|(t, it)| { it.exprs(t) 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 78ff441791343..c1332d99bff9a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -793,8 +793,8 @@ pub(crate) fn convert_reference_type( } fn could_deref_to_target(ty: &hir::Type, target: &hir::Type, db: &dyn HirDatabase) -> bool { - let ty_ref = hir::Type::reference(ty, hir::Mutability::Shared); - let target_ref = hir::Type::reference(target, hir::Mutability::Shared); + let ty_ref = ty.add_reference(hir::Mutability::Shared); + let target_ref = target.add_reference(hir::Mutability::Shared); ty_ref.could_coerce_to(db, &target_ref) } 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 7bc1be382261f..4080b5f12e59a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1,5 +1,5 @@ use either::Either; -use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile, Type}; +use hir::{db::ExpandDatabase, CallableKind, ClosureStyle, HirDisplay, HirFileIdExt, InFile}; use ide_db::{ famous_defs::FamousDefs, source_change::{SourceChange, SourceChangeBuilder}, @@ -88,7 +88,7 @@ fn add_reference( let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into())); let (_, mutability) = d.expected.as_reference()?; - let actual_with_ref = Type::reference(&d.actual, mutability); + let actual_with_ref = d.actual.add_reference(mutability); if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) { return None; } From b4508dc406f0def337bb4a6590f45dd0327a068e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Mon, 27 Jan 2025 18:48:20 +0200 Subject: [PATCH 22/75] Don't suggest `into_iter().method()` on iterators --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 11 ++++++++ .../ide-completion/src/completions/dot.rs | 26 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 5b35b0168ac39..01aa7e0b16c7f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4940,6 +4940,17 @@ impl Type { self.normalize_trait_assoc_type(db, &[], iterator_item.into()) } + pub fn impls_iterator(self, db: &dyn HirDatabase) -> bool { + let Some(iterator_trait) = + db.lang_item(self.env.krate, LangItem::Iterator).and_then(|it| it.as_trait()) + else { + return false; + }; + let canonical_ty = + Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; + method_resolution::implements_trait_unique(&canonical_ty, db, &self.env, iterator_trait) + } + /// Resolves the projection `<Self as IntoIterator>::IntoIter` and returns the resulting type pub fn into_iterator_iter(self, db: &dyn HirDatabase) -> Option<Type> { let trait_ = db.lang_item(self.env.krate, LangItem::IntoIterIntoIter).and_then(|it| { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d12654665ce95..0557265f235c7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -89,7 +89,7 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - if ctx.config.enable_auto_iter { + if ctx.config.enable_auto_iter && !receiver_ty.strip_references().impls_iterator(ctx.db) { // FIXME: // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. @@ -1505,4 +1505,28 @@ fn main() { "#]], ); } + + #[test] + fn no_iter_suggestion_on_iterator() { + check_no_kw( + r#" +//- minicore: iterator +struct MyIter; +impl Iterator for MyIter { + type Item = (); + fn next(&mut self) -> Option<Self::Item> { None } +} + +fn main() { + MyIter.$0 +} +"#, + expect![[r#" + me by_ref() (as Iterator) fn(&mut self) -> &mut Self + me into_iter() (as IntoIterator) fn(self) -> <Self as IntoIterator>::IntoIter + me next() (as Iterator) fn(&mut self) -> Option<<Self as Iterator>::Item> + me nth(…) (as Iterator) fn(&mut self, usize) -> Option<<Self as Iterator>::Item> + "#]], + ); + } } From 1ba894350d4f746757672b1a21e92035cbe563a1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Mon, 27 Jan 2025 18:57:31 +0200 Subject: [PATCH 23/75] Report calling unsafe fn pointer as unsafe --- .../crates/hir-ty/src/diagnostics/unsafe_check.rs | 8 +++++++- .../ide-diagnostics/src/handlers/missing_unsafe.rs | 12 ++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) 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 f6556924297ae..2aca99c93bad4 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 @@ -240,9 +240,15 @@ impl<'a> UnsafeVisitor<'a> { let inside_assignment = mem::replace(&mut self.inside_assignment, false); match expr { &Expr::Call { callee, .. } => { - if let Some(func) = self.infer[callee].as_fn_def(self.db) { + let callee = &self.infer[callee]; + if let Some(func) = callee.as_fn_def(self.db) { self.check_call(current, func); } + if let TyKind::Function(fn_ptr) = callee.kind(Interner) { + if fn_ptr.sig.safety == chalk_ir::Safety::Unsafe { + self.on_unsafe_op(current.into(), UnsafetyReason::UnsafeFnCall); + } + } } Expr::Path(path) => { let guard = diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index d48464769c8d6..323a5723d4a3e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -862,6 +862,18 @@ fn bar() { fn baz() { foo(); // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block +} + "#, + ); + } + + #[test] + fn unsafe_fn_ptr_call() { + check_diagnostics( + r#" +fn f(it: unsafe fn()){ + it(); + // ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block } "#, ); From 26c57086f62a580b975215fd2fded112f194425d Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Mon, 27 Jan 2025 18:23:42 +0100 Subject: [PATCH 24/75] Prioritize formatting thread tasks in main_loop --- .../rust-analyzer/src/handlers/dispatch.rs | 18 +++++++++--------- .../crates/rust-analyzer/src/main_loop.rs | 5 +++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index ff50f7533a64e..4683877db69b0 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -118,7 +118,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::Worker, f, Self::content_modified_error, @@ -147,7 +147,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, false, R>(ThreadIntent::Worker, f, on_cancelled) + self.on_with_thread_intent::<false, false, R>(ThreadIntent::Worker, f, on_cancelled) } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not @@ -166,7 +166,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::Worker, f, Self::content_modified_error, @@ -193,7 +193,7 @@ impl RequestDispatcher<'_> { } return self; } - self.on_with_thread_intent::<true, ALLOW_RETRYING, R>( + self.on_with_thread_intent::<false, ALLOW_RETRYING, R>( ThreadIntent::LatencySensitive, f, Self::content_modified_error, @@ -212,7 +212,7 @@ impl RequestDispatcher<'_> { R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug, R::Result: Serialize, { - self.on_with_thread_intent::<false, false, R>( + self.on_with_thread_intent::<true, false, R>( ThreadIntent::LatencySensitive, f, Self::content_modified_error, @@ -231,7 +231,7 @@ impl RequestDispatcher<'_> { } } - fn on_with_thread_intent<const MAIN_POOL: bool, const ALLOW_RETRYING: bool, R>( + fn on_with_thread_intent<const RUSTFMT: bool, const ALLOW_RETRYING: bool, R>( &mut self, intent: ThreadIntent, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result<R::Result>, @@ -251,10 +251,10 @@ impl RequestDispatcher<'_> { tracing::debug!(?params); let world = self.global_state.snapshot(); - if MAIN_POOL { - &mut self.global_state.task_pool.handle - } else { + if RUSTFMT { &mut self.global_state.fmt_pool.handle + } else { + &mut self.global_state.task_pool.handle } .spawn(intent, move || { let result = panic::catch_unwind(move || { 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 d6dc8b521fd6e..de0ee639ca55a 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 @@ -253,6 +253,11 @@ impl GlobalState { &self, inbox: &Receiver<lsp_server::Message>, ) -> Result<Option<Event>, crossbeam_channel::RecvError> { + // Make sure we reply to formatting requests ASAP so the editor doesn't block + if let Ok(task) = self.fmt_pool.receiver.try_recv() { + return Ok(Some(Event::Task(task))); + } + select! { recv(inbox) -> msg => return Ok(msg.ok().map(Event::Lsp)), From 3dd95e376709230f804fa503391823164588a5f5 Mon Sep 17 00:00:00 2001 From: Vladimir Krivopalov <vladimir@krivopalov.ru> Date: Mon, 27 Jan 2025 14:53:56 -0500 Subject: [PATCH 25/75] Add FreeBSD as a target OS for TestDir to avoid warnings Currently, running rust-analyzer tests on FreeBSD produces an "unused variable" warning. The code is fully compatible with FreeBSD and doesn't have to be omitted. Signed-off-by: Vladimir Krivopalov <vladimir@krivopalov.ru> --- .../crates/rust-analyzer/tests/slow-tests/testdir.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs index d113bd512789a..409be2894fea7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/testdir.rs @@ -43,10 +43,15 @@ impl TestDir { } fs::create_dir_all(&path).unwrap(); - #[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))] + #[cfg(any( + target_os = "macos", + target_os = "linux", + target_os = "windows", + target_os = "freebsd" + ))] if symlink { let symlink_path = base.join(format!("{pid}_{cnt}_symlink")); - #[cfg(any(target_os = "macos", target_os = "linux"))] + #[cfg(any(target_os = "macos", target_os = "linux", target_os = "freebsd"))] std::os::unix::fs::symlink(path, &symlink_path).unwrap(); #[cfg(target_os = "windows")] From 7268b3d81fd49cceb7c5f58dffb2a4d866528ec7 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:05:20 -0500 Subject: [PATCH 26/75] fix: Properly handle CRLF line endings in the syntax tree view --- .../crates/ide/src/view_syntax_tree.rs | 99 ++++++++----- .../editors/code/src/commands.ts | 14 +- .../rust-analyzer/editors/code/src/ctx.ts | 4 +- .../editors/code/src/syntax_tree_provider.ts | 140 ++++++++++++++---- 4 files changed, 181 insertions(+), 76 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs index 218ee15a7dd32..80c3a40e71e8b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_syntax_tree.rs @@ -1,11 +1,15 @@ use hir::Semantics; -use ide_db::{FileId, RootDatabase}; -use span::TextRange; +use ide_db::{ + line_index::{LineCol, LineIndex}, + FileId, LineIndexDatabase, RootDatabase, +}; +use span::{TextRange, TextSize}; use stdx::format_to; use syntax::{ ast::{self, IsString}, AstNode, AstToken, NodeOrToken, SourceFile, SyntaxNode, SyntaxToken, WalkEvent, }; +use triomphe::Arc; // Feature: Show Syntax Tree // @@ -18,24 +22,31 @@ use syntax::{ // |=== pub(crate) fn view_syntax_tree(db: &RootDatabase, file_id: FileId) -> String { let sema = Semantics::new(db); + let line_index = db.line_index(file_id); let parse = sema.parse_guess_edition(file_id); - syntax_node_to_json(parse.syntax(), None) + + let ctx = SyntaxTreeCtx { line_index, in_string: None }; + + syntax_node_to_json(parse.syntax(), &ctx) } -fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { +fn syntax_node_to_json(node: &SyntaxNode, ctx: &SyntaxTreeCtx) -> String { let mut result = String::new(); for event in node.preorder_with_tokens() { match event { WalkEvent::Enter(it) => { let kind = it.kind(); - let (text_range, inner_range_str) = match &ctx { - Some(ctx) => { + let (text_range, inner_range_str) = match &ctx.in_string { + Some(in_string) => { + let start_pos = TextPosition::new(&ctx.line_index, it.text_range().start()); + let end_pos = TextPosition::new(&ctx.line_index, it.text_range().end()); + let inner_start: u32 = it.text_range().start().into(); - let inner_end: u32 = it.text_range().end().into(); + let inner_end: u32 = it.text_range().start().into(); - let mut true_start = inner_start + ctx.offset; - let mut true_end = inner_end + ctx.offset; - for pos in &ctx.marker_positions { + let mut true_start = inner_start + in_string.offset; + let mut true_end = inner_end + in_string.offset; + for pos in &in_string.marker_positions { if *pos >= inner_end { break; } @@ -48,39 +59,33 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { let true_range = TextRange::new(true_start.into(), true_end.into()); - ( - true_range, - format!( - r#","istart":{:?},"iend":{:?}"#, - it.text_range().start(), - it.text_range().end() - ), - ) + (true_range, format!(r#","istart":{start_pos},"iend":{end_pos}"#,)) } None => (it.text_range(), "".to_owned()), }; - let start = text_range.start(); - let end = text_range.end(); + + let start = TextPosition::new(&ctx.line_index, text_range.start()); + let end = TextPosition::new(&ctx.line_index, text_range.end()); match it { NodeOrToken::Node(_) => { format_to!( result, - r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":["# + r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":["# ); } NodeOrToken::Token(token) => { let comma = if token.next_sibling_or_token().is_some() { "," } else { "" }; - match parse_rust_string(token) { + match parse_rust_string(token, ctx) { Some(parsed) => { format_to!( result, - r#"{{"type":"Node","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str},"children":[{parsed}]}}{comma}"# + r#"{{"type":"Node","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str},"children":[{parsed}]}}{comma}"# ); } None => format_to!( result, - r#"{{"type":"Token","kind":"{kind:?}","start":{start:?},"end":{end:?}{inner_range_str}}}{comma}"# + r#"{{"type":"Token","kind":"{kind:?}","start":{start},"end":{end}{inner_range_str}}}{comma}"# ), } } @@ -99,7 +104,26 @@ fn syntax_node_to_json(node: &SyntaxNode, ctx: Option<InStringCtx>) -> String { result } -fn parse_rust_string(token: SyntaxToken) -> Option<String> { +struct TextPosition { + offset: TextSize, + line: u32, + col: u32, +} + +impl std::fmt::Display for TextPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{:?},{},{}]", self.offset, self.line, self.col) + } +} + +impl TextPosition { + pub(crate) fn new(line_index: &LineIndex, offset: TextSize) -> Self { + let LineCol { line, col } = line_index.line_col(offset); + Self { offset, line, col } + } +} + +fn parse_rust_string(token: SyntaxToken, ctx: &SyntaxTreeCtx) -> Option<String> { let string_node = ast::String::cast(token)?; let text = string_node.value().ok()?; @@ -128,13 +152,20 @@ fn parse_rust_string(token: SyntaxToken) -> Option<String> { return None; } - Some(syntax_node_to_json( - node, - Some(InStringCtx { + let ctx = SyntaxTreeCtx { + line_index: ctx.line_index.clone(), + in_string: Some(InStringCtx { offset: string_node.text_range_between_quotes()?.start().into(), marker_positions, }), - )) + }; + + Some(syntax_node_to_json(node, &ctx)) +} + +struct SyntaxTreeCtx { + line_index: Arc<LineIndex>, + in_string: Option<InStringCtx>, } struct InStringCtx { @@ -160,7 +191,7 @@ mod tests { check( r#"fn foo() {}"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":11,"children":[{"type":"Node","kind":"FN","start":0,"end":11,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":6,"children":[{"type":"Token","kind":"IDENT","start":3,"end":6}]},{"type":"Node","kind":"PARAM_LIST","start":6,"end":8,"children":[{"type":"Token","kind":"L_PAREN","start":6,"end":7},{"type":"Token","kind":"R_PAREN","start":7,"end":8}]},{"type":"Token","kind":"WHITESPACE","start":8,"end":9},{"type":"Node","kind":"BLOCK_EXPR","start":9,"end":11,"children":[{"type":"Node","kind":"STMT_LIST","start":9,"end":11,"children":[{"type":"Token","kind":"L_CURLY","start":9,"end":10},{"type":"Token","kind":"R_CURLY","start":10,"end":11}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[11,0,11],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[6,0,6],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[6,0,6]}]},{"type":"Node","kind":"PARAM_LIST","start":[6,0,6],"end":[8,0,8],"children":[{"type":"Token","kind":"L_PAREN","start":[6,0,6],"end":[7,0,7]},{"type":"Token","kind":"R_PAREN","start":[7,0,7],"end":[8,0,8]}]},{"type":"Token","kind":"WHITESPACE","start":[8,0,8],"end":[9,0,9]},{"type":"Node","kind":"BLOCK_EXPR","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Node","kind":"STMT_LIST","start":[9,0,9],"end":[11,0,11],"children":[{"type":"Token","kind":"L_CURLY","start":[9,0,9],"end":[10,0,10]},{"type":"Token","kind":"R_CURLY","start":[10,0,10],"end":[11,0,11]}]}]}]}]}"# ]], ); @@ -173,7 +204,7 @@ fn test() { ", ""); }"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":60,"children":[{"type":"Node","kind":"FN","start":0,"end":60,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":60,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":60,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":58,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":57,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":57,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":57,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":52,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":51,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":30,"istart":0,"iend":5},{"type":"Node","kind":"FN","start":30,"end":46,"istart":5,"iend":21,"children":[{"type":"Token","kind":"FN_KW","start":30,"end":32,"istart":5,"iend":7},{"type":"Token","kind":"WHITESPACE","start":32,"end":33,"istart":7,"iend":8},{"type":"Node","kind":"NAME","start":33,"end":36,"istart":8,"iend":11,"children":[{"type":"Token","kind":"IDENT","start":33,"end":36,"istart":8,"iend":11}]},{"type":"Node","kind":"PARAM_LIST","start":36,"end":38,"istart":11,"iend":13,"children":[{"type":"Token","kind":"L_PAREN","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_PAREN","start":37,"end":38,"istart":12,"iend":13}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"BLOCK_EXPR","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Node","kind":"STMT_LIST","start":39,"end":46,"istart":14,"iend":21,"children":[{"type":"Token","kind":"L_CURLY","start":39,"end":40,"istart":14,"iend":15},{"type":"Token","kind":"WHITESPACE","start":40,"end":45,"istart":15,"iend":20},{"type":"Token","kind":"R_CURLY","start":45,"end":46,"istart":20,"iend":21}]}]}]},{"type":"Token","kind":"WHITESPACE","start":46,"end":51,"istart":21,"iend":26}]}]},{"type":"Token","kind":"COMMA","start":52,"end":53},{"type":"Token","kind":"WHITESPACE","start":53,"end":54},{"type":"Token","kind":"STRING","start":54,"end":56},{"type":"Token","kind":"R_PAREN","start":56,"end":57}]}]}]},{"type":"Token","kind":"SEMICOLON","start":57,"end":58}]},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"R_CURLY","start":59,"end":60}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[60,5,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[60,5,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[58,4,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[57,4,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[57,4,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[52,4,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[26,2,0],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[5,0,5]},{"type":"Node","kind":"FN","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[21,1,9],"children":[{"type":"Token","kind":"FN_KW","start":[30,2,4],"end":[30,2,4],"istart":[5,0,5],"iend":[7,0,7]},{"type":"Token","kind":"WHITESPACE","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Node","kind":"NAME","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11],"children":[{"type":"Token","kind":"IDENT","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[11,0,11]}]},{"type":"Node","kind":"PARAM_LIST","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_PAREN","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_PAREN","start":[37,2,11],"end":[37,2,11],"istart":[12,1,0],"iend":[13,1,1]}]},{"type":"Token","kind":"WHITESPACE","start":[38,2,12],"end":[38,2,12],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"BLOCK_EXPR","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Node","kind":"STMT_LIST","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[21,1,9],"children":[{"type":"Token","kind":"L_CURLY","start":[39,2,13],"end":[39,2,13],"istart":[14,1,2],"iend":[15,1,3]},{"type":"Token","kind":"WHITESPACE","start":[40,2,14],"end":[40,2,14],"istart":[15,1,3],"iend":[20,1,8]},{"type":"Token","kind":"R_CURLY","start":[45,3,4],"end":[45,3,4],"istart":[20,1,8],"iend":[21,1,9]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[46,3,5],"end":[46,3,5],"istart":[21,1,9],"iend":[26,2,0]}]}]},{"type":"Token","kind":"COMMA","start":[52,4,5],"end":[53,4,6]},{"type":"Token","kind":"WHITESPACE","start":[53,4,6],"end":[54,4,7]},{"type":"Token","kind":"STRING","start":[54,4,7],"end":[56,4,9]},{"type":"Token","kind":"R_PAREN","start":[56,4,9],"end":[57,4,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[57,4,10],"end":[58,4,11]}]},{"type":"Token","kind":"WHITESPACE","start":[58,4,11],"end":[59,5,0]},{"type":"Token","kind":"R_CURLY","start":[59,5,0],"end":[60,5,1]}]}]}]}]}"# ]], ) } @@ -190,7 +221,7 @@ fn bar() { ", ""); }"#, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":65,"children":[{"type":"Node","kind":"FN","start":0,"end":65,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":65,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":65,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":63,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":62,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":62,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":62,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":57,"children":[{"type":"Node","kind":"SOURCE_FILE","start":25,"end":56,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":25,"end":26,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":26,"end":38,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":26,"end":28,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":28,"end":29,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":29,"end":32,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":29,"end":32,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":32,"end":34,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":32,"end":33,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":33,"end":34,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":34,"end":35,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":35,"end":38,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":35,"end":36,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":37,"end":38,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":39,"end":51,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":39,"end":41,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":41,"end":42,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":42,"end":45,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":42,"end":45,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":45,"end":47,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":45,"end":46,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":46,"end":47,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":47,"end":48,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":48,"end":51,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":48,"end":49,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":50,"end":51,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":51,"end":56,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":57,"end":58},{"type":"Token","kind":"WHITESPACE","start":58,"end":59},{"type":"Token","kind":"STRING","start":59,"end":61},{"type":"Token","kind":"R_PAREN","start":61,"end":62}]}]}]},{"type":"Token","kind":"SEMICOLON","start":62,"end":63}]},{"type":"Token","kind":"WHITESPACE","start":63,"end":64},{"type":"Token","kind":"R_CURLY","start":64,"end":65}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[65,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[65,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[63,6,11],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[62,6,10],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[62,6,10],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[57,6,5],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[31,2,5],"children":[{"type":"Token","kind":"WHITESPACE","start":[25,1,13],"end":[25,1,13],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[26,2,0],"end":[26,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[28,2,2],"end":[28,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[29,2,3],"end":[29,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[32,2,6],"end":[32,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[33,2,7],"end":[33,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[34,2,8],"end":[34,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[35,2,9],"end":[35,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[36,2,10],"end":[36,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[37,3,0],"end":[37,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[38,3,1],"end":[38,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[26,2,0],"children":[{"type":"Token","kind":"FN_KW","start":[39,4,0],"end":[39,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[41,4,2],"end":[41,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[42,4,3],"end":[42,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[45,4,6],"end":[45,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[46,4,7],"end":[46,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[47,4,8],"end":[47,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Node","kind":"STMT_LIST","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[26,2,0],"children":[{"type":"Token","kind":"L_CURLY","start":[48,4,9],"end":[48,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[49,4,10],"end":[49,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[50,5,0],"end":[50,5,0],"istart":[25,1,13],"iend":[26,2,0]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[51,5,1],"end":[51,5,1],"istart":[26,2,0],"iend":[31,2,5]}]}]},{"type":"Token","kind":"COMMA","start":[57,6,5],"end":[58,6,6]},{"type":"Token","kind":"WHITESPACE","start":[58,6,6],"end":[59,6,7]},{"type":"Token","kind":"STRING","start":[59,6,7],"end":[61,6,9]},{"type":"Token","kind":"R_PAREN","start":[61,6,9],"end":[62,6,10]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[62,6,10],"end":[63,6,11]}]},{"type":"Token","kind":"WHITESPACE","start":[63,6,11],"end":[64,7,0]},{"type":"Token","kind":"R_CURLY","start":[64,7,0],"end":[65,7,1]}]}]}]}]}"# ]], ); @@ -205,7 +236,7 @@ fn bar() { "#, ""); }"###, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":68,"children":[{"type":"Node","kind":"FN","start":0,"end":68,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":68,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":68,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":66,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":65,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":65,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":65,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":60,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":58,"istart":0,"iend":31,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]},{"type":"Token","kind":"WHITESPACE","start":53,"end":58,"istart":26,"iend":31}]}]},{"type":"Token","kind":"COMMA","start":60,"end":61},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"STRING","start":62,"end":64},{"type":"Token","kind":"R_PAREN","start":64,"end":65}]}]}]},{"type":"Token","kind":"SEMICOLON","start":65,"end":66}]},{"type":"Token","kind":"WHITESPACE","start":66,"end":67},{"type":"Token","kind":"R_CURLY","start":67,"end":68}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[68,7,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[68,7,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[66,6,12],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[65,6,11],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[65,6,11],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[60,6,6],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[31,2,3],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[53,5,1],"end":[53,5,1],"istart":[26,1,14],"iend":[31,2,3]}]}]},{"type":"Token","kind":"COMMA","start":[60,6,6],"end":[61,6,7]},{"type":"Token","kind":"WHITESPACE","start":[61,6,7],"end":[62,6,8]},{"type":"Token","kind":"STRING","start":[62,6,8],"end":[64,6,10]},{"type":"Token","kind":"R_PAREN","start":[64,6,10],"end":[65,6,11]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[65,6,11],"end":[66,6,12]}]},{"type":"Token","kind":"WHITESPACE","start":[66,6,12],"end":[67,7,0]},{"type":"Token","kind":"R_CURLY","start":[67,7,0],"end":[68,7,1]}]}]}]}]}"# ]], ); @@ -219,7 +250,7 @@ fn bar() { }"$0#, ""); }"###, expect![[ - r#"{"type":"Node","kind":"SOURCE_FILE","start":0,"end":63,"children":[{"type":"Node","kind":"FN","start":0,"end":63,"children":[{"type":"Token","kind":"FN_KW","start":0,"end":2},{"type":"Token","kind":"WHITESPACE","start":2,"end":3},{"type":"Node","kind":"NAME","start":3,"end":7,"children":[{"type":"Token","kind":"IDENT","start":3,"end":7}]},{"type":"Node","kind":"PARAM_LIST","start":7,"end":9,"children":[{"type":"Token","kind":"L_PAREN","start":7,"end":8},{"type":"Token","kind":"R_PAREN","start":8,"end":9}]},{"type":"Token","kind":"WHITESPACE","start":9,"end":10},{"type":"Node","kind":"BLOCK_EXPR","start":10,"end":63,"children":[{"type":"Node","kind":"STMT_LIST","start":10,"end":63,"children":[{"type":"Token","kind":"L_CURLY","start":10,"end":11},{"type":"Token","kind":"WHITESPACE","start":11,"end":16},{"type":"Node","kind":"EXPR_STMT","start":16,"end":61,"children":[{"type":"Node","kind":"MACRO_EXPR","start":16,"end":60,"children":[{"type":"Node","kind":"MACRO_CALL","start":16,"end":60,"children":[{"type":"Node","kind":"PATH","start":16,"end":22,"children":[{"type":"Node","kind":"PATH_SEGMENT","start":16,"end":22,"children":[{"type":"Node","kind":"NAME_REF","start":16,"end":22,"children":[{"type":"Token","kind":"IDENT","start":16,"end":22}]}]}]},{"type":"Token","kind":"BANG","start":22,"end":23},{"type":"Node","kind":"TOKEN_TREE","start":23,"end":60,"children":[{"type":"Token","kind":"L_PAREN","start":23,"end":24},{"type":"Node","kind":"STRING","start":24,"end":55,"children":[{"type":"Node","kind":"SOURCE_FILE","start":27,"end":53,"istart":0,"iend":26,"children":[{"type":"Token","kind":"WHITESPACE","start":27,"end":28,"istart":0,"iend":1},{"type":"Node","kind":"FN","start":28,"end":40,"istart":1,"iend":13,"children":[{"type":"Token","kind":"FN_KW","start":28,"end":30,"istart":1,"iend":3},{"type":"Token","kind":"WHITESPACE","start":30,"end":31,"istart":3,"iend":4},{"type":"Node","kind":"NAME","start":31,"end":34,"istart":4,"iend":7,"children":[{"type":"Token","kind":"IDENT","start":31,"end":34,"istart":4,"iend":7}]},{"type":"Node","kind":"PARAM_LIST","start":34,"end":36,"istart":7,"iend":9,"children":[{"type":"Token","kind":"L_PAREN","start":34,"end":35,"istart":7,"iend":8},{"type":"Token","kind":"R_PAREN","start":35,"end":36,"istart":8,"iend":9}]},{"type":"Token","kind":"WHITESPACE","start":36,"end":37,"istart":9,"iend":10},{"type":"Node","kind":"BLOCK_EXPR","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Node","kind":"STMT_LIST","start":37,"end":40,"istart":10,"iend":13,"children":[{"type":"Token","kind":"L_CURLY","start":37,"end":38,"istart":10,"iend":11},{"type":"Token","kind":"WHITESPACE","start":38,"end":39,"istart":11,"iend":12},{"type":"Token","kind":"R_CURLY","start":39,"end":40,"istart":12,"iend":13}]}]}]},{"type":"Token","kind":"WHITESPACE","start":40,"end":41,"istart":13,"iend":14},{"type":"Node","kind":"FN","start":41,"end":53,"istart":14,"iend":26,"children":[{"type":"Token","kind":"FN_KW","start":41,"end":43,"istart":14,"iend":16},{"type":"Token","kind":"WHITESPACE","start":43,"end":44,"istart":16,"iend":17},{"type":"Node","kind":"NAME","start":44,"end":47,"istart":17,"iend":20,"children":[{"type":"Token","kind":"IDENT","start":44,"end":47,"istart":17,"iend":20}]},{"type":"Node","kind":"PARAM_LIST","start":47,"end":49,"istart":20,"iend":22,"children":[{"type":"Token","kind":"L_PAREN","start":47,"end":48,"istart":20,"iend":21},{"type":"Token","kind":"R_PAREN","start":48,"end":49,"istart":21,"iend":22}]},{"type":"Token","kind":"WHITESPACE","start":49,"end":50,"istart":22,"iend":23},{"type":"Node","kind":"BLOCK_EXPR","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Node","kind":"STMT_LIST","start":50,"end":53,"istart":23,"iend":26,"children":[{"type":"Token","kind":"L_CURLY","start":50,"end":51,"istart":23,"iend":24},{"type":"Token","kind":"WHITESPACE","start":51,"end":52,"istart":24,"iend":25},{"type":"Token","kind":"R_CURLY","start":52,"end":53,"istart":25,"iend":26}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":55,"end":56},{"type":"Token","kind":"WHITESPACE","start":56,"end":57},{"type":"Token","kind":"STRING","start":57,"end":59},{"type":"Token","kind":"R_PAREN","start":59,"end":60}]}]}]},{"type":"Token","kind":"SEMICOLON","start":60,"end":61}]},{"type":"Token","kind":"WHITESPACE","start":61,"end":62},{"type":"Token","kind":"R_CURLY","start":62,"end":63}]}]}]}]}"# + r#"{"type":"Node","kind":"SOURCE_FILE","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Node","kind":"FN","start":[0,0,0],"end":[63,6,1],"children":[{"type":"Token","kind":"FN_KW","start":[0,0,0],"end":[2,0,2]},{"type":"Token","kind":"WHITESPACE","start":[2,0,2],"end":[3,0,3]},{"type":"Node","kind":"NAME","start":[3,0,3],"end":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[3,0,3],"end":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[7,0,7],"end":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[7,0,7],"end":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[8,0,8],"end":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[9,0,9],"end":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[10,0,10],"end":[63,6,1],"children":[{"type":"Token","kind":"L_CURLY","start":[10,0,10],"end":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[11,0,11],"end":[16,1,4]},{"type":"Node","kind":"EXPR_STMT","start":[16,1,4],"end":[61,5,9],"children":[{"type":"Node","kind":"MACRO_EXPR","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"MACRO_CALL","start":[16,1,4],"end":[60,5,8],"children":[{"type":"Node","kind":"PATH","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"PATH_SEGMENT","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Node","kind":"NAME_REF","start":[16,1,4],"end":[22,1,10],"children":[{"type":"Token","kind":"IDENT","start":[16,1,4],"end":[22,1,10]}]}]}]},{"type":"Token","kind":"BANG","start":[22,1,10],"end":[23,1,11]},{"type":"Node","kind":"TOKEN_TREE","start":[23,1,11],"end":[60,5,8],"children":[{"type":"Token","kind":"L_PAREN","start":[23,1,11],"end":[24,1,12]},{"type":"Node","kind":"STRING","start":[24,1,12],"end":[55,5,3],"children":[{"type":"Node","kind":"SOURCE_FILE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[26,1,14],"children":[{"type":"Token","kind":"WHITESPACE","start":[27,1,15],"end":[27,1,15],"istart":[0,0,0],"iend":[1,0,1]},{"type":"Node","kind":"FN","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[13,1,1],"children":[{"type":"Token","kind":"FN_KW","start":[28,2,0],"end":[28,2,0],"istart":[1,0,1],"iend":[3,0,3]},{"type":"Token","kind":"WHITESPACE","start":[30,2,2],"end":[30,2,2],"istart":[3,0,3],"iend":[4,0,4]},{"type":"Node","kind":"NAME","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7],"children":[{"type":"Token","kind":"IDENT","start":[31,2,3],"end":[31,2,3],"istart":[4,0,4],"iend":[7,0,7]}]},{"type":"Node","kind":"PARAM_LIST","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[9,0,9],"children":[{"type":"Token","kind":"L_PAREN","start":[34,2,6],"end":[34,2,6],"istart":[7,0,7],"iend":[8,0,8]},{"type":"Token","kind":"R_PAREN","start":[35,2,7],"end":[35,2,7],"istart":[8,0,8],"iend":[9,0,9]}]},{"type":"Token","kind":"WHITESPACE","start":[36,2,8],"end":[36,2,8],"istart":[9,0,9],"iend":[10,0,10]},{"type":"Node","kind":"BLOCK_EXPR","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Node","kind":"STMT_LIST","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[13,1,1],"children":[{"type":"Token","kind":"L_CURLY","start":[37,2,9],"end":[37,2,9],"istart":[10,0,10],"iend":[11,0,11]},{"type":"Token","kind":"WHITESPACE","start":[38,2,10],"end":[38,2,10],"istart":[11,0,11],"iend":[12,1,0]},{"type":"Token","kind":"R_CURLY","start":[39,3,0],"end":[39,3,0],"istart":[12,1,0],"iend":[13,1,1]}]}]}]},{"type":"Token","kind":"WHITESPACE","start":[40,3,1],"end":[40,3,1],"istart":[13,1,1],"iend":[14,1,2]},{"type":"Node","kind":"FN","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[26,1,14],"children":[{"type":"Token","kind":"FN_KW","start":[41,4,0],"end":[41,4,0],"istart":[14,1,2],"iend":[16,1,4]},{"type":"Token","kind":"WHITESPACE","start":[43,4,2],"end":[43,4,2],"istart":[16,1,4],"iend":[17,1,5]},{"type":"Node","kind":"NAME","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8],"children":[{"type":"Token","kind":"IDENT","start":[44,4,3],"end":[44,4,3],"istart":[17,1,5],"iend":[20,1,8]}]},{"type":"Node","kind":"PARAM_LIST","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[22,1,10],"children":[{"type":"Token","kind":"L_PAREN","start":[47,4,6],"end":[47,4,6],"istart":[20,1,8],"iend":[21,1,9]},{"type":"Token","kind":"R_PAREN","start":[48,4,7],"end":[48,4,7],"istart":[21,1,9],"iend":[22,1,10]}]},{"type":"Token","kind":"WHITESPACE","start":[49,4,8],"end":[49,4,8],"istart":[22,1,10],"iend":[23,1,11]},{"type":"Node","kind":"BLOCK_EXPR","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Node","kind":"STMT_LIST","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[26,1,14],"children":[{"type":"Token","kind":"L_CURLY","start":[50,4,9],"end":[50,4,9],"istart":[23,1,11],"iend":[24,1,12]},{"type":"Token","kind":"WHITESPACE","start":[51,4,10],"end":[51,4,10],"istart":[24,1,12],"iend":[25,1,13]},{"type":"Token","kind":"R_CURLY","start":[52,5,0],"end":[52,5,0],"istart":[25,1,13],"iend":[26,1,14]}]}]}]}]}]},{"type":"Token","kind":"COMMA","start":[55,5,3],"end":[56,5,4]},{"type":"Token","kind":"WHITESPACE","start":[56,5,4],"end":[57,5,5]},{"type":"Token","kind":"STRING","start":[57,5,5],"end":[59,5,7]},{"type":"Token","kind":"R_PAREN","start":[59,5,7],"end":[60,5,8]}]}]}]},{"type":"Token","kind":"SEMICOLON","start":[60,5,8],"end":[61,5,9]}]},{"type":"Token","kind":"WHITESPACE","start":[61,5,9],"end":[62,6,0]},{"type":"Token","kind":"R_CURLY","start":[62,6,0],"end":[63,6,1]}]}]}]}]}"# ]], ); } diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts index b3aa04af7ed9a..eee623ecae9aa 100644 --- a/src/tools/rust-analyzer/editors/code/src/commands.ts +++ b/src/tools/rust-analyzer/editors/code/src/commands.ts @@ -361,10 +361,7 @@ export function syntaxTreeReveal(): Cmd { const activeEditor = vscode.window.activeTextEditor; if (activeEditor !== undefined) { - const start = activeEditor.document.positionAt(element.start); - const end = activeEditor.document.positionAt(element.end); - - const newSelection = new vscode.Selection(start, end); + const newSelection = new vscode.Selection(element.range.start, element.range.end); activeEditor.selection = newSelection; activeEditor.revealRange(newSelection); @@ -378,15 +375,12 @@ function elementToString( depth: number = 0, ): string { let result = " ".repeat(depth); - const start = element.istart ?? element.start; - const end = element.iend ?? element.end; + const offsets = element.inner?.offsets ?? element.offsets; - result += `${element.kind}@${start}..${end}`; + result += `${element.kind}@${offsets.start}..${offsets.end}`; if (element.type === "Token") { - const startPosition = activeDocument.positionAt(element.start); - const endPosition = activeDocument.positionAt(element.end); - const text = activeDocument.getText(new vscode.Range(startPosition, endPosition)); + const text = activeDocument.getText(element.range).replaceAll("\r\n", "\n"); // JSON.stringify quotes and escapes the string for us. result += ` ${JSON.stringify(text)}\n`; } else { diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 96dc4f19b82c6..4248305d5cc16 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -384,9 +384,7 @@ export class Ctx implements RustAnalyzerExtensionApi { return; } - const start = e.textEditor.document.offsetAt(selection.start); - const end = e.textEditor.document.offsetAt(selection.end); - const result = this.syntaxTreeProvider?.getElementByRange(start, end); + const result = this.syntaxTreeProvider?.getElementByRange(selection); if (result !== undefined) { await this.syntaxTreeView?.reveal(result); } diff --git a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts index c7e8007e838b1..3f7e30f13a3b4 100644 --- a/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts +++ b/src/tools/rust-analyzer/editors/code/src/syntax_tree_provider.ts @@ -37,11 +37,7 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement const editor = vscode.window.activeTextEditor; if (editor !== undefined) { - const start = editor.document.positionAt(element.start); - const end = editor.document.positionAt(element.end); - const range = new vscode.Range(start, end); - - const text = editor.document.getText(range); + const text = editor.document.getText(element.range); item.tooltip = new vscode.MarkdownString().appendCodeblock(text, "rust"); } @@ -74,14 +70,61 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement if (editor && isRustEditor(editor)) { const params = { textDocument: { uri: editor.document.uri.toString() }, range: null }; const fileText = await this.ctx.client.sendRequest(ra.viewSyntaxTree, params); - this.root = JSON.parse(fileText, (_key, value: SyntaxElement) => { + this.root = JSON.parse(fileText, (_key, value: RawElement): SyntaxElement => { + if (value.type !== "Node" && value.type !== "Token") { + // This is something other than a RawElement. + return value; + } + const [startOffset, startLine, startCol] = value.start; + const [endOffset, endLine, endCol] = value.end; + const range = new vscode.Range(startLine, startCol, endLine, endCol); + const offsets = { + start: startOffset, + end: endOffset, + }; + + let inner; + if (value.istart && value.iend) { + const [istartOffset, istartLine, istartCol] = value.istart; + const [iendOffset, iendLine, iendCol] = value.iend; + + inner = { + offsets: { + start: istartOffset, + end: iendOffset, + }, + range: new vscode.Range(istartLine, istartCol, iendLine, iendCol), + }; + } + if (value.type === "Node") { - for (const child of value.children) { - child.parent = value; + const result = { + type: value.type, + kind: value.kind, + offsets, + range, + inner, + children: value.children, + parent: undefined, + document: editor.document, + }; + + for (const child of result.children) { + child.parent = result; } - } - return value; + return result; + } else { + return { + type: value.type, + kind: value.kind, + offsets, + range, + inner, + parent: undefined, + document: editor.document, + }; + } }); } else { this.root = undefined; @@ -90,14 +133,14 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement this._onDidChangeTreeData.fire(); } - getElementByRange(start: number, end: number): SyntaxElement | undefined { + getElementByRange(target: vscode.Range): SyntaxElement | undefined { if (this.root === undefined) { return undefined; } let result: SyntaxElement = this.root; - if (this.root.start === start && this.root.end === end) { + if (this.root.range.isEqual(target)) { return result; } @@ -105,9 +148,9 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement outer: while (true) { for (const child of children) { - if (child.start <= start && child.end >= end) { + if (child.range.contains(target)) { result = child; - if (start === end && start === child.end) { + if (target.isEmpty && target.start === child.range.end) { // When the cursor is on the very end of a token, // we assume the user wants the next token instead. continue; @@ -136,31 +179,72 @@ export class SyntaxTreeProvider implements vscode.TreeDataProvider<SyntaxElement export type SyntaxNode = { type: "Node"; kind: string; - start: number; - end: number; - istart?: number; - iend?: number; + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + /** This element's position within a Rust string literal, if it's inside of one. */ + inner?: { + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + }; children: SyntaxElement[]; parent?: SyntaxElement; + document: vscode.TextDocument; }; type SyntaxToken = { type: "Token"; kind: string; - start: number; - end: number; - istart?: number; - iend?: number; + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + /** This element's position within a Rust string literal, if it's inside of one. */ + inner?: { + range: vscode.Range; + offsets: { + start: number; + end: number; + }; + }; parent?: SyntaxElement; + document: vscode.TextDocument; }; export type SyntaxElement = SyntaxNode | SyntaxToken; +type RawNode = { + type: "Node"; + kind: string; + start: [number, number, number]; + end: [number, number, number]; + istart?: [number, number, number]; + iend?: [number, number, number]; + children: SyntaxElement[]; +}; + +type RawToken = { + type: "Token"; + kind: string; + start: [number, number, number]; + end: [number, number, number]; + istart?: [number, number, number]; + iend?: [number, number, number]; +}; + +type RawElement = RawNode | RawToken; + export class SyntaxTreeItem extends vscode.TreeItem { constructor(private readonly element: SyntaxElement) { super(element.kind); - const icon = getIcon(element.kind); - if (element.type === "Node") { + const icon = getIcon(this.element.kind); + if (this.element.type === "Node") { this.contextValue = "syntaxNode"; this.iconPath = icon ?? new vscode.ThemeIcon("list-tree"); this.collapsibleState = vscode.TreeItemCollapsibleState.Expanded; @@ -170,11 +254,9 @@ export class SyntaxTreeItem extends vscode.TreeItem { this.collapsibleState = vscode.TreeItemCollapsibleState.None; } - if (element.istart !== undefined && element.iend !== undefined) { - this.description = `${this.element.istart}..${this.element.iend}`; - } else { - this.description = `${this.element.start}..${this.element.end}`; - } + const offsets = this.element.inner?.offsets ?? this.element.offsets; + + this.description = `${offsets.start}..${offsets.end}`; } } From a9cd21f03d92c855bf6af807d4569dcc408da91d Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Mon, 27 Jan 2025 14:20:05 -0600 Subject: [PATCH 27/75] Show status bar in RA output --- src/tools/rust-analyzer/editors/code/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index f148041ac3eba..57f4254b68996 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -501,6 +501,10 @@ }, { "pattern": "**/Cargo.lock" + }, + { + "scheme": "output", + "pattern": "extension-output-rust-lang.rust-analyzer*" } ] } From a4e65562746a195229efef4a50b225be11658cb1 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 28/75] Back out "Remove `has_default` from `FieldId`" This backs out commit 8aa6c09fcee6270c787a6f00615d76343fbe5c07. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-ty/src/infer/closure.rs | 5 ++- .../crates/hir-ty/src/infer/expr.rs | 6 ++-- .../crates/hir-ty/src/mir/lower.rs | 5 +++ .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++++-- .../rust-analyzer/crates/hir-ty/src/tests.rs | 8 ++--- .../rust-analyzer/crates/hir/src/display.rs | 4 +-- .../rust-analyzer/crates/hir/src/from_id.rs | 4 +-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 34 +++++++++---------- .../hir/src/semantics/child_by_source.rs | 6 +++- .../crates/hir/src/source_analyzer.rs | 16 +++++++-- 11 files changed, 64 insertions(+), 35 deletions(-) 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 6bc8741c33c57..80aa5516602e3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -519,6 +519,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, + pub has_default: bool, } impl FieldId { 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 0e9aed416004d..def0cb705d8f5 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 @@ -1167,9 +1167,11 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); + let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, + has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1219,12 +1221,13 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, _)) in it { + for (&arg, (i, d)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, + has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); 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 179565440c368..b91f32b09e531 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 @@ -1748,13 +1748,15 @@ impl InferenceContext<'_> { TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { let vd = &self.db.struct_data(*s).variant_data; let local_id = vd.field(name)?; - let field = FieldId { parent: (*s).into(), local_id }; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*s).into(), local_id, has_default }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { let vd = &self.db.union_data(*u).variant_data; let local_id = vd.field(name)?; - let field = FieldId { parent: (*u).into(), local_id }; + let has_default = vd.fields()[local_id].has_default; + let field = FieldId { parent: (*u).into(), local_id, has_default }; (field, parameters.clone()) } _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 940d992001d5b..b254bc8e5495f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -872,10 +872,13 @@ impl<'ctx> MirLowerCtx<'ctx> { None => { let local_id = LocalFieldId::from_raw(RawIdx::from(i as u32)); + let has_default = + variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, local_id, + has_default, })), &mut self.result.projection_store, ); @@ -897,10 +900,12 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, + has_default, })), &mut self.result.projection_store, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 2ffea34c85a10..43e6eb88988f4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,10 +632,12 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; + let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, + has_default, })), x.pat, )) @@ -644,8 +646,12 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, _)| { - PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) + let fields = variant_data.fields().iter().map(|(x, d)| { + PlaceElem::Field(Either::Left(FieldId { + parent: v, + local_id: x, + has_default: d.has_default, + })) }); self.pattern_match_tuple_like( current, 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 96e7130ecf031..56b7e6cba0823 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -143,7 +143,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id }; + let field = FieldId { parent: variant_id, local_id, has_default: true }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -161,7 +161,7 @@ fn check_impl( let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id }; + let field = FieldId { parent: variant_id, local_id, has_default: true }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -433,7 +433,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id }; + let field = FieldId { parent: variant_id, local_id, has_default: true }; Some(DefWithBodyId::FieldId(field)) } else { None @@ -451,7 +451,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let vd = db.variant_data(variant_id); defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { if fd.has_default { - let field = FieldId { parent: variant_id, local_id }; + let field = FieldId { parent: variant_id, local_id, has_default: true }; Some(DefWithBodyId::FieldId(field)) } else { None diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 6f4168ab0867d..7bd80442f0672 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, _)) = it.next() { - let field = Field { parent: (*self).into(), id }; + while let Some((id, d)) = it.next() { + let field = Field { parent: (*self).into(), id, has_default: d.has_default }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { 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 dd26e894d7932..5c12cf21da3ce 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From<VariantDef> for VariantId { impl From<Field> for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id } + FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } } } impl From<FieldId> for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id } + Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index a38c0211e8779..42e4483789c2d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -415,24 +415,18 @@ impl ModuleDef { def.diagnostics(db, &mut acc); } - let vd: Option<(VariantDef, Arc<VariantData>)> = match self { - ModuleDef::Adt(Adt::Struct(it)) => { - Some((it.into(), db.struct_data(it.id).variant_data.clone())) - } - ModuleDef::Adt(Adt::Union(it)) => { - Some((it.into(), db.union_data(it.id).variant_data.clone())) - } - ModuleDef::Variant(it) => { - Some((it.into(), db.enum_variant_data(it.id).variant_data.clone())) - } + let fields = match self { + ModuleDef::Adt(Adt::Struct(it)) => Some(it.fields(db)), + ModuleDef::Adt(Adt::Union(it)) => Some(it.fields(db)), + ModuleDef::Variant(it) => Some(it.fields(db)), _ => None, }; - if let Some((parent, vd)) = vd { - for (id, fd) in vd.fields().iter() { - if !fd.has_default { + if let Some(fields) = fields { + for field in fields { + if !field.has_default { continue; } - let def: DefWithBody = DefWithBody::Field(Field { parent, id }); + let def: DefWithBody = field.into(); def.diagnostics(db, &mut acc, style_lints); } } @@ -1258,6 +1252,7 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, + pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1423,7 +1418,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1485,7 +1480,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -1615,7 +1610,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, _)| Field { parent: self.into(), id }) + .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) .collect() } @@ -5203,10 +5198,13 @@ impl Type { _ => return Vec::new(), }; + let var_data = db.variant_data(variant_id); + let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let def = Field { parent: variant_id.into(), id: local_id }; + let has_default = fields[local_id].has_default; + let def = Field { parent: variant_id.into(), id: local_id, has_default }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index d5dfb98571864..ef10a4e148cda 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -160,7 +160,11 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let id = FieldId { parent, local_id }; + let has_default = match source { + Either::Left(_) => false, + Either::Right(rec) => rec.expr().is_some(), + }; + let id = FieldId { parent, local_id, has_default }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), 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 7d6f110181756..671a4603c26b1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -630,8 +630,10 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); + let fields = variant_data.fields(); let local_id = variant_data.field(&local_name)?; - let field = FieldId { parent: variant, local_id }; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -652,8 +654,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); + let fields = variant_data.fields(); let local_id = variant_data.field(&field_name)?; - let field = FieldId { parent: variant, local_id }; + let field = + FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1062,11 +1066,17 @@ impl SourceAnalyzer { missing_fields: Vec<LocalFieldId>, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); + let var_data = db.variant_data(variant); + let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { parent: variant, local_id }; + let field = FieldId { + parent: variant, + local_id, + has_default: fields[local_id].has_default, + }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) From ff2656d67152a6d3e5f279bdd59081c404bb54f3 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 29/75] Back out "Fix a mistake in condition" This backs out commit e5c38558f5dbc37cbc91f9fda58144ce02e1f5aa. --- src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs | 2 +- src/tools/rust-analyzer/crates/hir-ty/src/tests.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index d744fe64c0d0f..dd55febbf042d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -550,7 +550,7 @@ pub fn record_literal_missing_fields( ) -> Option<(VariantId, Vec<LocalFieldId>, /*has spread expr*/ bool)> { let (fields, has_spread_expr, has_ellipsis) = match expr { Expr::RecordLit { fields, spread, .. } => { - (fields, !matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) + (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) } _ => return None, }; 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 56b7e6cba0823..5177ed07b1fbf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -202,6 +202,7 @@ fn check_impl( for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); + dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { From f22e331a408f32111cbb7c7743b65b5eba95baa6 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 30/75] Back out "Add a test for field default value body as defining usage of TAIT" This backs out commit 4fe18a6fb5a1181a04c47391f558ebab5b8b0f39. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 6 +- .../rust-analyzer/crates/hir-ty/src/tests.rs | 112 ++++-------------- .../src/tests/type_alias_impl_traits.rs | 50 +------- 3 files changed, 27 insertions(+), 141 deletions(-) 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 80aa5516602e3..cf97d27fd1fd4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -1399,7 +1399,11 @@ impl HasModule for DefWithBodyId { DefWithBodyId::ConstId(it) => it.module(db), DefWithBodyId::VariantId(it) => it.module(db), DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), - DefWithBodyId::FieldId(it) => it.module(db), + DefWithBodyId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.module(db), + VariantId::StructId(it) => it.module(db), + VariantId::UnionId(it) => it.module(db), + }, } } } 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 5177ed07b1fbf..a5af712b42f28 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,6 @@ use std::env; use std::sync::LazyLock; use base_db::SourceDatabaseFileInputExt as _; -use either::Either; use expect_test::Expect; use hir_def::{ db::DefDatabase, @@ -24,14 +23,12 @@ use hir_def::{ hir::{ExprId, Pat, PatId}, item_scope::ItemScope, nameres::DefMap, - src::{HasChildSource, HasSource}, - AdtId, AssocItemId, DefWithBodyId, FieldId, HasModule, LocalModuleId, Lookup, ModuleDefId, - SyntheticSyntax, + src::HasSource, + AssocItemId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleDefId, SyntheticSyntax, }; use hir_expand::{db::ExpandDatabase, FileRange, InFile}; use itertools::Itertools; use rustc_hash::FxHashMap; -use span::TextSize; use stdx::format_to; use syntax::{ ast::{self, AstNode, HasName}, @@ -135,40 +132,14 @@ fn check_impl( None => continue, }; let def_map = module.def_map(&db); - visit_module(&db, &def_map, module.local_id, &mut |it| match it { - ModuleDefId::FunctionId(it) => defs.push(it.into()), - ModuleDefId::EnumVariantId(it) => { - defs.push(it.into()); - let variant_id = it.into(); - let vd = db.variant_data(variant_id); - defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { - if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; - Some(DefWithBodyId::FieldId(field)) - } else { - None - } - })); - } - ModuleDefId::ConstId(it) => defs.push(it.into()), - ModuleDefId::StaticId(it) => defs.push(it.into()), - ModuleDefId::AdtId(it) => { - let variant_id = match it { - AdtId::StructId(it) => it.into(), - AdtId::UnionId(it) => it.into(), - AdtId::EnumId(_) => return, - }; - let vd = db.variant_data(variant_id); - defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { - if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; - Some(DefWithBodyId::FieldId(field)) - } else { - None - } - })); - } - _ => {} + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) }); } defs.sort_by_key(|def| match def { @@ -189,20 +160,12 @@ fn check_impl( loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(it) => { - let cs = it.parent.child_source(&db); - match cs.value.get(it.local_id) { - Some(Either::Left(it)) => it.syntax().text_range().start(), - Some(Either::Right(it)) => it.syntax().text_range().end(), - None => TextSize::new(u32::MAX), - } - } + DefWithBodyId::FieldId(_) => unreachable!(), }); let mut unexpected_type_mismatches = String::new(); for def in defs { let (body, body_source_map) = db.body_with_source_map(def); let inference_result = db.infer(def); - dbg!(&inference_result); for (pat, mut ty) in inference_result.type_of_pat.iter() { if let Pat::Bind { id, .. } = body.pats[pat] { @@ -426,40 +389,14 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let def_map = module.def_map(&db); let mut defs: Vec<DefWithBodyId> = Vec::new(); - visit_module(&db, &def_map, module.local_id, &mut |it| match it { - ModuleDefId::FunctionId(it) => defs.push(it.into()), - ModuleDefId::EnumVariantId(it) => { - defs.push(it.into()); - let variant_id = it.into(); - let vd = db.variant_data(variant_id); - defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { - if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; - Some(DefWithBodyId::FieldId(field)) - } else { - None - } - })); - } - ModuleDefId::ConstId(it) => defs.push(it.into()), - ModuleDefId::StaticId(it) => defs.push(it.into()), - ModuleDefId::AdtId(it) => { - let variant_id = match it { - AdtId::StructId(it) => it.into(), - AdtId::UnionId(it) => it.into(), - AdtId::EnumId(_) => return, - }; - let vd = db.variant_data(variant_id); - defs.extend(vd.fields().iter().filter_map(|(local_id, fd)| { - if fd.has_default { - let field = FieldId { parent: variant_id, local_id, has_default: true }; - Some(DefWithBodyId::FieldId(field)) - } else { - None - } - })); - } - _ => {} + visit_module(&db, &def_map, module.local_id, &mut |it| { + defs.push(match it { + ModuleDefId::FunctionId(it) => it.into(), + ModuleDefId::EnumVariantId(it) => it.into(), + ModuleDefId::ConstId(it) => it.into(), + ModuleDefId::StaticId(it) => it.into(), + _ => return, + }) }); defs.sort_by_key(|def| match def { DefWithBodyId::FunctionId(it) => { @@ -479,14 +416,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(it) => { - let cs = it.parent.child_source(&db); - match cs.value.get(it.local_id) { - Some(Either::Left(it)) => it.syntax().text_range().start(), - Some(Either::Right(it)) => it.syntax().text_range().end(), - None => TextSize::new(u32::MAX), - } - } + DefWithBodyId::FieldId(_) => unreachable!(), }); for def in defs { let (body, source_map) = db.body_with_source_map(def); @@ -547,7 +477,7 @@ pub(crate) fn visit_module( let body = db.body(it.into()); visit_body(db, &body, cb); } - ModuleDefId::AdtId(AdtId::EnumId(it)) => { + ModuleDefId::AdtId(hir_def::AdtId::EnumId(it)) => { db.enum_data(it).variants.iter().for_each(|&(it, _)| { let body = db.body(it.into()); cb(it.into()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs index a2aada57da97d..e2b7bf379cc3b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/type_alias_impl_traits.rs @@ -157,53 +157,5 @@ static ALIAS: i32 = { 217..218 '5': i32 205..211: expected impl Trait + ?Sized, got Struct "#]], - ); -} - -#[test] -fn defining_type_alias_impl_trait_from_default_fields() { - check_no_mismatches( - r#" -trait Trait {} - -struct Struct; - -impl Trait for Struct {} - -type AliasTy = impl Trait; - -struct Foo { - foo: AliasTy = { - let x: AliasTy = Struct; - x - }, -} -"#, - ); - - check_infer_with_mismatches( - r#" -trait Trait {} - -struct Struct; - -impl Trait for Struct {} - -type AliasTy = impl Trait; - -struct Foo { - foo: i32 = { - let x: AliasTy = Struct; - 5 - }, -} -"#, - expect![[r#" - 114..164 '{ ... }': i32 - 128..129 'x': impl Trait + ?Sized - 141..147 'Struct': Struct - 157..158 '5': i32 - 141..147: expected impl Trait + ?Sized, got Struct - "#]], - ); + ) } From 9c1a7ab5d88b9f374554dfd550c5ba2fe5b8acf1 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 31/75] Back out "Implement `HasResolver` and `HasModule` for `FieldId`" This backs out commit ce9da9063097c26006886b3f403a0c50790c285a. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 12 ++++----- .../crates/hir-def/src/resolver.rs | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 20 deletions(-) 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 cf97d27fd1fd4..1f06331783df6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -1357,12 +1357,6 @@ impl HasModule for VariantId { } } -impl HasModule for FieldId { - fn module(&self, db: &dyn DefDatabase) -> ModuleId { - self.parent.module(db) - } -} - impl HasModule for MacroId { fn module(&self, db: &dyn DefDatabase) -> ModuleId { match *self { @@ -1386,7 +1380,11 @@ impl HasModule for TypeOwnerId { TypeOwnerId::ImplId(it) => it.module(db), TypeOwnerId::EnumVariantId(it) => it.module(db), TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), - TypeOwnerId::FieldId(it) => it.module(db), + TypeOwnerId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.module(db), + VariantId::StructId(it) => it.module(db), + VariantId::UnionId(it) => it.module(db), + }, } } } 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 b415efafa6fbf..52998942965d1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -27,11 +27,10 @@ use crate::{ type_ref::{LifetimeRef, TypesMap}, visibility::{RawVisibility, Visibility}, AdtId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId, EnumId, EnumVariantId, - ExternBlockId, ExternCrateId, FieldId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, - HasModule, ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, - Macro2Id, MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, - TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, - VariantId, + ExternBlockId, ExternCrateId, FunctionId, FxIndexMap, GenericDefId, GenericParamId, HasModule, + ImplId, ItemContainerId, ItemTreeLoc, LifetimeParamId, LocalModuleId, Lookup, Macro2Id, + MacroId, MacroRulesId, ModuleDefId, ModuleId, ProcMacroId, StaticId, StructId, TraitAliasId, + TraitId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, TypeParamId, UseId, VariantId, }; #[derive(Debug, Clone)] @@ -1228,7 +1227,11 @@ impl HasResolver for TypeOwnerId { TypeOwnerId::TypeAliasId(it) => it.resolver(db), TypeOwnerId::ImplId(it) => it.resolver(db), TypeOwnerId::EnumVariantId(it) => it.resolver(db), - TypeOwnerId::FieldId(it) => it.resolver(db), + TypeOwnerId::FieldId(it) => match it.parent { + VariantId::EnumVariantId(it) => it.resolver(db), + VariantId::StructId(it) => it.resolver(db), + VariantId::UnionId(it) => it.resolver(db), + }, } } } @@ -1241,7 +1244,11 @@ impl HasResolver for DefWithBodyId { DefWithBodyId::StaticId(s) => s.resolver(db), DefWithBodyId::VariantId(v) => v.resolver(db), DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db), - DefWithBodyId::FieldId(f) => f.resolver(db), + DefWithBodyId::FieldId(f) => match f.parent { + VariantId::EnumVariantId(it) => it.resolver(db), + VariantId::StructId(it) => it.resolver(db), + VariantId::UnionId(it) => it.resolver(db), + }, } } } @@ -1288,12 +1295,6 @@ impl HasResolver for VariantId { } } -impl HasResolver for FieldId { - fn resolver(self, db: &dyn DefDatabase) -> Resolver { - self.parent.resolver(db) - } -} - impl HasResolver for MacroId { fn resolver(self, db: &dyn DefDatabase) -> Resolver { match self { From 6f723ee9c778a1a3c18fcafd1375a8dc85dc4117 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 32/75] Back out "Compute diagnostics of a field body iff it has one" This backs out commit b4d4d02db8a95f5507fbd0aa90904d7b774f0027. --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 - .../crates/hir-ty/src/infer/closure.rs | 5 +---- .../crates/hir-ty/src/infer/expr.rs | 12 ++++-------- .../crates/hir-ty/src/mir/lower.rs | 11 +++-------- .../hir-ty/src/mir/lower/pattern_matching.rs | 10 ++-------- .../rust-analyzer/crates/hir/src/display.rs | 4 ++-- .../rust-analyzer/crates/hir/src/from_id.rs | 4 ++-- src/tools/rust-analyzer/crates/hir/src/lib.rs | 15 ++++----------- .../hir/src/semantics/child_by_source.rs | 6 +----- .../crates/hir/src/source_analyzer.rs | 18 +++--------------- 10 files changed, 22 insertions(+), 64 deletions(-) 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 1f06331783df6..95700b54db23c 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -519,7 +519,6 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; pub struct FieldId { pub parent: VariantId, pub local_id: LocalFieldId, - pub has_default: bool, } impl FieldId { 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 def0cb705d8f5..0e9aed416004d 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 @@ -1167,11 +1167,9 @@ impl InferenceContext<'_> { }; let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); - let has_default = vd.fields()[local_id].has_default; p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id, - has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); @@ -1221,13 +1219,12 @@ impl InferenceContext<'_> { .iter() .zip(fields.clone()) .chain(ar.iter().rev().zip(fields.rev())); - for (&arg, (i, d)) in it { + for (&arg, (i, _)) in it { let mut p = place.clone(); self.current_capture_span_stack.push(MirSpan::PatId(arg)); p.projections.push(ProjectionElem::Field(Either::Left(FieldId { parent: variant, local_id: i, - has_default: d.has_default, }))); self.consume_with_pat(p, arg); self.current_capture_span_stack.pop(); 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 b91f32b09e531..8b3ec1ff2ad0e 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 @@ -1746,17 +1746,13 @@ impl InferenceContext<'_> { }); } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { - let vd = &self.db.struct_data(*s).variant_data; - let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*s).into(), local_id, has_default }; + let local_id = self.db.struct_data(*s).variant_data.field(name)?; + let field = FieldId { parent: (*s).into(), local_id }; (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { - let vd = &self.db.union_data(*u).variant_data; - let local_id = vd.field(name)?; - let has_default = vd.fields()[local_id].has_default; - let field = FieldId { parent: (*u).into(), local_id, has_default }; + let local_id = self.db.union_data(*u).variant_data.field(name)?; + let field = FieldId { parent: (*u).into(), local_id }; (field, parameters.clone()) } _ => return None, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index b254bc8e5495f..5d89ebd4ef408 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -870,15 +870,12 @@ impl<'ctx> MirLowerCtx<'ctx> { .map(|(i, it)| match it { Some(it) => it, None => { - let local_id = - LocalFieldId::from_raw(RawIdx::from(i as u32)); - let has_default = - variant_data.fields()[local_id].has_default; let p = sp.project( ProjectionElem::Field(Either::Left(FieldId { parent: variant_id, - local_id, - has_default, + local_id: LocalFieldId::from_raw(RawIdx::from( + i as u32, + )), })), &mut self.result.projection_store, ); @@ -900,12 +897,10 @@ impl<'ctx> MirLowerCtx<'ctx> { }; let local_id = variant_data.field(name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[local_id].has_default; let place = place.project( PlaceElem::Field(Either::Left(FieldId { parent: union_id.into(), local_id, - has_default, })), &mut self.result.projection_store, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 43e6eb88988f4..2ffea34c85a10 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -632,12 +632,10 @@ impl MirLowerCtx<'_> { .map(|x| { let field_id = variant_data.field(&x.name).ok_or(MirLowerError::UnresolvedField)?; - let has_default = variant_data.fields()[field_id].has_default; Ok(( PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: field_id, - has_default, })), x.pat, )) @@ -646,12 +644,8 @@ impl MirLowerCtx<'_> { self.pattern_match_adt(current, current_else, it.into_iter(), cond_place, mode)? } AdtPatternShape::Tuple { args, ellipsis } => { - let fields = variant_data.fields().iter().map(|(x, d)| { - PlaceElem::Field(Either::Left(FieldId { - parent: v, - local_id: x, - has_default: d.has_default, - })) + let fields = variant_data.fields().iter().map(|(x, _)| { + PlaceElem::Field(Either::Left(FieldId { parent: v, local_id: x })) }); self.pattern_match_tuple_like( current, diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 7bd80442f0672..6f4168ab0867d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -252,8 +252,8 @@ impl HirDisplay for Struct { f.write_char('(')?; let mut it = variant_data.fields().iter().peekable(); - while let Some((id, d)) = it.next() { - let field = Field { parent: (*self).into(), id, has_default: d.has_default }; + while let Some((id, _)) = it.next() { + let field = Field { parent: (*self).into(), id }; write_visibility(module_id, field.visibility(f.db), f)?; field.ty(f.db).hir_fmt(f)?; if it.peek().is_some() { 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 5c12cf21da3ce..dd26e894d7932 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -237,13 +237,13 @@ impl From<VariantDef> for VariantId { impl From<Field> for FieldId { fn from(def: Field) -> Self { - FieldId { parent: def.parent.into(), local_id: def.id, has_default: def.has_default } + FieldId { parent: def.parent.into(), local_id: def.id } } } impl From<FieldId> for Field { fn from(def: FieldId) -> Self { - Field { parent: def.parent.into(), id: def.local_id, has_default: def.has_default } + Field { parent: def.parent.into(), id: def.local_id } } } diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 42e4483789c2d..ff9832d310196 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -423,9 +423,6 @@ impl ModuleDef { }; if let Some(fields) = fields { for field in fields { - if !field.has_default { - continue; - } let def: DefWithBody = field.into(); def.diagnostics(db, &mut acc, style_lints); } @@ -1252,7 +1249,6 @@ impl From<&Field> for DefWithBodyId { pub struct Field { pub(crate) parent: VariantDef, pub(crate) id: LocalFieldId, - pub(crate) has_default: bool, } #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] @@ -1418,7 +1414,7 @@ impl Struct { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1480,7 +1476,7 @@ impl Union { .variant_data .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -1610,7 +1606,7 @@ impl Variant { self.variant_data(db) .fields() .iter() - .map(|(id, d)| Field { parent: self.into(), id, has_default: d.has_default }) + .map(|(id, _)| Field { parent: self.into(), id }) .collect() } @@ -5198,13 +5194,10 @@ impl Type { _ => return Vec::new(), }; - let var_data = db.variant_data(variant_id); - let fields = var_data.fields(); db.field_types(variant_id) .iter() .map(|(local_id, ty)| { - let has_default = fields[local_id].has_default; - let def = Field { parent: variant_id.into(), id: local_id, has_default }; + let def = Field { parent: variant_id.into(), id: local_id }; let ty = ty.clone().substitute(Interner, substs); (def, self.derived(ty)) }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs index ef10a4e148cda..d5dfb98571864 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/child_by_source.rs @@ -160,11 +160,7 @@ impl ChildBySource for VariantId { let arena_map = arena_map.as_ref(); let parent = *self; for (local_id, source) in arena_map.value.iter() { - let has_default = match source { - Either::Left(_) => false, - Either::Right(rec) => rec.expr().is_some(), - }; - let id = FieldId { parent, local_id, has_default }; + let id = FieldId { parent, local_id }; match source.clone() { Either::Left(source) => res[keys::TUPLE_FIELD].insert(AstPtr::new(&source), id), Either::Right(source) => res[keys::RECORD_FIELD].insert(AstPtr::new(&source), id), 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 671a4603c26b1..98c30540b497e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -630,10 +630,7 @@ impl SourceAnalyzer { let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); - let local_id = variant_data.field(&local_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -654,10 +651,7 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); - let fields = variant_data.fields(); - let local_id = variant_data.field(&field_name)?; - let field = - FieldId { parent: variant, local_id, has_default: fields[local_id].has_default }; + let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); @@ -1066,17 +1060,11 @@ impl SourceAnalyzer { missing_fields: Vec<LocalFieldId>, ) -> Vec<(Field, Type)> { let field_types = db.field_types(variant); - let var_data = db.variant_data(variant); - let fields = var_data.fields(); missing_fields .into_iter() .map(|local_id| { - let field = FieldId { - parent: variant, - local_id, - has_default: fields[local_id].has_default, - }; + let field = FieldId { parent: variant, local_id }; let ty = field_types[local_id].clone().substitute(Interner, substs); (field.into(), Type::new_with_resolver_inner(db, &self.resolver, ty)) }) From 10089c2b5bc0224d539abfc3ef2d86491a3ddb87 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 33/75] Back out "Merge record lit's ellipsis into pre-existing spread's variant" This backs out commit c134b20c9cbc88a36e77acb8522e8dc4573bd906. --- .../crates/hir-def/src/expr_store.rs | 6 +++--- .../crates/hir-def/src/expr_store/lower.rs | 12 +++++------- .../crates/hir-def/src/expr_store/pretty.rs | 18 ++++++------------ .../rust-analyzer/crates/hir-def/src/hir.rs | 10 ++-------- .../crates/hir-ty/src/diagnostics/expr.rs | 13 +++++-------- .../crates/hir-ty/src/infer/closure.rs | 4 ++-- .../crates/hir-ty/src/infer/expr.rs | 4 ++-- .../crates/hir-ty/src/infer/mutability.rs | 12 ++++-------- .../crates/hir-ty/src/mir/lower.rs | 8 ++++---- .../crates/hir/src/source_analyzer.rs | 2 +- 10 files changed, 34 insertions(+), 55 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index d0695d0eac769..9df6eaade757a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -25,7 +25,7 @@ use crate::{ db::DefDatabase, hir::{ Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat, - PatId, RecordFieldPat, Spread, Statement, + PatId, RecordFieldPat, Statement, }, nameres::DefMap, path::{ModPath, Path}, @@ -362,7 +362,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Spread::Base(expr) = spread { + if let &Some(expr) = spread { f(expr); } } @@ -490,7 +490,7 @@ impl ExpressionStore { for field in fields.iter() { f(field.expr); } - if let &Spread::Base(expr) = spread { + if let &Some(expr) = spread { f(expr); } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 811fecf91f494..2cabd0f9795db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -45,7 +45,7 @@ use crate::{ }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, Expr, ExprId, Item, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Spread, Statement, + OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -602,13 +602,11 @@ impl ExprCollector<'_> { Some(RecordLitField { name, expr }) }) .collect(); - let spread = nfl.spread().map(|s| self.collect_expr(s)).map_or_else( - || if nfl.dotdot_token().is_some() { Spread::Yes } else { Spread::No }, - Spread::Base, - ); - Expr::RecordLit { path, fields, spread } + let spread = nfl.spread().map(|s| self.collect_expr(s)); + let ellipsis = nfl.dotdot_token().is_some(); + Expr::RecordLit { path, fields, spread, ellipsis } } else { - Expr::RecordLit { path, fields: Box::default(), spread: Spread::No } + Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 1b3a1bb4dc009..9a8a8c2cd057e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -8,7 +8,7 @@ use span::Edition; use crate::{ hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, LiteralOrConst, Movability, - Spread, Statement, + Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, VariantId, @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread } => { + Expr::RecordLit { path, fields, spread, ellipsis: _ } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), @@ -412,16 +412,10 @@ impl Printer<'_> { p.print_expr(field.expr); wln!(p, ","); } - match spread { - Spread::No => {} - Spread::Yes => { - w!(p, ".."); - } - Spread::Base(expr) => { - w!(p, ".."); - p.print_expr(*expr); - wln!(p); - } + if let Some(spread) = spread { + w!(p, ".."); + p.print_expr(*spread); + wln!(p); } }); w!(self, "}}"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index e09ce67a897d1..1e2417ecdf3a8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -251,7 +251,8 @@ pub enum Expr { RecordLit { path: Option<Box<Path>>, fields: Box<[RecordLitField]>, - spread: Spread, + spread: Option<ExprId>, + ellipsis: bool, }, Field { expr: ExprId, @@ -478,13 +479,6 @@ pub struct RecordLitField { pub expr: ExprId, } -#[derive(Debug, Clone, Eq, PartialEq)] -pub enum Spread { - No, - Yes, - Base(ExprId), -} - #[derive(Debug, Clone, Eq, PartialEq)] pub enum Statement { Let { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index dd55febbf042d..d8700e2777749 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -8,7 +8,6 @@ use base_db::CrateId; use chalk_solve::rust_ir::AdtKind; use either::Either; use hir_def::{ - hir::Spread, lang_item::LangItem, resolver::{HasResolver, ValueNs}, AdtId, AssocItemId, DefWithBodyId, HasModule, ItemContainerId, Lookup, @@ -547,11 +546,9 @@ pub fn record_literal_missing_fields( infer: &InferenceResult, id: ExprId, expr: &Expr, -) -> Option<(VariantId, Vec<LocalFieldId>, /*has spread expr*/ bool)> { - let (fields, has_spread_expr, has_ellipsis) = match expr { - Expr::RecordLit { fields, spread, .. } => { - (fields, matches!(spread, Spread::Base(_)), matches!(spread, Spread::Yes)) - } +) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { + let (fields, exhaustive, ellipsis) = match expr { + Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), _ => return None, }; @@ -567,7 +564,7 @@ pub fn record_literal_missing_fields( .fields() .iter() .filter_map(|(f, d)| { - if (has_ellipsis && d.has_default) || specified_fields.contains(&d.name) { + if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { None } else { Some(f) @@ -577,7 +574,7 @@ pub fn record_literal_missing_fields( if missed_fields.is_empty() { return None; } - Some((variant_def, missed_fields, has_spread_expr)) + Some((variant_def, missed_fields, exhaustive)) } pub fn record_pattern_missing_fields( 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 0e9aed416004d..9283c46d0f611 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,7 +12,7 @@ use hir_def::{ data::adt::VariantData, hir::{ Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId, - Spread, Statement, UnaryOp, + Statement, UnaryOp, }, lang_item::LangItem, path::Path, @@ -796,7 +796,7 @@ impl InferenceContext<'_> { self.consume_expr(expr); } Expr::RecordLit { fields, spread, .. } => { - if let &Spread::Base(expr) = spread { + if let &Some(expr) = spread { self.consume_expr(expr); } self.consume_exprs(fields.iter().map(|it| it.expr)); 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 8b3ec1ff2ad0e..b951443897cb0 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,7 @@ use either::Either; use hir_def::{ hir::{ ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, ExprOrPatId, - LabelId, Literal, Pat, PatId, Spread, Statement, UnaryOp, + LabelId, Literal, Pat, PatId, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -775,7 +775,7 @@ impl InferenceContext<'_> { } } } - if let Spread::Base(expr) = spread { + if let Some(expr) = spread { self.infer_expr(*expr, &Expectation::has_type(ty.clone()), ExprIsRead::Yes); } ty diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index e95a425498379..5b6c3cd152446 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -4,8 +4,8 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ hir::{ - Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Spread, - Statement, UnaryOp, + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, + UnaryOp, }, lang_item::LangItem, }; @@ -121,12 +121,8 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread } => { - let spread_expr = match spread { - Spread::Base(expr) => Some(*expr), - _ => None, - }; - self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(spread_expr)) + Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { + self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { if mutability == Mutability::Mut { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 5d89ebd4ef408..85e8d17203107 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -9,7 +9,7 @@ use hir_def::{ expr_store::{Body, HygieneId}, hir::{ ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, - LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Spread, + LiteralOrConst, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, }, lang_item::{LangItem, LangItemTarget}, path::Path, @@ -823,16 +823,16 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread } => { + Expr::RecordLit { fields, path, spread, ellipsis: _ } => { let spread_place = match spread { - &Spread::Base(it) => { + &Some(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { return Ok(None); }; current = c; Some(p) } - _ => None, + None => None, }; let variant_id = self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path { 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 98c30540b497e..ca239826d4fcf 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1023,7 +1023,7 @@ impl SourceAnalyzer { let expr_id = self.expr_id(db, &literal.clone().into())?; let substs = infer[expr_id].as_adt()?.1; - let (variant, missing_fields, _) = match expr_id { + let (variant, missing_fields, _exhaustive) = match expr_id { ExprOrPatId::ExprId(expr_id) => { record_literal_missing_fields(db, infer, expr_id, &body[expr_id])? } From d039b1094c497ba6a5b367e16f2ade45c9c3324a Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 34/75] Back out "Handle missing fields diagnostics" This backs out commit e6a103ae50699db1dbb0676d075a4bcda2247939. --- .../crates/hir-def/src/data/adt.rs | 2 -- .../crates/hir-def/src/expr_store/lower.rs | 5 ++- .../crates/hir-def/src/expr_store/pretty.rs | 2 +- .../rust-analyzer/crates/hir-def/src/hir.rs | 1 - .../crates/hir-def/src/item_tree.rs | 1 - .../crates/hir-def/src/item_tree/lower.rs | 5 ++- .../crates/hir-def/src/item_tree/pretty.rs | 8 ++--- .../crates/hir-ty/src/diagnostics/expr.rs | 12 ++----- .../crates/hir-ty/src/infer/mutability.rs | 2 +- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../src/handlers/missing_fields.rs | 31 ------------------- 11 files changed, 12 insertions(+), 59 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index aaa260a358b6a..8fc19854033cb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -85,7 +85,6 @@ pub struct FieldData { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibility, - pub has_default: bool, } fn repr_from_value( @@ -479,6 +478,5 @@ fn lower_field( name: field.name.clone(), type_ref: field.type_ref, visibility: item_tree[override_visibility.unwrap_or(field.visibility)].clone(), - has_default: field.has_default, } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 2cabd0f9795db..35cd8f3c69f78 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -603,10 +603,9 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - let ellipsis = nfl.dotdot_token().is_some(); - Expr::RecordLit { path, fields, spread, ellipsis } + Expr::RecordLit { path, fields, spread } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None, ellipsis: false } + Expr::RecordLit { path, fields: Box::default(), spread: None } }; self.alloc_expr(record_lit, syntax_ptr) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 9a8a8c2cd057e..6ba0bbd61c489 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -398,7 +398,7 @@ impl Printer<'_> { self.print_expr(*expr); } } - Expr::RecordLit { path, fields, spread, ellipsis: _ } => { + Expr::RecordLit { path, fields, spread } => { match path { Some(path) => self.print_path(path), None => w!(self, "�"), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 1e2417ecdf3a8..0dcddf162b2fa 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -252,7 +252,6 @@ pub enum Expr { path: Option<Box<Path>>, fields: Box<[RecordLitField]>, spread: Option<ExprId>, - ellipsis: bool, }, Field { expr: ExprId, 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 82e6ff821b76d..8d5b3eeb28e10 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 @@ -1007,7 +1007,6 @@ pub struct Field { pub name: Name, pub type_ref: TypeRefId, pub visibility: RawVisibilityId, - pub has_default: bool, } #[derive(Debug, Clone, Eq, PartialEq)] 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 69a1933079cee..71848845a84df 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 @@ -319,9 +319,8 @@ impl<'a> Ctx<'a> { }; let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); - let has_default = field.expr().is_some(); - Field { name, type_ref, visibility, has_default } + Field { name, type_ref, visibility } } fn lower_tuple_field( @@ -333,7 +332,7 @@ impl<'a> Ctx<'a> { let name = Name::new_tuple_field(idx); let visibility = self.lower_visibility(field); let type_ref = TypeRef::from_ast_opt(body_ctx, field.ty()); - Field { name, type_ref, visibility, has_default: false } + Field { name, type_ref, visibility } } fn lower_union(&mut self, union: &ast::Union) -> Option<FileItemTreeId<Union>> { 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 1e765ac78eb18..70bf2f13c88a1 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 @@ -135,9 +135,7 @@ impl Printer<'_> { self.whitespace(); w!(self, "{{"); self.indented(|this| { - for (idx, Field { name, type_ref, visibility, has_default: _ }) in - fields.iter().enumerate() - { + for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", @@ -153,9 +151,7 @@ impl Printer<'_> { FieldsShape::Tuple => { w!(self, "("); self.indented(|this| { - for (idx, Field { name, type_ref, visibility, has_default: _ }) in - fields.iter().enumerate() - { + for (idx, Field { name, type_ref, visibility }) in fields.iter().enumerate() { this.print_attrs_of( AttrOwner::Field(parent, Idx::from_raw(RawIdx::from(idx as u32))), "\n", diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index d8700e2777749..0b5f1319243f6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -547,8 +547,8 @@ pub fn record_literal_missing_fields( id: ExprId, expr: &Expr, ) -> Option<(VariantId, Vec<LocalFieldId>, /*exhaustive*/ bool)> { - let (fields, exhaustive, ellipsis) = match expr { - Expr::RecordLit { fields, spread, ellipsis, .. } => (fields, spread.is_none(), *ellipsis), + let (fields, exhaustive) = match expr { + Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()), _ => return None, }; @@ -563,13 +563,7 @@ pub fn record_literal_missing_fields( let missed_fields: Vec<LocalFieldId> = variant_data .fields() .iter() - .filter_map(|(f, d)| { - if (ellipsis && d.has_default) || specified_fields.contains(&d.name) { - None - } else { - Some(f) - } - }) + .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) }) .collect(); if missed_fields.is_empty() { return None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 5b6c3cd152446..d74a383f44ef4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -121,7 +121,7 @@ impl InferenceContext<'_> { Expr::Become { expr } => { self.infer_mut_expr(*expr, Mutability::Not); } - Expr::RecordLit { path: _, fields, spread, ellipsis: _ } => { + Expr::RecordLit { path: _, fields, spread } => { self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread)) } &Expr::Index { base, index } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 85e8d17203107..23072011a7b36 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -823,7 +823,7 @@ impl<'ctx> MirLowerCtx<'ctx> { } Expr::Become { .. } => not_supported!("tail-calls"), Expr::Yield { .. } => not_supported!("yield"), - Expr::RecordLit { fields, path, spread, ellipsis: _ } => { + Expr::RecordLit { fields, path, spread } => { let spread_place = match spread { &Some(it) => { let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else { 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 c495df9daaaf0..938b7182bc946 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 @@ -846,35 +846,4 @@ pub struct Claims { "#, ); } - - #[test] - fn default_field_values() { - check_diagnostics( - r#" -struct F { - field1: i32 = 4, - field2: bool, -} - -fn f() { - let _f = F { - field2: true, - .. - }; - - let _f = F { - //^ 💡 error: missing structure fields: - //| - field1 - field2: true, - }; - - let _f = F { - //^ 💡 error: missing structure fields: - //| - field2 - .. - }; -} -"#, - ); - } } From 0f9cdebe4d62ff57a6c0a5299c7ad98871ed47b4 Mon Sep 17 00:00:00 2001 From: David Barsky <me@davidbarsky.com> Date: Mon, 27 Jan 2025 17:20:11 -0500 Subject: [PATCH 35/75] Back out "feat: Implement `default-field-values`" This backs out commit 7de0b2e75a541b98f735ee6fcd12d326be38d23f. --- .../crates/hir-def/src/expr_store/body.rs | 3 - .../crates/hir-def/src/expr_store/lower.rs | 2 - .../crates/hir-def/src/expr_store/pretty.rs | 27 ------ .../rust-analyzer/crates/hir-def/src/lib.rs | 59 +------------ .../builtin_derive_macro.rs | 42 --------- .../crates/hir-def/src/resolver.rs | 10 --- .../hir-expand/src/builtin/derive_macro.rs | 86 ++----------------- .../hir-ty/src/diagnostics/unsafe_check.rs | 3 +- .../rust-analyzer/crates/hir-ty/src/infer.rs | 16 ---- .../crates/hir-ty/src/mir/lower.rs | 4 - .../crates/hir-ty/src/mir/pretty.rs | 34 +------- .../rust-analyzer/crates/hir-ty/src/tests.rs | 2 - .../rust-analyzer/crates/hir/src/from_id.rs | 2 - src/tools/rust-analyzer/crates/hir/src/lib.rs | 64 +------------- .../rust-analyzer/crates/ide-db/src/defs.rs | 1 - .../rust-analyzer/crates/ide-db/src/search.rs | 6 -- .../src/handlers/type_mismatch.rs | 15 ---- .../crates/parser/src/grammar/expressions.rs | 18 +--- .../crates/parser/src/grammar/items/adt.rs | 5 -- .../parser/test_data/generated/runner.rs | 12 --- .../comma_after_default_values_syntax.rast | 59 ------------- .../err/comma_after_default_values_syntax.rs | 4 - ...cord_literal_before_ellipsis_recovery.rast | 70 +++------------ ...record_literal_before_ellipsis_recovery.rs | 2 - .../ok/record_field_default_values.rast | 28 ------ .../inline/ok/record_field_default_values.rs | 1 - .../parser/inline/ok/record_lit.rast | 47 ---------- .../test_data/parser/inline/ok/record_lit.rs | 2 - .../ok/struct_initializer_with_defaults.rast | 39 --------- .../ok/struct_initializer_with_defaults.rs | 3 - .../rust-analyzer/src/cli/analysis_stats.rs | 6 -- .../rust-analyzer/crates/syntax/rust.ungram | 2 +- .../crates/syntax/src/ast/generated/nodes.rs | 4 - 33 files changed, 31 insertions(+), 647 deletions(-) delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast delete mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 3177b2c74afe3..a55fec4f8b1e1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -122,9 +122,6 @@ impl Body { src.map(|it| it.expr()) } DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()), - DefWithBodyId::FieldId(f) => { - f.record_field_source(db).map(|it| it.and_then(|it| it.expr())) - } } }; let module = def.module(db); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 35cd8f3c69f78..88f770da02a4e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -90,7 +90,6 @@ pub(super) fn lower_body( DefWithBodyId::ConstId(it) => db.attrs(it.into()), DefWithBodyId::InTypeConstId(_) => Attrs::EMPTY, DefWithBodyId::VariantId(it) => db.attrs(it.into()), - DefWithBodyId::FieldId(it) => db.attrs(it.into()), } .rust_analyzer_tool() .any(|attr| *attr.path() == tool_path![skip]); @@ -169,7 +168,6 @@ pub(super) fn lower_body( Awaitable::No("constant") } DefWithBodyId::VariantId(..) => Awaitable::No("enum variant"), - DefWithBodyId::FieldId(..) => Awaitable::No("field"), } }, ); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 6ba0bbd61c489..6a0b1e5197905 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -11,7 +11,6 @@ use crate::{ Statement, }, pretty::{print_generic_args, print_path, print_type_ref}, - VariantId, }; use super::*; @@ -57,32 +56,6 @@ pub(super) fn print_body_hir( loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition), ) } - DefWithBodyId::FieldId(it) => { - let parent_name: String = match it.parent { - VariantId::EnumVariantId(it) => { - let loc = it.lookup(db); - let enum_loc = loc.parent.lookup(db); - format!( - "{}::{}", - enum_loc.id.item_tree(db)[enum_loc.id.value] - .name - .display(db.upcast(), edition), - loc.id.item_tree(db)[loc.id.value].name.display(db.upcast(), edition), - ) - } - VariantId::StructId(it) => it - .lookup(db) - .id - .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()), - VariantId::UnionId(it) => it - .lookup(db) - .id - .resolved(db, |it| it.name.display(db.upcast(), edition).to_string()), - }; - let variant_data = it.parent.variant_data(db); - let field_name = &variant_data.fields()[it.local_id].name; - format!("field {}.{}", parent_name, field_name.display(db.upcast(), edition),) - } }; let mut p = Printer { 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 95700b54db23c..c8efd9043203b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -55,7 +55,6 @@ pub mod visibility; use intern::Interned; pub use rustc_abi as layout; -use src::HasSource; use triomphe::Arc; #[cfg(test)] @@ -78,7 +77,6 @@ use hir_expand::{ builtin::{BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, eager::expand_eager_macro_input, - files::InFileWrapper, impl_intern_lookup, name::Name, proc_macro::{CustomProcMacroExpander, ProcMacroKind}, @@ -521,41 +519,6 @@ pub struct FieldId { pub local_id: LocalFieldId, } -impl FieldId { - pub fn record_field_source( - &self, - db: &dyn DefDatabase, - ) -> InFileWrapper<HirFileId, Option<ast::RecordField>> { - let field_list = match self.parent { - crate::VariantId::EnumVariantId(it) => { - let s = it.lookup(db); - s.source(db).map(|it| { - it.field_list().and_then(|it| match it { - ast::FieldList::RecordFieldList(it) => Some(it), - _ => None, - }) - }) - } - crate::VariantId::StructId(it) => { - let s = it.lookup(db); - s.source(db).map(|it| { - it.field_list().and_then(|it| match it { - ast::FieldList::RecordFieldList(it) => Some(it), - _ => None, - }) - }) - } - crate::VariantId::UnionId(it) => { - let s = it.lookup(db); - s.source(db).map(|it| it.record_field_list()) - } - }; - field_list.map(|it| { - it.and_then(|it| it.fields().nth(self.local_id.into_raw().into_u32() as usize)) - }) - } -} - pub type LocalFieldId = Idx<data::adt::FieldData>; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -723,7 +686,6 @@ pub enum TypeOwnerId { TypeAliasId(TypeAliasId), ImplId(ImplId), EnumVariantId(EnumVariantId), - FieldId(FieldId), } impl TypeOwnerId { @@ -741,11 +703,6 @@ impl TypeOwnerId { GenericDefId::AdtId(AdtId::EnumId(it.lookup(db).parent)) } TypeOwnerId::InTypeConstId(_) => return None, - TypeOwnerId::FieldId(it) => GenericDefId::AdtId(match it.parent { - VariantId::EnumVariantId(it) => AdtId::EnumId(it.lookup(db).parent), - VariantId::StructId(it) => it.into(), - VariantId::UnionId(it) => it.into(), - }), }) } } @@ -760,8 +717,7 @@ impl_from!( TraitAliasId, TypeAliasId, ImplId, - EnumVariantId, - FieldId + EnumVariantId for TypeOwnerId ); @@ -774,7 +730,6 @@ impl From<DefWithBodyId> for TypeOwnerId { DefWithBodyId::ConstId(it) => it.into(), DefWithBodyId::InTypeConstId(it) => it.into(), DefWithBodyId::VariantId(it) => it.into(), - DefWithBodyId::FieldId(it) => it.into(), } } } @@ -930,7 +885,6 @@ pub enum DefWithBodyId { ConstId(ConstId), InTypeConstId(InTypeConstId), VariantId(EnumVariantId), - FieldId(FieldId), } impl_from!(FunctionId, ConstId, StaticId, InTypeConstId for DefWithBodyId); @@ -951,7 +905,6 @@ impl DefWithBodyId { // FIXME: stable rust doesn't allow generics in constants, but we should // use `TypeOwnerId::as_generic_def_id` when it does. DefWithBodyId::InTypeConstId(_) => None, - DefWithBodyId::FieldId(_) => None, } } } @@ -1379,11 +1332,6 @@ impl HasModule for TypeOwnerId { TypeOwnerId::ImplId(it) => it.module(db), TypeOwnerId::EnumVariantId(it) => it.module(db), TypeOwnerId::InTypeConstId(it) => it.lookup(db).owner.module(db), - TypeOwnerId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.module(db), - VariantId::StructId(it) => it.module(db), - VariantId::UnionId(it) => it.module(db), - }, } } } @@ -1396,11 +1344,6 @@ impl HasModule for DefWithBodyId { DefWithBodyId::ConstId(it) => it.module(db), DefWithBodyId::VariantId(it) => it.module(db), DefWithBodyId::InTypeConstId(it) => it.lookup(db).owner.module(db), - DefWithBodyId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.module(db), - VariantId::StructId(it) => it.module(db), - VariantId::UnionId(it) => it.module(db), - }, } } } 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 25391c910eaf6..c31d322132897 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 @@ -211,20 +211,6 @@ enum Bar { #[default] Bar, } -#[derive(Default)] -struct Baz { - field1: i32 = 2, - field2: bool = { false }, -} -#[derive(Default)] -enum Qux { - #[default] - Foo { - field1: i32, - field2: bool = true, - field3: (), - } -} "#, expect![[r#" #[derive(Default)] @@ -238,20 +224,6 @@ enum Bar { #[default] Bar, } -#[derive(Default)] -struct Baz { - field1: i32 = 2, - field2: bool = { false }, -} -#[derive(Default)] -enum Qux { - #[default] - Foo { - field1: i32, - field2: bool = true, - field3: (), - } -} impl <> $crate::default::Default for Foo< > where { fn default() -> Self { @@ -264,20 +236,6 @@ impl <> $crate::default::Default for Bar< > where { fn default() -> Self { Bar::Bar } -} -impl <> $crate::default::Default for Baz< > where { - fn default() -> Self { - Baz { - .. - } - } -} -impl <> $crate::default::Default for Qux< > where { - fn default() -> Self { - Qux::Foo { - field1: $crate::default::Default::default(), field3: $crate::default::Default::default(), .. - } - } }"#]], ); } 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 52998942965d1..7e13ae2f7a1be 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -1227,11 +1227,6 @@ impl HasResolver for TypeOwnerId { TypeOwnerId::TypeAliasId(it) => it.resolver(db), TypeOwnerId::ImplId(it) => it.resolver(db), TypeOwnerId::EnumVariantId(it) => it.resolver(db), - TypeOwnerId::FieldId(it) => match it.parent { - VariantId::EnumVariantId(it) => it.resolver(db), - VariantId::StructId(it) => it.resolver(db), - VariantId::UnionId(it) => it.resolver(db), - }, } } } @@ -1244,11 +1239,6 @@ impl HasResolver for DefWithBodyId { DefWithBodyId::StaticId(s) => s.resolver(db), DefWithBodyId::VariantId(v) => v.resolver(db), DefWithBodyId::InTypeConstId(c) => c.lookup(db).owner.resolver(db), - DefWithBodyId::FieldId(f) => match f.parent { - VariantId::EnumVariantId(it) => it.resolver(db), - VariantId::StructId(it) => it.resolver(db), - VariantId::UnionId(it) => it.resolver(db), - }, } } } 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 f8fb700d55e7b..28b6812139446 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 @@ -80,15 +80,9 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander> BuiltinDeriveExpander::find_by_name(ident) } -#[derive(Clone, Copy)] -enum HasDefault { - Yes, - No, -} - #[derive(Clone)] enum VariantShape { - Struct(Vec<(tt::Ident, HasDefault)>), + Struct(Vec<tt::Ident>), Tuple(usize), Unit, } @@ -104,7 +98,7 @@ impl VariantShape { fn field_names(&self, span: Span) -> Vec<tt::Ident> { match self { - VariantShape::Struct(s) => s.iter().map(|(ident, _)| ident.clone()).collect(), + VariantShape::Struct(s) => s.clone(), VariantShape::Tuple(n) => tuple_field_iterator(span, *n).collect(), VariantShape::Unit => vec![], } @@ -118,7 +112,7 @@ impl VariantShape { ) -> tt::TopSubtree { match self { VariantShape::Struct(fields) => { - let fields = fields.iter().map(|(it, _)| { + let fields = fields.iter().map(|it| { let mapped = field_map(it); quote! {span => #it : #mapped , } }); @@ -141,63 +135,6 @@ impl VariantShape { } } - fn default_expand( - &self, - path: tt::TopSubtree, - span: Span, - field_map: impl Fn(&tt::Ident) -> tt::TopSubtree, - ) -> tt::TopSubtree { - match self { - VariantShape::Struct(fields) => { - let contains_default = fields.iter().any(|it| matches!(it.1, HasDefault::Yes)); - let fields = fields - .iter() - .filter_map(|(it, has_default)| match has_default { - HasDefault::Yes => None, - HasDefault::No => Some(it), - }) - .map(|it| { - let mapped = field_map(it); - quote! {span => #it : #mapped , } - }); - if contains_default { - let mut double_dots = - tt::TopSubtreeBuilder::new(tt::Delimiter::invisible_spanned(span)); - double_dots.push(tt::Leaf::Punct(tt::Punct { - char: '.', - spacing: tt::Spacing::Joint, - span, - })); - double_dots.push(tt::Leaf::Punct(tt::Punct { - char: '.', - spacing: tt::Spacing::Alone, - span, - })); - let double_dots = double_dots.build(); - quote! {span => - #path { ##fields #double_dots } - } - } else { - quote! {span => - #path { ##fields } - } - } - } - &VariantShape::Tuple(n) => { - let fields = tuple_field_iterator(span, n).map(|it| { - let mapped = field_map(&it); - quote! {span => - #mapped , - } - }); - quote! {span => - #path ( ##fields ) - } - } - VariantShape::Unit => path, - } - } - fn from( call_site: Span, tm: &ExpansionSpanMap, @@ -207,15 +144,8 @@ impl VariantShape { None => VariantShape::Unit, Some(FieldList::RecordFieldList(it)) => VariantShape::Struct( it.fields() - .map(|it| { - ( - it.name(), - if it.expr().is_some() { HasDefault::Yes } else { HasDefault::No }, - ) - }) - .map(|(it, has_default)| { - name_to_token(call_site, tm, it).map(|ident| (ident, has_default)) - }) + .map(|it| it.name()) + .map(|it| name_to_token(call_site, tm, it)) .collect::<Result<_, _>>()?, ), Some(FieldList::TupleFieldList(it)) => VariantShape::Tuple(it.fields().count()), @@ -671,7 +601,7 @@ fn default_expand( let body = match &adt.shape { AdtShape::Struct(fields) => { let name = &adt.name; - fields.default_expand( + fields.as_pattern_map( quote!(span =>#name), span, |_| quote!(span =>#krate::default::Default::default()), @@ -681,7 +611,7 @@ fn default_expand( if let Some(d) = default_variant { let (name, fields) = &variants[*d]; let adt_name = &adt.name; - fields.default_expand( + fields.as_pattern_map( quote!(span =>#adt_name :: #name), span, |_| quote!(span =>#krate::default::Default::default()), @@ -713,7 +643,7 @@ fn debug_expand( expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { - let for_fields = fields.iter().map(|(it, _)| { + let for_fields = fields.iter().map(|it| { let x_string = it.to_string(); quote! {span => .field(#x_string, & #it) 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 2aca99c93bad4..ac849b0762d7f 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 @@ -35,8 +35,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafe DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) | DefWithBodyId::VariantId(_) - | DefWithBodyId::InTypeConstId(_) - | DefWithBodyId::FieldId(_) => false, + | DefWithBodyId::InTypeConstId(_) => false, }; let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() }; 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 1b2ef2aef3ff1..3c258e3c4cf4b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -134,9 +134,6 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer .unwrap() .0; } - DefWithBodyId::FieldId(f) => { - ctx.collect_field(f); - } } ctx.infer_body(); @@ -913,19 +910,6 @@ impl<'a> InferenceContext<'a> { self.return_ty = return_ty; } - fn collect_field(&mut self, field: FieldId) { - let variant_data = field.parent.variant_data(self.db.upcast()); - let field_data = &variant_data.fields()[field.local_id]; - let types_map = variant_data.types_map(); - let return_ty = - self.make_ty(field_data.type_ref, types_map, InferenceTyDiagnosticSource::Signature); - - // Field default value exprs might be defining usage sites of TAITs. - self.make_tait_coercion_table(iter::once(&return_ty)); - - self.return_ty = return_ty; - } - fn collect_fn(&mut self, func: FunctionId) { let data = self.db.function_data(func); let mut param_tys = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 23072011a7b36..cc6ed122af4bb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2130,10 +2130,6 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi db.enum_variant_data(it).name.display(db.upcast(), edition).to_string() } DefWithBodyId::InTypeConstId(it) => format!("in type const {it:?}"), - DefWithBodyId::FieldId(it) => it.parent.variant_data(db.upcast()).fields()[it.local_id] - .name - .display(db.upcast(), edition) - .to_string(), }; let _p = tracing::info_span!("mir_body_query", ?detail).entered(); let body = db.body(def); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 193b7bcd97c2a..2a26101ac439f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -6,7 +6,7 @@ use std::{ }; use either::Either; -use hir_def::{expr_store::Body, hir::BindingId, VariantId}; +use hir_def::{expr_store::Body, hir::BindingId}; use hir_expand::{name::Name, Lookup}; use la_arena::ArenaMap; use span::Edition; @@ -79,38 +79,6 @@ impl MirBody { hir_def::DefWithBodyId::InTypeConstId(id) => { w!(this, "in type const {id:?} = "); } - hir_def::DefWithBodyId::FieldId(id) => { - w!(this, "field "); - match id.parent { - VariantId::EnumVariantId(it) => { - let loc = it.lookup(db.upcast()); - let enum_loc = loc.parent.lookup(db.upcast()); - w!( - this, - "{}::{}", - enum_loc.id.item_tree(db.upcast())[enum_loc.id.value] - .name - .display(db.upcast(), Edition::LATEST), - loc.id.item_tree(db.upcast())[loc.id.value] - .name - .display(db.upcast(), Edition::LATEST), - ); - } - VariantId::StructId(id) => { - id.lookup(db.upcast()).id.resolved(db.upcast(), |it| { - w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST)); - }); - } - VariantId::UnionId(id) => { - id.lookup(db.upcast()).id.resolved(db.upcast(), |it| { - w!(this, "{}", it.name.display(db.upcast(), Edition::LATEST)); - }); - } - }; - let variant_data = id.parent.variant_data(db.upcast()); - let field_name = &variant_data.fields()[id.local_id].name; - w!(this, ".{}: _ = ", field_name.display(db.upcast(), Edition::LATEST)); - } }); ctx.result } 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 a5af712b42f28..69ec35f406df4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -160,7 +160,6 @@ fn check_impl( loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(_) => unreachable!(), }); let mut unexpected_type_mismatches = String::new(); for def in defs { @@ -416,7 +415,6 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { loc.source(&db).value.syntax().text_range().start() } DefWithBodyId::InTypeConstId(it) => it.source(&db).syntax().text_range().start(), - DefWithBodyId::FieldId(_) => unreachable!(), }); for def in defs { let (body, source_map) = db.body_with_source_map(def); 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 dd26e894d7932..537401afdc34a 100644 --- a/src/tools/rust-analyzer/crates/hir/src/from_id.rs +++ b/src/tools/rust-analyzer/crates/hir/src/from_id.rs @@ -147,7 +147,6 @@ impl From<DefWithBody> for DefWithBodyId { DefWithBody::Const(it) => DefWithBodyId::ConstId(it.id), DefWithBody::Variant(it) => DefWithBodyId::VariantId(it.into()), DefWithBody::InTypeConst(it) => DefWithBodyId::InTypeConstId(it.id), - DefWithBody::Field(it) => DefWithBodyId::FieldId(it.into()), } } } @@ -160,7 +159,6 @@ impl From<DefWithBodyId> for DefWithBody { DefWithBodyId::ConstId(it) => DefWithBody::Const(it.into()), DefWithBodyId::VariantId(it) => DefWithBody::Variant(it.into()), DefWithBodyId::InTypeConstId(it) => DefWithBody::InTypeConst(it.into()), - DefWithBodyId::FieldId(it) => DefWithBody::Field(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 ff9832d310196..56090bc6b6057 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -415,19 +415,6 @@ impl ModuleDef { def.diagnostics(db, &mut acc); } - let fields = match self { - ModuleDef::Adt(Adt::Struct(it)) => Some(it.fields(db)), - ModuleDef::Adt(Adt::Union(it)) => Some(it.fields(db)), - ModuleDef::Variant(it) => Some(it.fields(db)), - _ => None, - }; - if let Some(fields) = fields { - for field in fields { - let def: DefWithBody = field.into(); - def.diagnostics(db, &mut acc, style_lints); - } - } - acc } @@ -1239,12 +1226,6 @@ impl HasVisibility for Module { } } -impl From<&Field> for DefWithBodyId { - fn from(&f: &Field) -> Self { - DefWithBodyId::FieldId(f.into()) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Field { pub(crate) parent: VariantDef, @@ -1310,10 +1291,6 @@ impl AstNode for FieldSource { } impl Field { - pub fn module(self, db: &dyn HirDatabase) -> Module { - self.parent.module(db) - } - pub fn name(&self, db: &dyn HirDatabase) -> Name { self.parent.variant_data(db).fields()[self.id].name.clone() } @@ -1376,14 +1353,6 @@ impl Field { pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { self.parent } - - pub fn default_value_source( - &self, - db: &dyn HirDatabase, - ) -> Option<InFileWrapper<HirFileId, ast::Expr>> { - let id: hir_def::FieldId = (*self).into(); - id.record_field_source(db.upcast()).map(|it| it.and_then(|it| it.expr())).transpose() - } } impl HasVisibility for Field { @@ -1820,9 +1789,8 @@ pub enum DefWithBody { Const(Const), Variant(Variant), InTypeConst(InTypeConst), - Field(Field), } -impl_from!(Function, Const, Static, Variant, InTypeConst, Field for DefWithBody); +impl_from!(Function, Const, Static, Variant, InTypeConst for DefWithBody); impl DefWithBody { pub fn module(self, db: &dyn HirDatabase) -> Module { @@ -1832,7 +1800,6 @@ impl DefWithBody { DefWithBody::Static(s) => s.module(db), DefWithBody::Variant(v) => v.module(db), DefWithBody::InTypeConst(c) => c.module(db), - DefWithBody::Field(f) => f.module(db), } } @@ -1843,7 +1810,6 @@ impl DefWithBody { DefWithBody::Const(c) => c.name(db), DefWithBody::Variant(v) => Some(v.name(db)), DefWithBody::InTypeConst(_) => None, - DefWithBody::Field(f) => Some(f.name(db)), } } @@ -1859,7 +1825,6 @@ impl DefWithBody { &DefWithBodyId::from(it.id).resolver(db.upcast()), TyKind::Error.intern(Interner), ), - DefWithBody::Field(it) => it.ty(db), } } @@ -1870,7 +1835,6 @@ impl DefWithBody { DefWithBody::Const(it) => it.id.into(), DefWithBody::Variant(it) => it.into(), DefWithBody::InTypeConst(it) => it.id.into(), - DefWithBody::Field(it) => it.into(), } } @@ -1916,23 +1880,6 @@ impl DefWithBody { item_tree_source_maps.konst(konst.value) } DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => &TypesSourceMap::EMPTY, - DefWithBody::Field(field) => match field.parent { - VariantDef::Struct(strukt) => { - let strukt = strukt.id.lookup(db.upcast()).id; - item_tree_source_maps = strukt.item_tree_with_source_map(db.upcast()).1; - item_tree_source_maps.strukt(strukt.value).item() - } - VariantDef::Union(union) => { - let union = union.id.lookup(db.upcast()).id; - item_tree_source_maps = union.item_tree_with_source_map(db.upcast()).1; - item_tree_source_maps.union(union.value).item() - } - VariantDef::Variant(variant) => { - let variant = variant.id.lookup(db.upcast()).id; - item_tree_source_maps = variant.item_tree_with_source_map(db.upcast()).1; - item_tree_source_maps.variant(variant.value) - } - }, }; for (_, def_map) in body.blocks(db.upcast()) { @@ -2188,8 +2135,8 @@ impl DefWithBody { DefWithBody::Static(it) => it.into(), DefWithBody::Const(it) => it.into(), DefWithBody::Variant(it) => it.into(), - // FIXME: don't ignore diagnostics for in type const and default field value exprs - DefWithBody::InTypeConst(_) | DefWithBody::Field(_) => return, + // FIXME: don't ignore diagnostics for in type const + DefWithBody::InTypeConst(_) => return, }; for diag in hir_ty::diagnostics::incorrect_case(db, def.into()) { acc.push(diag.into()) @@ -3322,10 +3269,7 @@ impl AsAssocItem for DefWithBody { match self { DefWithBody::Function(it) => it.as_assoc_item(db), DefWithBody::Const(it) => it.as_assoc_item(db), - DefWithBody::Static(_) - | DefWithBody::Variant(_) - | DefWithBody::InTypeConst(_) - | DefWithBody::Field(_) => None, + DefWithBody::Static(_) | DefWithBody::Variant(_) | DefWithBody::InTypeConst(_) => None, } } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 6925880ba996c..bad536080567f 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -972,7 +972,6 @@ impl TryFrom<DefWithBody> for Definition { DefWithBody::Const(it) => Ok(it.into()), DefWithBody::Variant(it) => Ok(it.into()), DefWithBody::InTypeConst(_) => Err(()), - DefWithBody::Field(it) => Ok(it.into()), } } } 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 d2a237a5c003b..7963e8ae4f78c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -310,9 +310,6 @@ impl Definition { DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), // FIXME: implement DefWithBody::InTypeConst(_) => return SearchScope::empty(), - DefWithBody::Field(f) => { - f.default_value_source(db).map(|src| src.syntax().cloned()) - } }; return match def { Some(def) => SearchScope::file_range( @@ -330,9 +327,6 @@ impl Definition { DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), // FIXME: implement DefWithBody::InTypeConst(_) => return SearchScope::empty(), - DefWithBody::Field(f) => { - f.default_value_source(db).map(|src| src.syntax().cloned()) - } }; return match def { Some(def) => SearchScope::file_range( 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 4080b5f12e59a..73dcbc13b79a1 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 @@ -1232,21 +1232,6 @@ fn f() { let (_, _, _, ..) = (true, 42); // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown}) } -"#, - ); - } - - #[test] - fn diagnostics_inside_field_default_expr() { - check_diagnostics( - r#" -struct Foo { - foo: i32 = { - let x = false; - x - // ^ error: expected i32, found bool - }, -} "#, ); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index fe1316c9bfde3..389c01933c999 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -678,8 +678,6 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike // S { x }; // S { x, y: 32, }; // S { x, y: 32, ..Default::default() }; -// S { x, y: 32, .. }; -// S { .. }; // S { x: ::default() }; // TupleStruct { 0: 1 }; // } @@ -711,8 +709,6 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // fn main() { // S { field ..S::default() } // S { 0 ..S::default() } - // S { field .. } - // S { 0 .. } // } name_ref_or_index(p); p.error("expected `:`"); @@ -743,13 +739,7 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { .. } = S {}; // } - // test struct_initializer_with_defaults - // fn foo() { - // let _s = S { .. }; - // } - - // We permit `.. }` on the left-hand side of a destructuring assignment - // or defaults values. + // We permit `.. }` on the left-hand side of a destructuring assignment. if !p.at(T!['}']) { expr(p); @@ -760,12 +750,6 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { ..x, a: 0 } // } - // test_err comma_after_default_values_syntax - // fn foo() { - // S { .., }; - // S { .., a: 0 } - // } - // Do not bump, so we can support additional fields after this comma. p.error("cannot use a comma after the base struct"); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index 9a16c9db6daf1..21078175c0ec0 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -135,11 +135,6 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { name(p); p.expect(T![:]); types::type_(p); - // test record_field_default_values - // struct S { f: f32 = 0.0 } - if p.eat(T![=]) { - expressions::expr(p); - } m.complete(p, RECORD_FIELD); } else { m.abandon(p); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index c8ea8c547a98b..b9f87b6af2421 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -482,10 +482,6 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs"); } #[test] - fn record_field_default_values() { - run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs"); - } - #[test] fn record_field_list() { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs"); } @@ -548,10 +544,6 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs"); } #[test] - fn struct_initializer_with_defaults() { - run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs"); - } - #[test] fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); } #[test] fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); } @@ -727,10 +719,6 @@ mod err { ); } #[test] - fn comma_after_default_values_syntax() { - run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs"); - } - #[test] fn crate_visibility_empty_recover() { run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast deleted file mode 100644 index feb617e1aa2ab..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast +++ /dev/null @@ -1,59 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - DOT2 ".." - ERROR - COMMA "," - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - DOT2 ".." - ERROR - COMMA "," - WHITESPACE " " - RECORD_EXPR_FIELD - NAME_REF - IDENT "a" - COLON ":" - WHITESPACE " " - LITERAL - INT_NUMBER "0" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" -error 21: expected expression -error 36: expected expression -error 37: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs deleted file mode 100644 index f1ecdf89fab17..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn foo() { - S { .., }; - S { .., a: 0 } -} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast index 12b4e233e3044..08ae906421c30 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast @@ -44,56 +44,6 @@ SOURCE_FILE WHITESPACE " " R_CURLY "}" WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - RECORD_EXPR_FIELD - NAME_REF - INT_NUMBER "0" - WHITESPACE " " - DOT2 ".." - CALL_EXPR - PATH_EXPR - PATH - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "default" - ARG_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - RECORD_EXPR_FIELD - NAME_REF - IDENT "field" - WHITESPACE " " - DOT2 ".." - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n " RECORD_EXPR PATH PATH_SEGMENT @@ -108,6 +58,20 @@ SOURCE_FILE INT_NUMBER "0" WHITESPACE " " DOT2 ".." + CALL_EXPR + PATH_EXPR + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" @@ -118,9 +82,3 @@ error 25: expected COMMA error 42: expected SEMICOLON error 52: expected `:` error 52: expected COMMA -error 69: expected SEMICOLON -error 83: expected `:` -error 83: expected COMMA -error 88: expected SEMICOLON -error 98: expected `:` -error 98: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs index 416cd763fdb56..65398ccb88e5d 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs @@ -1,6 +1,4 @@ fn main() { S { field ..S::default() } S { 0 ..S::default() } - S { field .. } - S { 0 .. } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast deleted file mode 100644 index 33088f2cabf39..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast +++ /dev/null @@ -1,28 +0,0 @@ -SOURCE_FILE - STRUCT - STRUCT_KW "struct" - WHITESPACE " " - NAME - IDENT "S" - WHITESPACE " " - RECORD_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - RECORD_FIELD - NAME - IDENT "f" - COLON ":" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "f32" - WHITESPACE " " - EQ "=" - WHITESPACE " " - LITERAL - FLOAT_NUMBER "0.0" - WHITESPACE " " - R_CURLY "}" - WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs deleted file mode 100644 index d7b38944a8aa8..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs +++ /dev/null @@ -1 +0,0 @@ -struct S { f: f32 = 0.0 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast index b868da55bcea7..00948c322f4c9 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast @@ -120,53 +120,6 @@ SOURCE_FILE R_CURLY "}" SEMICOLON ";" WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - RECORD_EXPR_FIELD - PATH_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "x" - COMMA "," - WHITESPACE " " - RECORD_EXPR_FIELD - NAME_REF - IDENT "y" - COLON ":" - WHITESPACE " " - LITERAL - INT_NUMBER "32" - COMMA "," - WHITESPACE " " - DOT2 ".." - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " - EXPR_STMT - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - DOT2 ".." - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n " EXPR_STMT RECORD_EXPR PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs index 42895f759b2be..86411fbb7dc05 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs @@ -3,8 +3,6 @@ fn foo() { S { x }; S { x, y: 32, }; S { x, y: 32, ..Default::default() }; - S { x, y: 32, .. }; - S { .. }; S { x: ::default() }; TupleStruct { 0: 1 }; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast deleted file mode 100644 index 987e219ae822b..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast +++ /dev/null @@ -1,39 +0,0 @@ -SOURCE_FILE - FN - FN_KW "fn" - WHITESPACE " " - NAME - IDENT "foo" - PARAM_LIST - L_PAREN "(" - R_PAREN ")" - WHITESPACE " " - BLOCK_EXPR - STMT_LIST - L_CURLY "{" - WHITESPACE "\n " - LET_STMT - LET_KW "let" - WHITESPACE " " - IDENT_PAT - NAME - IDENT "_s" - WHITESPACE " " - EQ "=" - WHITESPACE " " - RECORD_EXPR - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - WHITESPACE " " - RECORD_EXPR_FIELD_LIST - L_CURLY "{" - WHITESPACE " " - DOT2 ".." - WHITESPACE " " - R_CURLY "}" - SEMICOLON ";" - WHITESPACE "\n" - R_CURLY "}" - WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs deleted file mode 100644 index e08204f94c4b4..0000000000000 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn foo() { - let _s = S { .. }; -} 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 824f262ca3635..cd709afe091f3 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 @@ -673,9 +673,6 @@ impl flags::AnalysisStats { DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::InTypeConst(_) => unimplemented!(), - DefWithBody::Field(it) => { - it.default_value_source(db).map(|it| it.syntax().cloned()) - } }; if let Some(src) = source { let original_file = src.file_id.original_file(db); @@ -990,9 +987,6 @@ impl flags::AnalysisStats { DefWithBody::Const(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::Variant(it) => it.source(db).map(|it| it.syntax().cloned()), DefWithBody::InTypeConst(_) => unimplemented!(), - DefWithBody::Field(it) => { - it.default_value_source(db).map(|it| it.syntax().cloned()) - } }; if let Some(src) = source { let original_file = src.file_id.original_file(db); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index bbb8413cbc080..4e2a70d6cd917 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -241,7 +241,7 @@ RecordFieldList = RecordField = Attr* Visibility? - Name ':' Type ('=' Expr)? + Name ':' Type TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 8f10ea94645d6..69e2a9f9c1b22 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1538,14 +1538,10 @@ impl ast::HasDocComments for RecordField {} impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { - #[inline] - pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } - #[inline] - pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] From 1eb9f5e825ad2c23a3a464f865b93a7cf1150a9b Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Mon, 27 Jan 2025 22:16:45 -0600 Subject: [PATCH 36/75] Fix scip inherent impl overflow --- .../crates/rust-analyzer/src/cli/scip.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 dc0f722aae6a0..22bf0075ad5d7 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 @@ -444,14 +444,14 @@ impl SymbolGenerator { MonikerResult::Moniker(moniker) => TokenSymbols { symbol: scip::symbol::format_symbol(moniker_to_symbol(moniker)), enclosing_symbol: None, - is_inherent_impl: moniker - .identifier - .description - .get(moniker.identifier.description.len() - 2) - .is_some_and(|descriptor| { + is_inherent_impl: match &moniker.identifier.description[..] { + // inherent impls are represented as impl#[SelfType] + [.., descriptor, _] => { descriptor.desc == MonikerDescriptorKind::Type && descriptor.name == "impl" - }), + } + _ => false, + }, }, MonikerResult::Local { enclosing_moniker } => { let local_symbol = scip::types::Symbol::new_local(local_count); From 8e68c3fc623d94031c14019bee22fcab76edfdea Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Tue, 28 Jan 2025 14:34:47 +0100 Subject: [PATCH 37/75] Disable `Receiver` based autoderef temporarily --- src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs | 6 +++++- .../crates/hir-ty/src/tests/method_resolution.rs | 4 ++-- .../crates/ide-completion/src/completions/dot.rs | 4 +--- 3 files changed, 8 insertions(+), 6 deletions(-) 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 62feca5f8cbbf..e0e366f450198 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -194,7 +194,11 @@ pub(crate) fn deref_by_trait( } let trait_id = || { - if use_receiver_trait { + // FIXME: Remove the `false` once `Receiver` needs to be stabilized, doing so will + // effectively bump the MSRV of rust-analyzer to 1.84 due to 1.83 and below lacking the + // blanked impl on `Deref`. + #[expect(clippy::overly_complex_bool_expr)] + if use_receiver_trait && false { if let Some(receiver) = db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) { 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 8866de22dfb99..e5f791ea6ffcd 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 @@ -2163,9 +2163,9 @@ impl Receiver for Bar { fn main() { let bar = Bar; let _v1 = bar.foo1(); - //^^^ type: i32 + //^^^ type: {unknown} let _v2 = bar.foo2(); - //^^^ type: bool + //^^^ type: {unknown} } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 0557265f235c7..b38b9ac1f5391 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1500,9 +1500,7 @@ fn main() { bar.$0 } "#, - expect![[r#" - me foo() fn(self: Bar) -"#]], + expect![[r#""#]], ); } From 0a2221dcd6cf0f1e388f05a10e7c21514361adc5 Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Mon, 27 Jan 2025 23:10:05 -0600 Subject: [PATCH 38/75] Fix scip `crate` symbol name --- .../rust-analyzer/crates/ide/src/moniker.rs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index d97c12ebafb38..66ea49a98a088 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -289,7 +289,10 @@ fn def_to_non_local_moniker( definition: Definition, from_crate: Crate, ) -> Option<Moniker> { - let module = definition.module(db)?; + let module = match definition { + Definition::Module(module) if module.is_crate_root() => module, + _ => definition.module(db)?, + }; let krate = module.krate(); let edition = krate.edition(db); @@ -322,12 +325,18 @@ fn def_to_non_local_moniker( name: name.display(db, edition).to_string(), desc: def_to_kind(db, def).into(), }); - } else if reverse_description.is_empty() { - // Don't allow the last descriptor to be absent. - return None; } else { match def { - Definition::Module(module) if module.is_crate_root() => {} + Definition::Module(module) if module.is_crate_root() => { + // only include `crate` namespace by itself because we prefer + // `rust-analyzer cargo foo . bar/` over `rust-analyzer cargo foo . crate/bar/` + if reverse_description.is_empty() { + reverse_description.push(MonikerDescriptor { + name: "crate".to_owned(), + desc: MonikerDescriptorKind::Namespace, + }); + } + } _ => { tracing::error!(?def, "Encountered enclosing definition with no name"); } @@ -340,6 +349,9 @@ fn def_to_non_local_moniker( }; def = next_def; } + if reverse_description.is_empty() { + return None; + } reverse_description.reverse(); let description = reverse_description; From d417284b6a4b86f682d474fd8cd11e92651c312b Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Mon, 27 Jan 2025 23:14:26 -0600 Subject: [PATCH 39/75] Fix scip symbols for implicit modules --- .../crates/ide/src/static_index.rs | 37 +++++++++++++------ .../crates/rust-analyzer/src/cli/scip.rs | 6 ++- 2 files changed, 30 insertions(+), 13 deletions(-) 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 8050a38b3ca13..3121bb7a8e31e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -169,10 +169,10 @@ impl StaticIndex<'_> { .unwrap(); // hovers let sema = hir::Semantics::new(self.db); - let tokens_or_nodes = sema.parse_guess_edition(file_id).syntax().clone(); + let root = sema.parse_guess_edition(file_id).syntax().clone(); let edition = sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); - let tokens = tokens_or_nodes.descendants_with_tokens().filter_map(|it| match it { + let tokens = root.descendants_with_tokens().filter_map(|it| match it { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(it) => Some(it), }); @@ -194,24 +194,19 @@ impl StaticIndex<'_> { ) }); let mut result = StaticIndexedFile { file_id, inlay_hints, folds, tokens: vec![] }; - for token in tokens { - let range = token.text_range(); - let node = token.parent().unwrap(); - let def = match get_definition(&sema, token.clone()) { - Some(it) => it, - None => continue, - }; + + let mut add_token = |def: Definition, range: TextRange, scope_node: &SyntaxNode| { let id = if let Some(it) = self.def_map.get(&def) { *it } else { let it = self.tokens.insert(TokenStaticData { - documentation: documentation_for_definition(&sema, def, &node), + documentation: documentation_for_definition(&sema, def, scope_node), hover: Some(hover_for_definition( &sema, file_id, def, None, - &node, + scope_node, None, false, &hover_config, @@ -240,6 +235,22 @@ impl StaticIndex<'_> { }, }); result.tokens.push((range, id)); + }; + + if let Some(module) = sema.file_to_module_def(file_id) { + let def = Definition::Module(module); + let range = root.text_range(); + add_token(def, range, &root); + } + + for token in tokens { + let range = token.text_range(); + let node = token.parent().unwrap(); + let def = match get_definition(&sema, token.clone()) { + Some(it) => it, + None => continue, + }; + add_token(def, range, &node); } self.files.push(result); } @@ -300,6 +311,10 @@ mod tests { let mut range_set: FxHashSet<_> = ranges.iter().map(|it| it.0).collect(); for f in s.files { for (range, _) in f.tokens { + if range.start() == TextSize::from(0) { + // ignore whole file range corresponding to module definition + continue; + } let it = FileRange { file_id: f.file_id, range }; if !range_set.contains(&it) { panic!("additional range {it:?}"); 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 22bf0075ad5d7..ee7b6989751e3 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 @@ -549,7 +549,9 @@ mod test { continue; } for &(range, id) in &file.tokens { - if range.contains(offset - TextSize::from(1)) { + // check if cursor is within token, ignoring token for the module defined by the file (whose range is the whole file) + if range.start() != TextSize::from(0) && range.contains(offset - TextSize::from(1)) + { let token = si.tokens.get(id).unwrap(); found_symbol = match token.moniker.as_ref() { None => None, @@ -885,7 +887,7 @@ pub mod example_mod { ); let file = si.files.first().unwrap(); - let (_, token_id) = file.tokens.first().unwrap(); + let (_, token_id) = file.tokens.get(1).unwrap(); // first token is file module, second is `bar` let token = si.tokens.get(*token_id).unwrap(); assert_eq!(token.documentation.as_ref().map(|d| d.as_str()), Some("foo")); From f10b62211944a19c72576b5864d3f043393da716 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Tue, 28 Jan 2025 11:30:47 -0500 Subject: [PATCH 40/75] feat: Implement `default-field-values` only in parser --- .../crates/parser/src/grammar/expressions.rs | 18 ++++- .../crates/parser/src/grammar/items/adt.rs | 5 ++ .../parser/test_data/generated/runner.rs | 12 ++++ .../comma_after_default_values_syntax.rast | 59 ++++++++++++++++ .../err/comma_after_default_values_syntax.rs | 4 ++ ...cord_literal_before_ellipsis_recovery.rast | 70 +++++++++++++++---- ...record_literal_before_ellipsis_recovery.rs | 2 + .../ok/record_field_default_values.rast | 28 ++++++++ .../inline/ok/record_field_default_values.rs | 1 + .../parser/inline/ok/record_lit.rast | 47 +++++++++++++ .../test_data/parser/inline/ok/record_lit.rs | 2 + .../ok/struct_initializer_with_defaults.rast | 39 +++++++++++ .../ok/struct_initializer_with_defaults.rs | 3 + .../rust-analyzer/crates/syntax/rust.ungram | 2 +- .../crates/syntax/src/ast/generated/nodes.rs | 4 ++ 15 files changed, 280 insertions(+), 16 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs index 389c01933c999..fe1316c9bfde3 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs @@ -678,6 +678,8 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike // S { x }; // S { x, y: 32, }; // S { x, y: 32, ..Default::default() }; +// S { x, y: 32, .. }; +// S { .. }; // S { x: ::default() }; // TupleStruct { 0: 1 }; // } @@ -709,6 +711,8 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // fn main() { // S { field ..S::default() } // S { 0 ..S::default() } + // S { field .. } + // S { 0 .. } // } name_ref_or_index(p); p.error("expected `:`"); @@ -739,7 +743,13 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { .. } = S {}; // } - // We permit `.. }` on the left-hand side of a destructuring assignment. + // test struct_initializer_with_defaults + // fn foo() { + // let _s = S { .. }; + // } + + // We permit `.. }` on the left-hand side of a destructuring assignment + // or defaults values. if !p.at(T!['}']) { expr(p); @@ -750,6 +760,12 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { // S { ..x, a: 0 } // } + // test_err comma_after_default_values_syntax + // fn foo() { + // S { .., }; + // S { .., a: 0 } + // } + // Do not bump, so we can support additional fields after this comma. p.error("cannot use a comma after the base struct"); } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index 21078175c0ec0..9a16c9db6daf1 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -135,6 +135,11 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { name(p); p.expect(T![:]); types::type_(p); + // test record_field_default_values + // struct S { f: f32 = 0.0 } + if p.eat(T![=]) { + expressions::expr(p); + } m.complete(p, RECORD_FIELD); } else { m.abandon(p); diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index b9f87b6af2421..c8ea8c547a98b 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -482,6 +482,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_attrs.rs"); } #[test] + fn record_field_default_values() { + run_and_expect_no_errors("test_data/parser/inline/ok/record_field_default_values.rs"); + } + #[test] fn record_field_list() { run_and_expect_no_errors("test_data/parser/inline/ok/record_field_list.rs"); } @@ -544,6 +548,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/stmt_postfix_expr_ambiguity.rs"); } #[test] + fn struct_initializer_with_defaults() { + run_and_expect_no_errors("test_data/parser/inline/ok/struct_initializer_with_defaults.rs"); + } + #[test] fn struct_item() { run_and_expect_no_errors("test_data/parser/inline/ok/struct_item.rs"); } #[test] fn trait_alias() { run_and_expect_no_errors("test_data/parser/inline/ok/trait_alias.rs"); } @@ -719,6 +727,10 @@ mod err { ); } #[test] + fn comma_after_default_values_syntax() { + run_and_expect_errors("test_data/parser/inline/err/comma_after_default_values_syntax.rs"); + } + #[test] fn crate_visibility_empty_recover() { run_and_expect_errors("test_data/parser/inline/err/crate_visibility_empty_recover.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast new file mode 100644 index 0000000000000..feb617e1aa2ab --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rast @@ -0,0 +1,59 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + ERROR + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 21: expected expression +error 36: expected expression +error 37: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs new file mode 100644 index 0000000000000..f1ecdf89fab17 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/comma_after_default_values_syntax.rs @@ -0,0 +1,4 @@ +fn foo() { + S { .., }; + S { .., a: 0 } +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast index 08ae906421c30..12b4e233e3044 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rast @@ -44,6 +44,56 @@ SOURCE_FILE WHITESPACE " " R_CURLY "}" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + INT_NUMBER "0" + WHITESPACE " " + DOT2 ".." + CALL_EXPR + PATH_EXPR + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "field" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n " RECORD_EXPR PATH PATH_SEGMENT @@ -58,20 +108,6 @@ SOURCE_FILE INT_NUMBER "0" WHITESPACE " " DOT2 ".." - CALL_EXPR - PATH_EXPR - PATH - PATH - PATH_SEGMENT - NAME_REF - IDENT "S" - COLON2 "::" - PATH_SEGMENT - NAME_REF - IDENT "default" - ARG_LIST - L_PAREN "(" - R_PAREN ")" WHITESPACE " " R_CURLY "}" WHITESPACE "\n" @@ -82,3 +118,9 @@ error 25: expected COMMA error 42: expected SEMICOLON error 52: expected `:` error 52: expected COMMA +error 69: expected SEMICOLON +error 83: expected `:` +error 83: expected COMMA +error 88: expected SEMICOLON +error 98: expected `:` +error 98: expected COMMA diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs index 65398ccb88e5d..416cd763fdb56 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/record_literal_before_ellipsis_recovery.rs @@ -1,4 +1,6 @@ fn main() { S { field ..S::default() } S { 0 ..S::default() } + S { field .. } + S { 0 .. } } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast new file mode 100644 index 0000000000000..33088f2cabf39 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rast @@ -0,0 +1,28 @@ +SOURCE_FILE + STRUCT + STRUCT_KW "struct" + WHITESPACE " " + NAME + IDENT "S" + WHITESPACE " " + RECORD_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_FIELD + NAME + IDENT "f" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "f32" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + FLOAT_NUMBER "0.0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs new file mode 100644 index 0000000000000..d7b38944a8aa8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_field_default_values.rs @@ -0,0 +1 @@ +struct S { f: f32 = 0.0 } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast index 00948c322f4c9..b868da55bcea7 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rast @@ -120,6 +120,53 @@ SOURCE_FILE R_CURLY "}" SEMICOLON ";" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "y" + COLON ":" + WHITESPACE " " + LITERAL + INT_NUMBER "32" + COMMA "," + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " EXPR_STMT RECORD_EXPR PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs index 86411fbb7dc05..42895f759b2be 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/record_lit.rs @@ -3,6 +3,8 @@ fn foo() { S { x }; S { x, y: 32, }; S { x, y: 32, ..Default::default() }; + S { x, y: 32, .. }; + S { .. }; S { x: ::default() }; TupleStruct { 0: 1 }; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast new file mode 100644 index 0000000000000..987e219ae822b --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rast @@ -0,0 +1,39 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + IDENT_PAT + NAME + IDENT "_s" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs new file mode 100644 index 0000000000000..e08204f94c4b4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/struct_initializer_with_defaults.rs @@ -0,0 +1,3 @@ +fn foo() { + let _s = S { .. }; +} diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 4e2a70d6cd917..bbb8413cbc080 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -241,7 +241,7 @@ RecordFieldList = RecordField = Attr* Visibility? - Name ':' Type + Name ':' Type ('=' Expr)? TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 69e2a9f9c1b22..8f10ea94645d6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -1538,10 +1538,14 @@ impl ast::HasDocComments for RecordField {} impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { + #[inline] + pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) } #[inline] pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } + #[inline] + pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] From 19afce0c9cbf47768918145112ab5186367a5693 Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Tue, 28 Jan 2025 10:44:32 -0600 Subject: [PATCH 41/75] Update lsif_contains_generated_constant --- .../rust-analyzer/tests/slow-tests/cli.rs | 156 +++++++++--------- 1 file changed, 80 insertions(+), 76 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs index fba5466691289..4ef930e9854ea 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/cli.rs @@ -43,89 +43,93 @@ mod tests { expect![[r#" {"id":2,"type":"vertex","label":"foldingRangeResult","result":[{"startLine":2,"startCharacter":43,"endLine":6,"endCharacter":1},{"startLine":3,"startCharacter":19,"endLine":5,"endCharacter":5},{"startLine":9,"startCharacter":10,"endLine":12,"endCharacter":1}]} {"id":3,"type":"edge","label":"textDocument/foldingRange","inV":2,"outV":1} - {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}} + {"id":4,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}} {"id":5,"type":"vertex","label":"resultSet"} {"id":6,"type":"edge","label":"next","inV":5,"outV":4} - {"id":7,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}} + {"id":7,"type":"vertex","label":"range","start":{"line":0,"character":3},"end":{"line":0,"character":8}} {"id":8,"type":"vertex","label":"resultSet"} {"id":9,"type":"edge","label":"next","inV":8,"outV":7} - {"id":10,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}} - {"id":11,"type":"edge","label":"next","inV":8,"outV":10} - {"id":12,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}} - {"id":13,"type":"vertex","label":"resultSet"} - {"id":14,"type":"edge","label":"next","inV":13,"outV":12} - {"id":15,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}} + {"id":10,"type":"vertex","label":"range","start":{"line":2,"character":13},"end":{"line":2,"character":43}} + {"id":11,"type":"vertex","label":"resultSet"} + {"id":12,"type":"edge","label":"next","inV":11,"outV":10} + {"id":13,"type":"vertex","label":"range","start":{"line":8,"character":0},"end":{"line":8,"character":30}} + {"id":14,"type":"edge","label":"next","inV":11,"outV":13} + {"id":15,"type":"vertex","label":"range","start":{"line":8,"character":32},"end":{"line":8,"character":39}} {"id":16,"type":"vertex","label":"resultSet"} {"id":17,"type":"edge","label":"next","inV":16,"outV":15} - {"id":18,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}} + {"id":18,"type":"vertex","label":"range","start":{"line":9,"character":4},"end":{"line":9,"character":9}} {"id":19,"type":"vertex","label":"resultSet"} {"id":20,"type":"edge","label":"next","inV":19,"outV":18} - {"id":21,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}} - {"id":22,"type":"edge","label":"next","inV":8,"outV":21} - {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}} - {"id":24,"type":"vertex","label":"resultSet"} - {"id":25,"type":"edge","label":"next","inV":24,"outV":23} - {"id":26,"type":"edge","label":"contains","inVs":[4,7,10,12,15,18,21,23],"outV":1} - {"id":27,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}} - {"id":28,"type":"edge","label":"textDocument/hover","inV":27,"outV":5} - {"id":29,"type":"vertex","label":"referenceResult"} - {"id":30,"type":"edge","label":"textDocument/references","inV":29,"outV":5} - {"id":31,"type":"edge","label":"item","document":1,"property":"references","inVs":[4],"outV":29} - {"id":32,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}} - {"id":33,"type":"edge","label":"textDocument/hover","inV":32,"outV":8} - {"id":34,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"} - {"id":35,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"} - {"id":36,"type":"edge","label":"packageInformation","inV":34,"outV":35} - {"id":37,"type":"edge","label":"moniker","inV":35,"outV":8} - {"id":38,"type":"vertex","label":"definitionResult"} - {"id":39,"type":"edge","label":"item","document":1,"inVs":[7],"outV":38} - {"id":40,"type":"edge","label":"textDocument/definition","inV":38,"outV":8} - {"id":41,"type":"vertex","label":"referenceResult"} - {"id":42,"type":"edge","label":"textDocument/references","inV":41,"outV":8} - {"id":43,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[7],"outV":41} - {"id":44,"type":"edge","label":"item","document":1,"property":"references","inVs":[10,21],"outV":41} - {"id":45,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}} - {"id":46,"type":"edge","label":"textDocument/hover","inV":45,"outV":13} - {"id":47,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"} - {"id":48,"type":"edge","label":"packageInformation","inV":34,"outV":47} - {"id":49,"type":"edge","label":"moniker","inV":47,"outV":13} - {"id":50,"type":"vertex","label":"definitionResult"} - {"id":51,"type":"edge","label":"item","document":1,"inVs":[12],"outV":50} - {"id":52,"type":"edge","label":"textDocument/definition","inV":50,"outV":13} - {"id":53,"type":"vertex","label":"referenceResult"} - {"id":54,"type":"edge","label":"textDocument/references","inV":53,"outV":13} - {"id":55,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[12],"outV":53} - {"id":56,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}} - {"id":57,"type":"edge","label":"textDocument/hover","inV":56,"outV":16} - {"id":58,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"} - {"id":59,"type":"edge","label":"packageInformation","inV":34,"outV":58} - {"id":60,"type":"edge","label":"moniker","inV":58,"outV":16} - {"id":61,"type":"vertex","label":"definitionResult"} - {"id":62,"type":"edge","label":"item","document":1,"inVs":[15],"outV":61} - {"id":63,"type":"edge","label":"textDocument/definition","inV":61,"outV":16} - {"id":64,"type":"vertex","label":"referenceResult"} - {"id":65,"type":"edge","label":"textDocument/references","inV":64,"outV":16} - {"id":66,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":64} - {"id":67,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}} - {"id":68,"type":"edge","label":"textDocument/hover","inV":67,"outV":19} - {"id":69,"type":"vertex","label":"definitionResult"} - {"id":70,"type":"vertex","label":"range","start":{"line":0,"character":0},"end":{"line":13,"character":0}} - {"id":71,"type":"edge","label":"contains","inVs":[70],"outV":1} - {"id":72,"type":"edge","label":"item","document":1,"inVs":[70],"outV":69} - {"id":73,"type":"edge","label":"textDocument/definition","inV":69,"outV":19} - {"id":74,"type":"vertex","label":"referenceResult"} - {"id":75,"type":"edge","label":"textDocument/references","inV":74,"outV":19} - {"id":76,"type":"edge","label":"item","document":1,"property":"references","inVs":[18],"outV":74} - {"id":77,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}} - {"id":78,"type":"edge","label":"textDocument/hover","inV":77,"outV":24} - {"id":79,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"} - {"id":80,"type":"edge","label":"packageInformation","inV":34,"outV":79} - {"id":81,"type":"edge","label":"moniker","inV":79,"outV":24} - {"id":82,"type":"vertex","label":"definitionResult"} - {"id":83,"type":"edge","label":"item","document":1,"inVs":[23],"outV":82} - {"id":84,"type":"edge","label":"textDocument/definition","inV":82,"outV":24} - {"id":85,"type":"vertex","label":"referenceResult"} - {"id":86,"type":"edge","label":"textDocument/references","inV":85,"outV":24} - {"id":87,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[23],"outV":85} + {"id":21,"type":"vertex","label":"range","start":{"line":10,"character":8},"end":{"line":10,"character":13}} + {"id":22,"type":"edge","label":"next","inV":5,"outV":21} + {"id":23,"type":"vertex","label":"range","start":{"line":11,"character":4},"end":{"line":11,"character":34}} + {"id":24,"type":"edge","label":"next","inV":11,"outV":23} + {"id":25,"type":"vertex","label":"range","start":{"line":11,"character":36},"end":{"line":11,"character":43}} + {"id":26,"type":"vertex","label":"resultSet"} + {"id":27,"type":"edge","label":"next","inV":26,"outV":25} + {"id":28,"type":"edge","label":"contains","inVs":[4,7,10,13,15,18,21,23,25],"outV":1} + {"id":29,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nextern crate foo\n```"}}} + {"id":30,"type":"edge","label":"textDocument/hover","inV":29,"outV":5} + {"id":31,"type":"vertex","label":"packageInformation","name":"foo","manager":"cargo","version":"0.0.0"} + {"id":32,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::crate","unique":"scheme","kind":"export"} + {"id":33,"type":"edge","label":"packageInformation","inV":31,"outV":32} + {"id":34,"type":"edge","label":"moniker","inV":32,"outV":5} + {"id":35,"type":"vertex","label":"definitionResult"} + {"id":36,"type":"edge","label":"item","document":1,"inVs":[4],"outV":35} + {"id":37,"type":"edge","label":"textDocument/definition","inV":35,"outV":5} + {"id":38,"type":"vertex","label":"referenceResult"} + {"id":39,"type":"edge","label":"textDocument/references","inV":38,"outV":5} + {"id":40,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[4],"outV":38} + {"id":41,"type":"edge","label":"item","document":1,"property":"references","inVs":[21],"outV":38} + {"id":42,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\n#[allow]\n```\n\n---\n\nValid forms are:\n\n* \\#\\[allow(lint1, lint2, ..., /\\*opt\\*/ reason = \"...\")\\]"}}} + {"id":43,"type":"edge","label":"textDocument/hover","inV":42,"outV":8} + {"id":44,"type":"vertex","label":"referenceResult"} + {"id":45,"type":"edge","label":"textDocument/references","inV":44,"outV":8} + {"id":46,"type":"edge","label":"item","document":1,"property":"references","inVs":[7],"outV":44} + {"id":47,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmacro_rules! generate_const_from_identifier\n```"}}} + {"id":48,"type":"edge","label":"textDocument/hover","inV":47,"outV":11} + {"id":49,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::generate_const_from_identifier","unique":"scheme","kind":"export"} + {"id":50,"type":"edge","label":"packageInformation","inV":31,"outV":49} + {"id":51,"type":"edge","label":"moniker","inV":49,"outV":11} + {"id":52,"type":"vertex","label":"definitionResult"} + {"id":53,"type":"edge","label":"item","document":1,"inVs":[10],"outV":52} + {"id":54,"type":"edge","label":"textDocument/definition","inV":52,"outV":11} + {"id":55,"type":"vertex","label":"referenceResult"} + {"id":56,"type":"edge","label":"textDocument/references","inV":55,"outV":11} + {"id":57,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[10],"outV":55} + {"id":58,"type":"edge","label":"item","document":1,"property":"references","inVs":[13,23],"outV":55} + {"id":59,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nconst REQ_001: &str = \"encoded_data\"\n```"}}} + {"id":60,"type":"edge","label":"textDocument/hover","inV":59,"outV":16} + {"id":61,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::REQ_001","unique":"scheme","kind":"export"} + {"id":62,"type":"edge","label":"packageInformation","inV":31,"outV":61} + {"id":63,"type":"edge","label":"moniker","inV":61,"outV":16} + {"id":64,"type":"vertex","label":"definitionResult"} + {"id":65,"type":"edge","label":"item","document":1,"inVs":[15],"outV":64} + {"id":66,"type":"edge","label":"textDocument/definition","inV":64,"outV":16} + {"id":67,"type":"vertex","label":"referenceResult"} + {"id":68,"type":"edge","label":"textDocument/references","inV":67,"outV":16} + {"id":69,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[15],"outV":67} + {"id":70,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo\n```\n\n```rust\nmod tests\n```"}}} + {"id":71,"type":"edge","label":"textDocument/hover","inV":70,"outV":19} + {"id":72,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests","unique":"scheme","kind":"export"} + {"id":73,"type":"edge","label":"packageInformation","inV":31,"outV":72} + {"id":74,"type":"edge","label":"moniker","inV":72,"outV":19} + {"id":75,"type":"vertex","label":"definitionResult"} + {"id":76,"type":"edge","label":"item","document":1,"inVs":[18],"outV":75} + {"id":77,"type":"edge","label":"textDocument/definition","inV":75,"outV":19} + {"id":78,"type":"vertex","label":"referenceResult"} + {"id":79,"type":"edge","label":"textDocument/references","inV":78,"outV":19} + {"id":80,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[18],"outV":78} + {"id":81,"type":"vertex","label":"hoverResult","result":{"contents":{"kind":"markdown","value":"\n```rust\nfoo::tests\n```\n\n```rust\nconst REQ_002: &str = \"encoded_data\"\n```"}}} + {"id":82,"type":"edge","label":"textDocument/hover","inV":81,"outV":26} + {"id":83,"type":"vertex","label":"moniker","scheme":"rust-analyzer","identifier":"foo::tests::REQ_002","unique":"scheme","kind":"export"} + {"id":84,"type":"edge","label":"packageInformation","inV":31,"outV":83} + {"id":85,"type":"edge","label":"moniker","inV":83,"outV":26} + {"id":86,"type":"vertex","label":"definitionResult"} + {"id":87,"type":"edge","label":"item","document":1,"inVs":[25],"outV":86} + {"id":88,"type":"edge","label":"textDocument/definition","inV":86,"outV":26} + {"id":89,"type":"vertex","label":"referenceResult"} + {"id":90,"type":"edge","label":"textDocument/references","inV":89,"outV":26} + {"id":91,"type":"edge","label":"item","document":1,"property":"definitions","inVs":[25],"outV":89} "#]].assert_eq(stdout); } From db07adf1d8b9f1437205990c7cd768409dae431d Mon Sep 17 00:00:00 2001 From: Ali Bektas <bektasali@protonmail.com> Date: Tue, 28 Jan 2025 21:38:56 +0100 Subject: [PATCH 42/75] try to infer array type from slice pattern rust-analyzer equivalent of rust-lang/rust#2827aa97 --- .../crates/hir-ty/src/infer/pat.rs | 35 ++++++++++++++++++- .../crates/hir-ty/src/tests/simple.rs | 28 +++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index ca8d5bae5e504..83b134604745d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -6,12 +6,13 @@ use hir_def::{ expr_store::Body, hir::{Binding, BindingAnnotation, BindingId, Expr, ExprId, Literal, Pat, PatId}, path::Path, + HasModule, }; use hir_expand::name::Name; use stdx::TupleExt; use crate::{ - consteval::{try_const_usize, usize_const}, + consteval::{self, try_const_usize, usize_const}, infer::{ coerce::CoerceNever, expr::ExprIsRead, BindingMode, Expectation, InferenceContext, TypeMismatch, @@ -479,6 +480,19 @@ impl InferenceContext<'_> { suffix: &[PatId], default_bm: BindingMode, ) -> Ty { + let expected = self.resolve_ty_shallow(expected); + + // If `expected` is an infer ty, we try to equate it to an array if the given pattern + // allows it. See issue #16609 + if expected.is_ty_var() { + if let Some(resolved_array_ty) = + self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) + { + self.unify(&expected, &resolved_array_ty); + } + } + + let expected = self.resolve_ty_shallow(&expected); let elem_ty = match expected.kind(Interner) { TyKind::Array(st, _) | TyKind::Slice(st) => st.clone(), _ => self.err_ty(), @@ -553,6 +567,25 @@ impl InferenceContext<'_> { | Pat::Expr(_) => false, } } + + fn try_resolve_slice_ty_to_array_ty( + &mut self, + before: &[PatId], + suffix: &[PatId], + slice: &Option<PatId>, + ) -> Option<Ty> { + if !slice.is_none() { + return None; + } + + let len = before.len() + suffix.len(); + let size = + consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db.upcast())); + + let elem_ty = self.table.new_type_var(); + let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner); + Some(array_ty) + } } pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { 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 1563660457050..9e0920e41ba47 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 @@ -3814,3 +3814,31 @@ async fn foo(a: (), b: i32) -> u32 { "#, ); } + +#[test] +fn irrefutable_slices() { + check_infer( + r#" +//- minicore: from +struct A; + +impl From<A> for [u8; 2] { + fn from(a: A) -> Self { + [0; 2] + } +} +impl From<A> for [u8; 3] { + fn from(a: A) -> Self { + [0; 3] + } +} + + +fn main() { + let a = A; + let [b, c] = a.into(); +} +"#, + expect![], + ); +} From fc6c6b49c56558c5e4827b6e1875e1149ebddb00 Mon Sep 17 00:00:00 2001 From: Ali Bektas <bektasali@protonmail.com> Date: Tue, 28 Jan 2025 21:47:47 +0100 Subject: [PATCH 43/75] add test, bless tests --- .../crates/hir-ty/src/tests/patterns.rs | 4 ++-- .../crates/hir-ty/src/tests/simple.rs | 21 ++++++++++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) 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 4949d4016bf15..50bc47cf673a4 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 @@ -67,9 +67,9 @@ fn infer_pattern() { 143..144 'e': {unknown} 157..204 'if let... }': () 160..175 'let [val] = opt': bool - 164..169 '[val]': [{unknown}] + 164..169 '[val]': [{unknown}; 1] 165..168 'val': {unknown} - 172..175 'opt': [{unknown}] + 172..175 'opt': [{unknown}; 1] 176..204 '{ ... }': () 190..191 'h': {unknown} 194..197 'val': {unknown} 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 9e0920e41ba47..50a1ecd006d8f 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 @@ -3839,6 +3839,25 @@ fn main() { let [b, c] = a.into(); } "#, - expect![], + expect![[r#" + 50..51 'a': A + 64..86 '{ ... }': [u8; 2] + 74..80 '[0; 2]': [u8; 2] + 75..76 '0': u8 + 78..79 '2': usize + 128..129 'a': A + 142..164 '{ ... }': [u8; 3] + 152..158 '[0; 3]': [u8; 3] + 153..154 '0': u8 + 156..157 '3': usize + 179..224 '{ ...o(); }': () + 189..190 'a': A + 193..194 'A': A + 204..210 '[b, c]': [u8; 2] + 205..206 'b': u8 + 208..209 'c': u8 + 213..214 'a': A + 213..221 'a.into()': [u8; 2] + "#]], ); } From 14ab46691b77ddadbab95cff05f63dd23ca37b54 Mon Sep 17 00:00:00 2001 From: Ali Bektas <bektasali@protonmail.com> Date: Wed, 29 Jan 2025 00:00:52 +0100 Subject: [PATCH 44/75] Equip infer_pat_* with declaration origin --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../crates/hir-ty/src/infer/expr.rs | 11 ++- .../crates/hir-ty/src/infer/pat.rs | 96 ++++++++++++++----- .../rust-analyzer/crates/hir-ty/src/lib.rs | 18 ++++ 4 files changed, 101 insertions(+), 26 deletions(-) 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 3c258e3c4cf4b..59c6ba18eee1c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -943,7 +943,7 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); - self.infer_top_pat(*pat, &ty); + self.infer_top_pat(*pat, &ty, None); if ty .data(Interner) .flags 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 b951443897cb0..f937c4fca96d0 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 @@ -43,9 +43,9 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer, - FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, - TyBuilder, TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext, + DeclOrigin, FnAbi, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, + TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -1633,7 +1633,12 @@ impl InferenceContext<'_> { }; this.infer_top_pat(*pat, &ty); + let decl = DeclContext { + has_else: else_branch.is_some(), + origin: DeclOrigin::LocalDecl, + }; + this.infer_top_pat(*pat, &ty, Some(decl)); if let Some(expr) = else_branch { let previous_diverges = mem::replace(&mut this.diverges, Diverges::Maybe); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 83b134604745d..98693de4bb612 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -19,8 +19,8 @@ use crate::{ }, lower::lower_to_chalk_mutability, primitive::UintTy, - static_lifetime, InferenceDiagnostic, Interner, Mutability, Scalar, Substitution, Ty, - TyBuilder, TyExt, TyKind, + static_lifetime, DeclContext, DeclOrigin, InferenceDiagnostic, Interner, Mutability, Scalar, + Substitution, Ty, TyBuilder, TyExt, TyKind, }; impl InferenceContext<'_> { @@ -35,6 +35,7 @@ impl InferenceContext<'_> { id: PatId, ellipsis: Option<u32>, subs: &[PatId], + decl: Option<DeclContext>, ) -> Ty { let (ty, def) = self.resolve_variant(id.into(), path, true); let var_data = def.map(|it| it.variant_data(self.db.upcast())); @@ -93,13 +94,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(subpat, &expected_ty, default_bm); + self.infer_pat(subpat, &expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for &inner in subs { - self.infer_pat(inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm, decl); } } } @@ -115,6 +116,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, id: PatId, subs: impl ExactSizeIterator<Item = (Name, PatId)>, + decl: Option<DeclContext>, ) -> Ty { let (ty, def) = self.resolve_variant(id.into(), path, false); if let Some(variant) = def { @@ -163,13 +165,13 @@ impl InferenceContext<'_> { } }; - self.infer_pat(inner, &expected_ty, default_bm); + self.infer_pat(inner, &expected_ty, default_bm, decl); } } None => { let err_ty = self.err_ty(); for (_, inner) in subs { - self.infer_pat(inner, &err_ty, default_bm); + self.infer_pat(inner, &err_ty, default_bm, decl); } } } @@ -186,6 +188,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, ellipsis: Option<u32>, subs: &[PatId], + decl: Option<DeclContext>, ) -> Ty { let expected = self.resolve_ty_shallow(expected); let expectations = match expected.as_tuple() { @@ -210,12 +213,12 @@ impl InferenceContext<'_> { // Process pre for (ty, pat) in inner_tys.iter_mut().zip(pre) { - *ty = self.infer_pat(*pat, ty, default_bm); + *ty = self.infer_pat(*pat, ty, default_bm, decl); } // Process post for (ty, pat) in inner_tys.iter_mut().skip(pre.len() + n_uncovered_patterns).zip(post) { - *ty = self.infer_pat(*pat, ty, default_bm); + *ty = self.infer_pat(*pat, ty, default_bm, decl); } TyKind::Tuple(inner_tys.len(), Substitution::from_iter(Interner, inner_tys)) @@ -224,11 +227,17 @@ impl InferenceContext<'_> { /// The resolver needs to be updated to the surrounding expression when inside assignment /// (because there, `Pat::Path` can refer to a variable). - pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) { - self.infer_pat(pat, expected, BindingMode::default()); + pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty, decl: Option<DeclContext>) { + self.infer_pat(pat, expected, BindingMode::default(), decl); } - fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty { + fn infer_pat( + &mut self, + pat: PatId, + expected: &Ty, + mut default_bm: BindingMode, + decl: Option<DeclContext>, + ) -> Ty { let mut expected = self.resolve_ty_shallow(expected); if matches!(&self.body[pat], Pat::Ref { .. }) || self.inside_assignment { @@ -262,11 +271,11 @@ impl InferenceContext<'_> { let ty = match &self.body[pat] { Pat::Tuple { args, ellipsis } => { - self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args) + self.infer_tuple_pat_like(&expected, default_bm, *ellipsis, args, decl) } Pat::Or(pats) => { for pat in pats.iter() { - self.infer_pat(*pat, &expected, default_bm); + self.infer_pat(*pat, &expected, default_bm, decl); } expected.clone() } @@ -275,6 +284,7 @@ impl InferenceContext<'_> { lower_to_chalk_mutability(mutability), &expected, default_bm, + decl, ), Pat::TupleStruct { path: p, args: subpats, ellipsis } => self .infer_tuple_struct_pat_like( @@ -284,10 +294,11 @@ impl InferenceContext<'_> { pat, *ellipsis, subpats, + decl, ), Pat::Record { path: p, args: fields, ellipsis: _ } => { let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs) + self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs, decl) } Pat::Path(path) => { let ty = self.infer_path(path, pat.into()).unwrap_or_else(|| self.err_ty()); @@ -320,10 +331,10 @@ impl InferenceContext<'_> { } } Pat::Bind { id, subpat } => { - return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected); + return self.infer_bind_pat(pat, *id, default_bm, *subpat, &expected, decl); } Pat::Slice { prefix, slice, suffix } => { - self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm) + self.infer_slice_pat(&expected, prefix, slice, suffix, default_bm, decl) } Pat::Wild => expected.clone(), Pat::Range { .. } => { @@ -346,7 +357,7 @@ impl InferenceContext<'_> { _ => (self.result.standard_types.unknown.clone(), None), }; - let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm); + let inner_ty = self.infer_pat(*inner, &inner_ty, default_bm, decl); let mut b = TyBuilder::adt(self.db, box_adt).push(inner_ty); if let Some(alloc_ty) = alloc_ty { @@ -421,6 +432,7 @@ impl InferenceContext<'_> { mutability: Mutability, expected: &Ty, default_bm: BindingMode, + decl: Option<DeclContext>, ) -> Ty { let (expectation_type, expectation_lt) = match expected.as_reference() { Some((inner_ty, lifetime, _exp_mut)) => (inner_ty.clone(), lifetime.clone()), @@ -434,7 +446,7 @@ impl InferenceContext<'_> { (inner_ty, inner_lt) } }; - let subty = self.infer_pat(inner_pat, &expectation_type, default_bm); + let subty = self.infer_pat(inner_pat, &expectation_type, default_bm, decl); TyKind::Ref(mutability, expectation_lt, subty).intern(Interner) } @@ -445,6 +457,7 @@ impl InferenceContext<'_> { default_bm: BindingMode, subpat: Option<PatId>, expected: &Ty, + decl: Option<DeclContext>, ) -> Ty { let Binding { mode, .. } = self.body.bindings[binding]; let mode = if mode == BindingAnnotation::Unannotated { @@ -455,7 +468,7 @@ impl InferenceContext<'_> { self.result.binding_modes.insert(pat, mode); let inner_ty = match subpat { - Some(subpat) => self.infer_pat(subpat, expected, default_bm), + Some(subpat) => self.infer_pat(subpat, expected, default_bm, decl), None => expected.clone(), }; let inner_ty = self.insert_type_vars_shallow(inner_ty); @@ -479,12 +492,13 @@ impl InferenceContext<'_> { slice: &Option<PatId>, suffix: &[PatId], default_bm: BindingMode, + decl: Option<DeclContext>, ) -> Ty { let expected = self.resolve_ty_shallow(expected); // If `expected` is an infer ty, we try to equate it to an array if the given pattern // allows it. See issue #16609 - if expected.is_ty_var() { + if self.decl_allows_array_type_infer(decl) && expected.is_ty_var() { if let Some(resolved_array_ty) = self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) { @@ -499,7 +513,7 @@ impl InferenceContext<'_> { }; for &pat_id in prefix.iter().chain(suffix.iter()) { - self.infer_pat(pat_id, &elem_ty, default_bm); + self.infer_pat(pat_id, &elem_ty, default_bm, decl); } if let &Some(slice_pat_id) = slice { @@ -513,7 +527,7 @@ impl InferenceContext<'_> { _ => TyKind::Slice(elem_ty.clone()), } .intern(Interner); - self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm); + self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm, decl); } match expected.kind(Interner) { @@ -586,6 +600,44 @@ impl InferenceContext<'_> { let array_ty = TyKind::Array(elem_ty.clone(), size).intern(Interner); Some(array_ty) } + + /// Determines whether we can infer the expected type in the slice pattern to be of type array. + /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable + /// patterns we wouldn't e.g. report ambiguity in the following situation: + /// + /// ```ignore(rust) + /// struct Zeroes; + /// const ARR: [usize; 2] = [0; 2]; + /// const ARR2: [usize; 2] = [2; 2]; + /// + /// impl Into<&'static [usize; 2]> for Zeroes { + /// fn into(self) -> &'static [usize; 2] { + /// &ARR + /// } + /// } + /// + /// impl Into<&'static [usize]> for Zeroes { + /// fn into(self) -> &'static [usize] { + /// &ARR2 + /// } + /// } + /// + /// fn main() { + /// let &[a, b]: &[usize] = Zeroes.into() else { + /// .. + /// }; + /// } + /// ``` + /// + /// If we're in an irrefutable pattern we prefer the array impl candidate given that + /// the slice impl candidate would be be rejected anyway (if no ambiguity existed). + fn decl_allows_array_type_infer(&self, decl_ctxt: Option<DeclContext>) -> bool { + if let Some(decl_ctxt) = decl_ctxt { + !decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl) + } else { + false + } + } } pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool { 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 4b159b7541e66..acac7ddcad3cf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -1049,3 +1049,21 @@ pub fn known_const_to_ast( } Some(make::expr_const_value(konst.display(db, edition).to_string().as_str())) } + +#[derive(Debug, Copy, Clone)] +pub(crate) enum DeclOrigin { + // from an `if let` expression + LetExpr, + // from `let x = ..` + LocalDecl, +} + +/// Provides context for checking patterns in declarations. More specifically this +/// allows us to infer array types if the pattern is irrefutable and allows us to infer +/// the size of the array. See issue #76342. +#[derive(Debug, Copy, Clone)] +pub(crate) struct DeclContext { + // whether we're in a let-else context + pub(crate) has_else: bool, + pub(crate) origin: DeclOrigin, +} From 437921fb770ed6097eb587e03f3c98a8052d839c Mon Sep 17 00:00:00 2001 From: Ali Bektas <bektasali@protonmail.com> Date: Wed, 29 Jan 2025 00:41:10 +0100 Subject: [PATCH 45/75] call infer_pats from infer::expr with context --- src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 f937c4fca96d0..a8753628afad4 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 @@ -334,7 +334,7 @@ impl InferenceContext<'_> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat(pat, &input_ty); + self.infer_top_pat(pat, &input_ty, None); self.result.standard_types.bool_.clone() } Expr::Block { statements, tail, label, id } => { @@ -461,7 +461,7 @@ impl InferenceContext<'_> { // Now go through the argument patterns for (arg_pat, arg_ty) in args.iter().zip(&sig_tys) { - self.infer_top_pat(*arg_pat, arg_ty); + self.infer_top_pat(*arg_pat, arg_ty, None); } // FIXME: lift these out into a struct @@ -582,7 +582,7 @@ impl InferenceContext<'_> { let mut all_arms_diverge = Diverges::Always; for arm in arms.iter() { let input_ty = self.resolve_ty_shallow(&input_ty); - self.infer_top_pat(arm.pat, &input_ty); + self.infer_top_pat(arm.pat, &input_ty, None); } let expected = expected.adjust_for_branches(&mut self.table); @@ -927,7 +927,7 @@ impl InferenceContext<'_> { let resolver_guard = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, tgt_expr); self.inside_assignment = true; - self.infer_top_pat(target, &rhs_ty); + self.infer_top_pat(target, &rhs_ty, None); self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } @@ -1632,7 +1632,6 @@ impl InferenceContext<'_> { decl_ty }; - this.infer_top_pat(*pat, &ty); let decl = DeclContext { has_else: else_branch.is_some(), origin: DeclOrigin::LocalDecl, From 22b8339703652e48e2a2cd5cbe5e6f0503b4b99e Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl <cessen@cessen.com> Date: Wed, 29 Jan 2025 12:07:20 +0100 Subject: [PATCH 46/75] Update TentHash to version 1.0 The TentHash spec was frozen Jan 1st 2025, and release 1.0 of the Rust crate is a minor cleanup as a follow-up to that, representing a commitment to API stability as well. The hash output remains the same as version 0.4, which rust-analyzer was previously using. The only API change was a struct rename. --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml | 2 +- src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 2dfca7c4803e2..3ba7df3ad5103 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2001,9 +2001,9 @@ dependencies = [ [[package]] name = "tenthash" -version = "0.4.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d67f9f3cf70e0852941d7bc3cb884b49b24b8ee956baf91ad0abae31f5ef11fb" +checksum = "2d092d622df8bb64e5de8dc86a3667702d5f1e0fe2f0604c6035540703c8cd1e" [[package]] name = "test-fixture" diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml index c24cbb4a31176..b8ce2b7430b98 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/crates/rust-analyzer/Cargo.toml @@ -37,7 +37,7 @@ rustc-hash.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } serde.workspace = true serde_derive.workspace = true -tenthash = "0.4.0" +tenthash = "1.0.0" num_cpus = "1.15.0" mimalloc = { version = "0.1.30", default-features = false, optional = true } lsp-server.workspace = true diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index ccffa7a671e66..02361a22328b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -50,7 +50,7 @@ mod integrated_benchmarks; use hir::Mutability; use ide::{CompletionItem, CompletionItemRefMode, CompletionRelevance}; use serde::de::DeserializeOwned; -use tenthash::TentHasher; +use tenthash::TentHash; pub use crate::{ lsp::capabilities::server_capabilities, main_loop::main_loop, reload::ws_to_crate_graph, @@ -66,7 +66,7 @@ pub fn from_json<T: DeserializeOwned>( } fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; 20] { - fn hash_completion_relevance(hasher: &mut TentHasher, relevance: &CompletionRelevance) { + fn hash_completion_relevance(hasher: &mut TentHash, relevance: &CompletionRelevance) { use ide_completion::{ CompletionRelevancePostfixMatch, CompletionRelevanceReturnType, CompletionRelevanceTypeMatch, @@ -108,7 +108,7 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; } } - let mut hasher = TentHasher::new(); + let mut hasher = TentHash::new(); hasher.update([ u8::from(is_ref_completion), u8::from(item.is_snippet), From 0d9355ecdb72cf7c12195d008e0dd99ab4045895 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 29 Jan 2025 14:09:04 +0100 Subject: [PATCH 47/75] Remove mutable syntax tree shenanigans from adjustment hints --- .../crates/ide/src/inlay_hints/adjustment.rs | 102 +++++---------- .../crates/syntax/src/ast/prec.rs | 117 +++++++++++++++++- 2 files changed, 148 insertions(+), 71 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 2acd4021cc155..d3b95750f7e18 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -13,11 +13,7 @@ use ide_db::famous_defs::FamousDefs; use ide_db::text_edit::TextEditBuilder; use span::EditionedFileId; -use stdx::never; -use syntax::{ - ast::{self, make, AstNode}, - ted, -}; +use syntax::ast::{self, prec::ExprPrecedence, AstNode}; use crate::{ AdjustmentHints, AdjustmentHintsMode, InlayHint, InlayHintLabel, InlayHintLabelPart, @@ -104,12 +100,14 @@ pub(super) fn hints( }; let iter: &mut dyn Iterator<Item = _> = iter.as_mut().either(|it| it as _, |it| it as _); + let mut has_adjustments = false; let mut allow_edit = !postfix; for Adjustment { source, target, kind } in iter { if source == target { cov_mark::hit!(same_type_adjustment); continue; } + has_adjustments = true; // FIXME: Add some nicer tooltips to each of these let (text, coercion) = match kind { @@ -172,6 +170,10 @@ pub(super) fn hints( }; if postfix { &mut post } else { &mut pre }.label.append_part(label); } + if !has_adjustments { + return None; + } + if !postfix && needs_inner_parens { pre.label.append_str("("); } @@ -254,71 +256,31 @@ fn mode_and_needs_parens_for_adjustment_hints( /// Returns whatever we need to add parentheses on the inside and/or outside of `expr`, /// if we are going to add (`postfix`) adjustments hints to it. fn needs_parens_for_adjustment_hints(expr: &ast::Expr, postfix: bool) -> (bool, bool) { - // This is a very miserable pile of hacks... - // - // `Expr::needs_parens_in` requires that the expression is the child of the other expression, - // that is supposed to be its parent. - // - // But we want to check what would happen if we add `*`/`.*` to the inner expression. - // To check for inner we need `` expr.needs_parens_in(`*expr`) ``, - // to check for outer we need `` `*expr`.needs_parens_in(parent) ``, - // where "expr" is the `expr` parameter, `*expr` is the edited `expr`, - // and "parent" is the parent of the original expression... - // - // For this we utilize mutable trees, which is a HACK, but it works. - // - // FIXME: comeup with a better API for `needs_parens_in`, so that we don't have to do *this* - - // Make `&expr`/`expr?` - let dummy_expr = { - // `make::*` function go through a string, so they parse wrongly. - // for example `` make::expr_try(`|| a`) `` would result in a - // `|| (a?)` and not `(|| a)?`. - // - // Thus we need dummy parens to preserve the relationship we want. - // The parens are then simply ignored by the following code. - let dummy_paren = make::expr_paren(expr.clone()); - if postfix { - make::expr_try(dummy_paren) - } else { - make::expr_ref(dummy_paren, false) - } - }; - - // Do the dark mutable tree magic. - // This essentially makes `dummy_expr` and `expr` switch places (families), - // so that `expr`'s parent is not `dummy_expr`'s parent. - let dummy_expr = dummy_expr.clone_for_update(); - let expr = expr.clone_for_update(); - ted::replace(expr.syntax(), dummy_expr.syntax()); - - let parent = dummy_expr.syntax().parent(); - let Some(expr) = (|| { - if postfix { - let ast::Expr::TryExpr(e) = &dummy_expr else { return None }; - let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None }; - - e.expr() - } else { - let ast::Expr::RefExpr(e) = &dummy_expr else { return None }; - let Some(ast::Expr::ParenExpr(e)) = e.expr() else { return None }; - - e.expr() - } - })() else { - never!("broken syntax tree?\n{:?}\n{:?}", expr, dummy_expr); - return (true, true); - }; - - // At this point - // - `parent` is the parent of the original expression - // - `dummy_expr` is the original expression wrapped in the operator we want (`*`/`.*`) - // - `expr` is the clone of the original expression (with `dummy_expr` as the parent) - - let needs_outer_parens = parent.is_some_and(|p| dummy_expr.needs_parens_in(p)); - let needs_inner_parens = expr.needs_parens_in(dummy_expr.syntax().clone()); - - (needs_outer_parens, needs_inner_parens) + let prec = expr.precedence(); + if postfix { + // postfix ops have higher precedence than any other operator, so we need to wrap + // any inner expression that is below (except for jumps if they don't have a value) + let needs_inner_parens = prec < ExprPrecedence::Unambiguous && { + prec != ExprPrecedence::Jump || !expr.is_ret_like_with_no_value() + }; + // given we are the higher precedence, no parent expression will have stronger requirements + let needs_outer_parens = false; + (needs_outer_parens, needs_inner_parens) + } else { + // We need to wrap all binary like things, thats everything below prefix except for jumps + let needs_inner_parens = prec < ExprPrecedence::Prefix && prec != ExprPrecedence::Jump; + let parent = expr + .syntax() + .parent() + .and_then(ast::Expr::cast) + // if we are already wrapped, great, no need to wrap again + .filter(|it| !matches!(it, ast::Expr::ParenExpr(_))) + .map(|it| it.precedence()); + // if we have no parent, we don't need outer parens to disambiguate + // otherwise anything with higher precedence than what we insert needs to wrap us + let needs_outer_parens = parent.is_some_and(|prec| prec > ExprPrecedence::Prefix); + (needs_outer_parens, needs_inner_parens) + } } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs index 28089ffb3771f..5d33f132ac150 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs @@ -5,7 +5,122 @@ use crate::{ match_ast, AstNode, SyntaxNode, }; +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] +pub enum ExprPrecedence { + // return, break, yield, closures + Jump, + // = += -= *= /= %= &= |= ^= <<= >>= + Assign, + // .. ..= + Range, + // || + LOr, + // && + LAnd, + // == != < > <= >= + Compare, + // | + BitOr, + // ^ + BitXor, + // & + BitAnd, + // << >> + Shift, + // + - + Sum, + // * / % + Product, + // as + Cast, + // unary - * ! & &mut + Prefix, + // paths, loops, function calls, array indexing, field expressions, method calls + Unambiguous, +} + +#[derive(PartialEq, Debug)] +pub enum Fixity { + /// The operator is left-associative + Left, + /// The operator is right-associative + Right, + /// The operator is not associative + None, +} + +pub fn precedence(expr: &ast::Expr) -> ExprPrecedence { + match expr { + Expr::ClosureExpr(closure) => match closure.ret_type() { + None => ExprPrecedence::Jump, + Some(_) => ExprPrecedence::Unambiguous, + }, + + Expr::BreakExpr(_) + | Expr::ContinueExpr(_) + | Expr::ReturnExpr(_) + | Expr::YeetExpr(_) + | Expr::YieldExpr(_) => ExprPrecedence::Jump, + + Expr::RangeExpr(..) => ExprPrecedence::Range, + + Expr::BinExpr(bin_expr) => match bin_expr.op_kind() { + Some(it) => match it { + BinaryOp::LogicOp(logic_op) => match logic_op { + ast::LogicOp::And => ExprPrecedence::LAnd, + ast::LogicOp::Or => ExprPrecedence::LOr, + }, + BinaryOp::ArithOp(arith_op) => match arith_op { + ast::ArithOp::Add | ast::ArithOp::Sub => ExprPrecedence::Sum, + ast::ArithOp::Div | ast::ArithOp::Rem | ast::ArithOp::Mul => { + ExprPrecedence::Product + } + ast::ArithOp::Shl | ast::ArithOp::Shr => ExprPrecedence::Shift, + ast::ArithOp::BitXor => ExprPrecedence::BitXor, + ast::ArithOp::BitOr => ExprPrecedence::BitOr, + ast::ArithOp::BitAnd => ExprPrecedence::BitAnd, + }, + BinaryOp::CmpOp(_) => ExprPrecedence::Compare, + BinaryOp::Assignment { .. } => ExprPrecedence::Assign, + }, + None => ExprPrecedence::Unambiguous, + }, + Expr::CastExpr(_) => ExprPrecedence::Cast, + + Expr::LetExpr(_) | Expr::PrefixExpr(_) | Expr::RefExpr(_) => ExprPrecedence::Prefix, + + Expr::ArrayExpr(_) + | Expr::AsmExpr(_) + | Expr::AwaitExpr(_) + | Expr::BecomeExpr(_) + | Expr::BlockExpr(_) + | Expr::CallExpr(_) + | Expr::FieldExpr(_) + | Expr::ForExpr(_) + | Expr::FormatArgsExpr(_) + | Expr::IfExpr(_) + | Expr::IndexExpr(_) + | Expr::Literal(_) + | Expr::LoopExpr(_) + | Expr::MacroExpr(_) + | Expr::MatchExpr(_) + | Expr::MethodCallExpr(_) + | Expr::OffsetOfExpr(_) + | Expr::ParenExpr(_) + | Expr::PathExpr(_) + | Expr::RecordExpr(_) + | Expr::TryExpr(_) + | Expr::TupleExpr(_) + | Expr::UnderscoreExpr(_) + | Expr::WhileExpr(_) => ExprPrecedence::Unambiguous, + } +} + impl Expr { + pub fn precedence(&self) -> ExprPrecedence { + precedence(self) + } + // Implementation is based on // - https://doc.rust-lang.org/reference/expressions.html#expression-precedence // - https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html @@ -261,7 +376,7 @@ impl Expr { } /// Returns true if self is one of `return`, `break`, `continue` or `yield` with **no associated value**. - fn is_ret_like_with_no_value(&self) -> bool { + pub fn is_ret_like_with_no_value(&self) -> bool { use Expr::*; match self { From 0cf275b9fe9d610678ded0bfcad262b8552a1b51 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl <cessen@cessen.com> Date: Wed, 29 Jan 2025 17:22:13 +0100 Subject: [PATCH 48/75] Fix #19071: ensure `completion_item_hash` serializes items uniquely Previously it may have been possible for different completion items to produce colliding hashes, not because of the hash but because of how the items were serialized into byte streams for hashing. See #19071 for details. The chances of that happening were low, if it was actually possible at all. Nevertheless, this commit ensures that it definitely can't happen. This commit uses a handful of techniques used to fix this, but they all boil down to "ensure this could be re-parsed". If it's possible to parse to recreate the original item, then by construction there is no chance of two different items getting serialized to identical byte streams. --- .../crates/rust-analyzer/src/lib.rs | 74 +++++++++++++------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index ccffa7a671e66..98ba8ab3af02b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -79,32 +79,34 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; u8::from(relevance.requires_import), u8::from(relevance.is_private_editable), ]); - if let Some(type_match) = &relevance.type_match { - let label = match type_match { - CompletionRelevanceTypeMatch::CouldUnify => "could_unify", - CompletionRelevanceTypeMatch::Exact => "exact", - }; - hasher.update(label); + + match relevance.type_match { + None => hasher.update([0u8]), + Some(CompletionRelevanceTypeMatch::CouldUnify) => hasher.update([1u8]), + Some(CompletionRelevanceTypeMatch::Exact) => hasher.update([2u8]), } + + hasher.update([u8::from(relevance.trait_.is_some())]); if let Some(trait_) = &relevance.trait_ { hasher.update([u8::from(trait_.is_op_method), u8::from(trait_.notable_trait)]); } - if let Some(postfix_match) = &relevance.postfix_match { - let label = match postfix_match { - CompletionRelevancePostfixMatch::NonExact => "non_exact", - CompletionRelevancePostfixMatch::Exact => "exact", - }; - hasher.update(label); + + match relevance.postfix_match { + None => hasher.update([0u8]), + Some(CompletionRelevancePostfixMatch::NonExact) => hasher.update([1u8]), + Some(CompletionRelevancePostfixMatch::Exact) => hasher.update([2u8]), } + + hasher.update([u8::from(relevance.function.is_some())]); if let Some(function) = &relevance.function { hasher.update([u8::from(function.has_params), u8::from(function.has_self_param)]); - let label = match function.return_type { - CompletionRelevanceReturnType::Other => "other", - CompletionRelevanceReturnType::DirectConstructor => "direct_constructor", - CompletionRelevanceReturnType::Constructor => "constructor", - CompletionRelevanceReturnType::Builder => "builder", + let discriminant: u8 = match function.return_type { + CompletionRelevanceReturnType::Other => 0, + CompletionRelevanceReturnType::DirectConstructor => 1, + CompletionRelevanceReturnType::Constructor => 2, + CompletionRelevanceReturnType::Builder => 3, }; - hasher.update(label); + hasher.update([discriminant]); } } @@ -115,35 +117,59 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; u8::from(item.deprecated), u8::from(item.trigger_call_info), ]); + + hasher.update(item.label.primary.len().to_le_bytes()); hasher.update(&item.label.primary); + + hasher.update([u8::from(item.label.detail_left.is_some())]); if let Some(label_detail) = &item.label.detail_left { + hasher.update(label_detail.len().to_le_bytes()); hasher.update(label_detail); } + + hasher.update([u8::from(item.label.detail_right.is_some())]); if let Some(label_detail) = &item.label.detail_right { + hasher.update(label_detail.len().to_le_bytes()); hasher.update(label_detail); } + // NB: do not hash edits or source range, as those may change between the time the client sends the resolve request // and the time it receives it: some editors do allow changing the buffer between that, leading to ranges being different. // // Documentation hashing is skipped too, as it's a large blob to process, // while not really making completion properties more unique as they are already. - hasher.update(item.kind.tag()); + + let kind_tag = item.kind.tag(); + hasher.update(kind_tag.len().to_le_bytes()); + hasher.update(kind_tag); + + hasher.update(item.lookup.len().to_le_bytes()); hasher.update(&item.lookup); + + hasher.update([u8::from(item.detail.is_some())]); if let Some(detail) = &item.detail { + hasher.update(detail.len().to_le_bytes()); hasher.update(detail); } + hash_completion_relevance(&mut hasher, &item.relevance); + + hasher.update([u8::from(item.ref_match.is_some())]); if let Some((ref_mode, text_size)) = &item.ref_match { - let prefix = match ref_mode { - CompletionItemRefMode::Reference(Mutability::Shared) => "&", - CompletionItemRefMode::Reference(Mutability::Mut) => "&mut ", - CompletionItemRefMode::Dereference => "*", + let descriminant = match ref_mode { + CompletionItemRefMode::Reference(Mutability::Shared) => 0u8, + CompletionItemRefMode::Reference(Mutability::Mut) => 1u8, + CompletionItemRefMode::Dereference => 2u8, }; - hasher.update(prefix); + hasher.update([descriminant]); hasher.update(u32::from(*text_size).to_le_bytes()); } + + hasher.update(item.import_to_add.len().to_le_bytes()); for import_path in &item.import_to_add { + hasher.update(import_path.len().to_le_bytes()); hasher.update(import_path); } + hasher.finalize() } From 615186586b76c831ddf3d9427f7e148f6b1c11bd Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl <cessen@cessen.com> Date: Wed, 29 Jan 2025 17:47:38 +0100 Subject: [PATCH 49/75] Fix typo --- src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 98ba8ab3af02b..0b459c10cee79 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -156,12 +156,12 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; hasher.update([u8::from(item.ref_match.is_some())]); if let Some((ref_mode, text_size)) = &item.ref_match { - let descriminant = match ref_mode { + let discriminant = match ref_mode { CompletionItemRefMode::Reference(Mutability::Shared) => 0u8, CompletionItemRefMode::Reference(Mutability::Mut) => 1u8, CompletionItemRefMode::Dereference => 2u8, }; - hasher.update([descriminant]); + hasher.update([discriminant]); hasher.update(u32::from(*text_size).to_le_bytes()); } From 7ffccb03468d5573da2b4ff2587e953346e05bf3 Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl <cessen@cessen.com> Date: Wed, 29 Jan 2025 20:21:57 +0100 Subject: [PATCH 50/75] Use `to_ne_bytes` instead of `to_le_bytes` --- .../crates/rust-analyzer/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 0b459c10cee79..c2ef03ecae8dd 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -118,18 +118,18 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; u8::from(item.trigger_call_info), ]); - hasher.update(item.label.primary.len().to_le_bytes()); + hasher.update(item.label.primary.len().to_ne_bytes()); hasher.update(&item.label.primary); hasher.update([u8::from(item.label.detail_left.is_some())]); if let Some(label_detail) = &item.label.detail_left { - hasher.update(label_detail.len().to_le_bytes()); + hasher.update(label_detail.len().to_ne_bytes()); hasher.update(label_detail); } hasher.update([u8::from(item.label.detail_right.is_some())]); if let Some(label_detail) = &item.label.detail_right { - hasher.update(label_detail.len().to_le_bytes()); + hasher.update(label_detail.len().to_ne_bytes()); hasher.update(label_detail); } @@ -140,15 +140,15 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; // while not really making completion properties more unique as they are already. let kind_tag = item.kind.tag(); - hasher.update(kind_tag.len().to_le_bytes()); + hasher.update(kind_tag.len().to_ne_bytes()); hasher.update(kind_tag); - hasher.update(item.lookup.len().to_le_bytes()); + hasher.update(item.lookup.len().to_ne_bytes()); hasher.update(&item.lookup); hasher.update([u8::from(item.detail.is_some())]); if let Some(detail) = &item.detail { - hasher.update(detail.len().to_le_bytes()); + hasher.update(detail.len().to_ne_bytes()); hasher.update(detail); } @@ -162,12 +162,12 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; CompletionItemRefMode::Dereference => 2u8, }; hasher.update([discriminant]); - hasher.update(u32::from(*text_size).to_le_bytes()); + hasher.update(u32::from(*text_size).to_ne_bytes()); } - hasher.update(item.import_to_add.len().to_le_bytes()); + hasher.update(item.import_to_add.len().to_ne_bytes()); for import_path in &item.import_to_add { - hasher.update(import_path.len().to_le_bytes()); + hasher.update(import_path.len().to_ne_bytes()); hasher.update(import_path); } From e70b5899821ea1e520c0e50b2bcfbbe16da25581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Sat, 1 Feb 2025 08:21:14 +0200 Subject: [PATCH 51/75] Stop running some release workflows on forks and update old URLs --- .../rust-analyzer/.github/workflows/autopublish.yaml | 1 + .../rust-analyzer/.github/workflows/publish-libs.yaml | 1 + .../rust-analyzer/.github/workflows/release.yaml | 11 +++++++---- src/tools/rust-analyzer/README.md | 2 +- src/tools/rust-analyzer/xtask/src/publish/notes.rs | 6 +++--- src/tools/rust-analyzer/xtask/test_data/expected.md | 8 ++++---- 6 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index 5258d9ddd3ad0..e4fa94643ba19 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -11,6 +11,7 @@ on: jobs: publish: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest steps: diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index f1533bf26e52b..5023a634fde42 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -9,6 +9,7 @@ on: jobs: publish-libs: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest steps: diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml index 39ac652de0f86..3f1c8b2a9c1ce 100644 --- a/src/tools/rust-analyzer/.github/workflows/release.yaml +++ b/src/tools/rust-analyzer/.github/workflows/release.yaml @@ -22,6 +22,7 @@ env: jobs: dist: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} strategy: matrix: include: @@ -138,6 +139,7 @@ jobs: path: ./dist dist-x86_64-unknown-linux-musl: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: dist (x86_64-unknown-linux-musl) runs-on: ubuntu-latest env: @@ -183,6 +185,7 @@ jobs: path: ./dist publish: + if: ${{ github.repository == 'rust-lang/rust-analyzer' || github.event.action == 'workflow_dispatch' }} name: publish runs-on: ubuntu-latest needs: ["dist", "dist-x86_64-unknown-linux-musl"] @@ -257,24 +260,24 @@ jobs: working-directory: ./editors/code - name: Publish Extension (Code Marketplace, release) - if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code # token from https://dev.azure.com/rust-analyzer/ run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix - name: Publish Extension (OpenVSX, release) - if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref == 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 - name: Publish Extension (Code Marketplace, nightly) - if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release - name: Publish Extension (OpenVSX, nightly) - if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') + if: github.ref != 'refs/heads/release' && github.repository == 'rust-lang/rust-analyzer' working-directory: ./editors/code run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index 552f71f151847..fb885c5be7615 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -1,6 +1,6 @@ <p align="center"> <img - src="https://raw.githubusercontent.com/rust-analyzer/rust-analyzer/master/assets/logo-wide.svg" + src="https://raw.githubusercontent.com/rust-lang/rust-analyzer/master/assets/logo-wide.svg" alt="rust-analyzer logo"> </p> diff --git a/src/tools/rust-analyzer/xtask/src/publish/notes.rs b/src/tools/rust-analyzer/xtask/src/publish/notes.rs index c30267295bf41..7245ce2431187 100644 --- a/src/tools/rust-analyzer/xtask/src/publish/notes.rs +++ b/src/tools/rust-analyzer/xtask/src/publish/notes.rs @@ -549,18 +549,18 @@ impl Macro { } "pr" => { let pr = &self.target; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/pull/{pr}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/pull/{pr}"); format!("[`#{pr}`]({url})") } "commit" => { let hash = &self.target; let short = &hash[0..7]; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/commit/{hash}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/commit/{hash}"); format!("[`{short}`]({url})") } "release" => { let date = &self.target; - let url = format!("https://github.com/rust-analyzer/rust-analyzer/releases/{date}"); + let url = format!("https://github.com/rust-lang/rust-analyzer/releases/{date}"); format!("[`{date}`]({url})") } _ => bail!("macro not supported: {name}"), diff --git a/src/tools/rust-analyzer/xtask/test_data/expected.md b/src/tools/rust-analyzer/xtask/test_data/expected.md index 19c940c67bdc5..301837b5c21b6 100644 --- a/src/tools/rust-analyzer/xtask/test_data/expected.md +++ b/src/tools/rust-analyzer/xtask/test_data/expected.md @@ -2,12 +2,12 @@ Hello! -Commit: [`0123456`](https://github.com/rust-analyzer/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \ -Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/2022-01-01) +Commit: [`0123456`](https://github.com/rust-lang/rust-analyzer/commit/0123456789abcdef0123456789abcdef01234567) \ +Release: [`2022-01-01`](https://github.com/rust-lang/rust-analyzer/releases/2022-01-01) ## New Features -- **BREAKING** [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd> +- **BREAKING** [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111) shortcut <kbd>ctrl</kbd>+<kbd>r</kbd> - hyphen-prefixed list item - nested list item - `foo` -> `foofoo` @@ -65,7 +65,7 @@ Release: [`2022-01-01`](https://github.com/rust-analyzer/rust-analyzer/releases/ - list item with an inline image  -The highlight of the month is probably [`#1111`](https://github.com/rust-analyzer/rust-analyzer/pull/1111). +The highlight of the month is probably [`#1111`](https://github.com/rust-lang/rust-analyzer/pull/1111). See [online manual](https://example.com/manual) for more information. ```bash From acb9544ef852d479aebe94b0dcc14eb86bc3c597 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Sun, 2 Feb 2025 10:04:57 +0100 Subject: [PATCH 52/75] More rustfmt hang investigations --- .../crates/rust-analyzer/src/global_state.rs | 5 +++-- .../src/handlers/notification.rs | 3 ++- .../rust-analyzer/src/handlers/request.rs | 21 +++++++++++-------- .../crates/rust-analyzer/src/reload.rs | 3 +-- 4 files changed, 18 insertions(+), 14 deletions(-) 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 0f2d7823b7e7c..b52f64aaacecb 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 @@ -396,6 +396,7 @@ impl GlobalState { || !self.config.same_source_root_parent_map(&self.local_roots_parent_map) { let config_change = { + let _p = span!(Level::INFO, "GlobalState::process_changes/config_change").entered(); let user_config_path = (|| { let mut p = Config::user_config_dir_path()?; p.push("rust-analyzer.toml"); @@ -569,12 +570,12 @@ impl GlobalState { if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error { if err.message.starts_with("server panicked") { - self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)) + self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)); } } let duration = start.elapsed(); - tracing::debug!("handled {} - ({}) in {:0.2?}", method, response.id, duration); + tracing::debug!(name: "message response", method, %response.id, duration = format_args!("{:0.2?}", duration)); self.send(response.into()); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 84ba89d9f31f9..48856d19e155d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -74,7 +74,8 @@ pub(crate) fn handle_did_open_text_document( tracing::error!("duplicate DidOpenTextDocument: {}", path); } - state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); + let contents = params.text_document.text.into_bytes(); + state.vfs.write().0.set_file_contents(path, Some(contents)); if state.config.discover_workspace_config().is_some() { tracing::debug!("queuing task"); let _ = state diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 39cbf53eaa21d..ed028f1d37b67 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -2318,18 +2318,21 @@ fn run_rustfmt( } }; - tracing::debug!(?command, "created format command"); + let output = { + let _p = tracing::info_span!("rustfmt", ?command).entered(); - let mut rustfmt = command - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context(format!("Failed to spawn {command:?}"))?; + let mut rustfmt = command + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context(format!("Failed to spawn {command:?}"))?; - rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; + rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; + + rustfmt.wait_with_output()? + }; - let output = rustfmt.wait_with_output()?; let captured_stdout = String::from_utf8(output.stdout)?; let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default(); 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 0add2cdf5a71a..d18e577047749 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -701,8 +701,7 @@ impl GlobalState { let (crate_graph, proc_macro_paths, ws_data) = { // Create crate graph from all the workspaces - let vfs = &mut self.vfs.write().0; - + let vfs = &self.vfs.read().0; let load = |path: &AbsPath| { let vfs_path = vfs::VfsPath::from(path.to_path_buf()); self.crate_graph_file_dependencies.insert(vfs_path.clone()); From 4d9be70709bc020347e75fde6d24086c45746709 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Mon, 3 Feb 2025 10:44:02 +0100 Subject: [PATCH 53/75] Split cache priming into distinct phases --- .../crates/ide-db/src/prime_caches.rs | 114 +++++++++++++----- .../crates/rust-analyzer/src/main_loop.rs | 15 ++- 2 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 0002fda0ba732..238b2b3c7d573 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -7,11 +7,12 @@ mod topologic_sort; use std::time::Duration; use hir::db::DefDatabase; +use itertools::Itertools; use crate::{ base_db::{ ra_salsa::{Database, ParallelDatabase, Snapshot}, - Cancelled, CrateId, SourceDatabase, SourceRootDatabase, + Cancelled, CrateId, SourceDatabase, }, symbol_index::SymbolsDatabase, FxIndexMap, RootDatabase, @@ -26,6 +27,7 @@ pub struct ParallelPrimeCachesProgress { pub crates_total: usize, /// the total number of crates that have finished priming pub crates_done: usize, + pub work_type: &'static str, } pub fn parallel_prime_caches( @@ -51,37 +53,28 @@ pub fn parallel_prime_caches( EndCrate { crate_id: CrateId }, } + // We split off def map computation from other work, + // as the def map is the relevant one. Once the defmaps are computed + // the project is ready to go, the other indices are just nice to have for some IDE features. + #[derive(PartialOrd, Ord, PartialEq, Eq, Copy, Clone)] + enum PrimingPhase { + DefMap, + ImportMap, + CrateSymbols, + } + let (work_sender, progress_receiver) = { let (progress_sender, progress_receiver) = crossbeam_channel::unbounded(); let (work_sender, work_receiver) = crossbeam_channel::unbounded(); - let graph = graph.clone(); - let local_roots = db.local_roots(); let prime_caches_worker = move |db: Snapshot<RootDatabase>| { - while let Ok((crate_id, crate_name)) = work_receiver.recv() { + while let Ok((crate_id, crate_name, kind)) = work_receiver.recv() { progress_sender .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?; - // Compute the DefMap and possibly ImportMap - let file_id = graph[crate_id].root_file_id; - let root_id = db.file_source_root(file_id); - if db.source_root(root_id).is_library { - db.crate_def_map(crate_id); - } else { - // This also computes the DefMap - db.import_map(crate_id); - } - - // Compute the symbol search index. - // This primes the cache for `ide_db::symbol_index::world_symbols()`. - // - // We do this for workspace crates only (members of local_roots), because doing it - // for all dependencies could be *very* unnecessarily slow in a large project. - // - // FIXME: We should do it unconditionally if the configuration is set to default to - // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we - // would need to pipe that configuration information down here. - if local_roots.contains(&root_id) { - db.crate_symbols(crate_id.into()); + match kind { + PrimingPhase::DefMap => _ = db.crate_def_map(crate_id), + PrimingPhase::ImportMap => _ = db.import_map(crate_id), + PrimingPhase::CrateSymbols => _ = db.crate_symbols(crate_id.into()), } progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?; @@ -112,16 +105,30 @@ pub fn parallel_prime_caches( let mut crates_currently_indexing = FxIndexMap::with_capacity_and_hasher(num_worker_threads, Default::default()); + let mut additional_phases = vec![]; + while crates_done < crates_total { db.unwind_if_cancelled(); for crate_id in &mut crates_to_prime { - work_sender - .send(( - crate_id, - graph[crate_id].display_name.as_deref().unwrap_or_default().to_owned(), - )) - .ok(); + let krate = &graph[crate_id]; + let name = krate.display_name.as_deref().unwrap_or_default().to_owned(); + if krate.origin.is_lang() { + additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap)); + } else if krate.origin.is_local() { + // Compute the symbol search index. + // This primes the cache for `ide_db::symbol_index::world_symbols()`. + // + // We do this for workspace crates only (members of local_roots), because doing it + // for all dependencies could be *very* unnecessarily slow in a large project. + // + // FIXME: We should do it unconditionally if the configuration is set to default to + // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we + // would need to pipe that configuration information down here. + additional_phases.push((crate_id, name.clone(), PrimingPhase::CrateSymbols)); + } + + work_sender.send((crate_id, name, PrimingPhase::DefMap)).ok(); } // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision @@ -153,6 +160,51 @@ pub fn parallel_prime_caches( crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), crates_done, crates_total, + work_type: "Indexing", + }; + + cb(progress); + } + + let mut crates_done = 0; + let crates_total = additional_phases.len(); + for w in additional_phases.into_iter().sorted_by_key(|&(_, _, phase)| phase) { + work_sender.send(w).ok(); + } + + while crates_done < crates_total { + db.unwind_if_cancelled(); + + // recv_timeout is somewhat a hack, we need a way to from this thread check to see if the current salsa revision + // is cancelled on a regular basis. workers will only exit if they are processing a task that is cancelled, or + // if this thread exits, and closes the work channel. + let worker_progress = match progress_receiver.recv_timeout(Duration::from_millis(10)) { + Ok(p) => p, + Err(crossbeam_channel::RecvTimeoutError::Timeout) => { + continue; + } + Err(crossbeam_channel::RecvTimeoutError::Disconnected) => { + // our workers may have died from a cancelled task, so we'll check and re-raise here. + db.unwind_if_cancelled(); + break; + } + }; + match worker_progress { + ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name } => { + crates_currently_indexing.insert(crate_id, crate_name); + } + ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { + crates_currently_indexing.swap_remove(&crate_id); + crates_to_prime.mark_done(crate_id); + crates_done += 1; + } + }; + + let progress = ParallelPrimeCachesProgress { + crates_currently_indexing: crates_currently_indexing.values().cloned().collect(), + crates_done, + crates_total, + work_type: "Populating symbols", }; cb(progress); 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 de0ee639ca55a..d4a90181efedd 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 @@ -325,17 +325,19 @@ impl GlobalState { } for progress in prime_caches_progress { - let (state, message, fraction); + let (state, message, fraction, title); match progress { PrimeCachesProgress::Begin => { state = Progress::Begin; message = None; fraction = 0.0; + title = "Indexing"; } PrimeCachesProgress::Report(report) => { state = Progress::Report; + title = report.work_type; - message = match &report.crates_currently_indexing[..] { + message = match &*report.crates_currently_indexing { [crate_name] => Some(format!( "{}/{} ({crate_name})", report.crates_done, report.crates_total @@ -356,6 +358,7 @@ impl GlobalState { state = Progress::End; message = None; fraction = 1.0; + title = "Indexing"; self.prime_caches_queue.op_completed(()); if cancelled { @@ -365,7 +368,13 @@ impl GlobalState { } }; - self.report_progress("Indexing", state, message, Some(fraction), None); + self.report_progress( + title, + state, + message, + Some(fraction), + Some("rustAnalyzer/cachePriming".to_owned()), + ); } } Event::Vfs(message) => { From 806ec889633d725898fbb0fa67dbbd25ed88a01c Mon Sep 17 00:00:00 2001 From: Ali Bektas <bektasali@protonmail.com> Date: Wed, 29 Jan 2025 00:56:37 +0100 Subject: [PATCH 54/75] Revert tests::patterns::infer_pattern And apply requested changes --- .../crates/hir-ty/src/infer/expr.rs | 9 ++-- .../crates/hir-ty/src/infer/pat.rs | 48 +++++++++---------- .../rust-analyzer/crates/hir-ty/src/lib.rs | 11 ++--- .../crates/hir-ty/src/tests/patterns.rs | 4 +- 4 files changed, 35 insertions(+), 37 deletions(-) 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 a8753628afad4..86e5afdb50923 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 @@ -334,7 +334,11 @@ impl InferenceContext<'_> { ExprIsRead::No }; let input_ty = self.infer_expr(expr, &Expectation::none(), child_is_read); - self.infer_top_pat(pat, &input_ty, None); + self.infer_top_pat( + pat, + &input_ty, + Some(DeclContext { origin: DeclOrigin::LetExpr }), + ); self.result.standard_types.bool_.clone() } Expr::Block { statements, tail, label, id } => { @@ -1633,8 +1637,7 @@ impl InferenceContext<'_> { }; let decl = DeclContext { - has_else: else_branch.is_some(), - origin: DeclOrigin::LocalDecl, + origin: DeclOrigin::LocalDecl { has_else: else_branch.is_some() }, }; this.infer_top_pat(*pat, &ty, Some(decl)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 98693de4bb612..5ff22bea34dea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -498,7 +498,7 @@ impl InferenceContext<'_> { // If `expected` is an infer ty, we try to equate it to an array if the given pattern // allows it. See issue #16609 - if self.decl_allows_array_type_infer(decl) && expected.is_ty_var() { + if self.pat_is_irrefutable(decl) && expected.is_ty_var() { if let Some(resolved_array_ty) = self.try_resolve_slice_ty_to_array_ty(prefix, suffix, slice) { @@ -601,42 +601,38 @@ impl InferenceContext<'_> { Some(array_ty) } - /// Determines whether we can infer the expected type in the slice pattern to be of type array. + /// Used to determine whether we can infer the expected type in the slice pattern to be of type array. /// This is only possible if we're in an irrefutable pattern. If we were to allow this in refutable /// patterns we wouldn't e.g. report ambiguity in the following situation: /// /// ```ignore(rust) - /// struct Zeroes; - /// const ARR: [usize; 2] = [0; 2]; - /// const ARR2: [usize; 2] = [2; 2]; + /// struct Zeroes; + /// const ARR: [usize; 2] = [0; 2]; + /// const ARR2: [usize; 2] = [2; 2]; /// - /// impl Into<&'static [usize; 2]> for Zeroes { - /// fn into(self) -> &'static [usize; 2] { + /// impl Into<&'static [usize; 2]> for Zeroes { + /// fn into(self) -> &'static [usize; 2] { /// &ARR - /// } - /// } + /// } + /// } /// - /// impl Into<&'static [usize]> for Zeroes { - /// fn into(self) -> &'static [usize] { - /// &ARR2 - /// } - /// } + /// impl Into<&'static [usize]> for Zeroes { + /// fn into(self) -> &'static [usize] { + /// &ARR2 + /// } + /// } /// - /// fn main() { - /// let &[a, b]: &[usize] = Zeroes.into() else { - /// .. - /// }; - /// } + /// fn main() { + /// let &[a, b]: &[usize] = Zeroes.into() else { + /// .. + /// }; + /// } /// ``` /// /// If we're in an irrefutable pattern we prefer the array impl candidate given that - /// the slice impl candidate would be be rejected anyway (if no ambiguity existed). - fn decl_allows_array_type_infer(&self, decl_ctxt: Option<DeclContext>) -> bool { - if let Some(decl_ctxt) = decl_ctxt { - !decl_ctxt.has_else && matches!(decl_ctxt.origin, DeclOrigin::LocalDecl) - } else { - false - } + /// the slice impl candidate would be rejected anyway (if no ambiguity existed). + fn pat_is_irrefutable(&self, decl_ctxt: Option<DeclContext>) -> bool { + matches!(decl_ctxt, Some(DeclContext { origin: DeclOrigin::LocalDecl { has_else: false } })) } } 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 acac7ddcad3cf..55d81875a2be4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -1052,18 +1052,17 @@ pub fn known_const_to_ast( #[derive(Debug, Copy, Clone)] pub(crate) enum DeclOrigin { - // from an `if let` expression LetExpr, - // from `let x = ..` - LocalDecl, + /// from `let x = ..` + LocalDecl { + has_else: bool, + }, } /// Provides context for checking patterns in declarations. More specifically this /// allows us to infer array types if the pattern is irrefutable and allows us to infer -/// the size of the array. See issue #76342. +/// the size of the array. See issue rust-lang/rust#76342. #[derive(Debug, Copy, Clone)] pub(crate) struct DeclContext { - // whether we're in a let-else context - pub(crate) has_else: bool, pub(crate) origin: DeclOrigin, } 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 50bc47cf673a4..4949d4016bf15 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 @@ -67,9 +67,9 @@ fn infer_pattern() { 143..144 'e': {unknown} 157..204 'if let... }': () 160..175 'let [val] = opt': bool - 164..169 '[val]': [{unknown}; 1] + 164..169 '[val]': [{unknown}] 165..168 'val': {unknown} - 172..175 'opt': [{unknown}; 1] + 172..175 'opt': [{unknown}] 176..204 '{ ... }': () 190..191 'h': {unknown} 194..197 'val': {unknown} From 5acbff815bf1c0e8049d87ab6a08723a925c02ac Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Mon, 3 Feb 2025 11:42:04 +0100 Subject: [PATCH 55/75] Do not use make use of `InferenceResult::has_errors` flag for mir building It generaly does not work as expected right now as we fallback type parameters to errors --- .../rust-analyzer/crates/hir-ty/src/infer.rs | 3 +++ .../crates/hir-ty/src/method_resolution.rs | 21 +++++++++++++++---- .../crates/hir-ty/src/mir/lower.rs | 2 +- .../src/handlers/incorrect_case.rs | 2 ++ .../src/handlers/mutability_errors.rs | 3 ++- 5 files changed, 25 insertions(+), 6 deletions(-) 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 3c258e3c4cf4b..f4a018e2eec15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -466,6 +466,9 @@ pub struct InferenceResult { pub type_of_for_iterator: FxHashMap<ExprId, Ty>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>, /// Whether there are any type-mismatching errors in the result. + // FIXME: This isn't as useful as initially thought due to us falling back placeholders to + // `TyKind::Error`. + // Which will then mark this field. pub(crate) has_errors: bool, /// Interned common types to return references to. // FIXME: Move this into `InferenceContext` 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 1cea67ee96419..db94351dcc995 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 @@ -4,6 +4,7 @@ //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. use std::ops::ControlFlow; +use arrayvec::ArrayVec; use base_db::CrateId; use chalk_ir::{cast::Cast, UniverseIndex, WithKind}; use hir_def::{ @@ -732,15 +733,27 @@ fn lookup_impl_assoc_item_for_trait_ref( let self_ty = trait_ref.self_type_parameter(Interner); let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let impls = db.trait_impls_in_deps(env.krate); - let self_impls = match self_ty.kind(Interner) { - TyKind::Adt(id, _) => { - id.0.module(db.upcast()).containing_block().and_then(|it| db.trait_impls_in_block(it)) + + let trait_module = hir_trait_id.module(db.upcast()); + let type_module = match self_ty_fp { + TyFingerprint::Adt(adt_id) => Some(adt_id.module(db.upcast())), + TyFingerprint::ForeignType(type_id) => { + Some(from_foreign_def_id(type_id).module(db.upcast())) } + TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db.upcast())), _ => None, }; + + let def_blocks: ArrayVec<_, 2> = + [trait_module.containing_block(), type_module.and_then(|it| it.containing_block())] + .into_iter() + .flatten() + .filter_map(|block_id| db.trait_impls_in_block(block_id)) + .collect(); + let impls = impls .iter() - .chain(self_impls.as_ref()) + .chain(&def_blocks) .flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp)); let table = InferenceTable::new(db, env); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index cc6ed122af4bb..549450e9bebd7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2156,7 +2156,7 @@ pub fn lower_to_mir( // need to take this input explicitly. root_expr: ExprId, ) -> Result<MirBody> { - if infer.has_errors { + if infer.type_mismatches().next().is_some() { return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 0cc80bda2c8a0..246330e6efaac 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -936,6 +936,7 @@ fn func() { fn override_lint_level() { check_diagnostics( r#" +#![allow(unused_variables)] #[warn(nonstandard_style)] fn foo() { let BAR; @@ -992,6 +993,7 @@ struct QUX; const foo: i32 = 0; fn BAR() { let BAZ; + _ = BAZ; } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 139797914440c..0e3c4c7aa3642 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -831,13 +831,14 @@ fn f() { #[test] fn or_pattern() { - // FIXME: `None` is inferred as unknown here for some reason check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; + //^^^^^ 💡 warn: variable does not need to be mutable + f(x); } "#, From a118b18894304d0fd13888a414a6dc415d553277 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Mon, 3 Feb 2025 13:28:14 +0100 Subject: [PATCH 56/75] Fix some mir eval/lowerings --- .../rust-analyzer/.git-blame-ignore-revs | 1 + .../crates/hir-def/src/lang_item.rs | 1 + .../crates/hir-ty/src/mir/eval.rs | 11 +-- .../crates/hir-ty/src/mir/eval/shim.rs | 79 ++++++++++++++++++- .../crates/intern/src/symbol/symbols.rs | 1 + 5 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/.git-blame-ignore-revs b/src/tools/rust-analyzer/.git-blame-ignore-revs index 2ccdc8c042563..651502965ef7a 100644 --- a/src/tools/rust-analyzer/.git-blame-ignore-revs +++ b/src/tools/rust-analyzer/.git-blame-ignore-revs @@ -14,3 +14,4 @@ f247090558c9ba3c551566eae5882b7ca865225f b2f6fd4f961fc7e4fbfdb80cae2e6065f8436f15 c48062fe2ab9a2d913d1985a6b0aec4bf936bfc1 f532576ac53ddcc666bc8d59e0b6437065e2f599 +4704881b641884de50645637108b6b6f5b68aaf9 diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index e83ce6dc42cee..166e00c9d20b3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -501,4 +501,5 @@ language_item_table! { String, sym::String, string, Target::Struct, GenericRequirement::None; CStr, sym::CStr, c_str, Target::Struct, GenericRequirement::None; + Ordering, sym::Ordering, ordering, Target::Enum, GenericRequirement::None; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 8e4c4db102064..6b20522cf34c4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -1644,14 +1644,15 @@ impl Evaluator<'_> { Variants::Multiple { tag, tag_encoding, variants, .. } => { let size = tag.size(&*self.target_data_layout).bytes_usize(); let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field + let is_signed = tag.is_signed(); match tag_encoding { TagEncoding::Direct => { let tag = &bytes[offset..offset + size]; - Ok(i128::from_le_bytes(pad16(tag, false))) + Ok(i128::from_le_bytes(pad16(tag, is_signed))) } TagEncoding::Niche { untagged_variant, niche_start, .. } => { let tag = &bytes[offset..offset + size]; - let candidate_tag = i128::from_le_bytes(pad16(tag, false)) + let candidate_tag = i128::from_le_bytes(pad16(tag, is_signed)) .wrapping_sub(*niche_start as i128) as usize; let idx = variants @@ -2943,10 +2944,10 @@ pub fn render_const_using_debug_impl( // a3 = ::core::fmt::Arguments::new_v1(a1, a2) // FIXME: similarly, we should call function here, not directly working with memory. let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size())?; - evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?; + evaluator.write_memory(a3, &a1.to_bytes())?; + evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?; + evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?; evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?; - evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?; - evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?; let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully( db.upcast(), &hir_def::path::Path::from_known_path_with_no_generic(path![std::fmt::format]), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 0a78f4a5b24b7..38b189a517f2e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1,11 +1,12 @@ //! Interpret intrinsics, lang items and `extern "C"` wellknown functions which their implementation //! is not available. //! -use std::cmp; +use std::cmp::{self, Ordering}; use chalk_ir::TyKind; use hir_def::{ builtin_type::{BuiltinInt, BuiltinUint}, + lang_item::LangItemTarget, resolver::HasResolver, }; use hir_expand::name::Name; @@ -1317,6 +1318,82 @@ impl Evaluator<'_> { self.write_memory_using_ref(dst, size)?.fill(val); Ok(()) } + "ptr_metadata" => { + let [ptr] = args else { + return Err(MirEvalError::InternalError( + "ptr_metadata args are not provided".into(), + )); + }; + let arg = ptr.interval.get(self)?.to_owned(); + let metadata = &arg[self.ptr_size()..]; + destination.write_from_bytes(self, metadata)?; + Ok(()) + } + "three_way_compare" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::InternalError( + "three_way_compare args are not provided".into(), + )); + }; + let Some(ty) = + generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)) + else { + return Err(MirEvalError::InternalError( + "three_way_compare generic arg is not provided".into(), + )); + }; + let signed = match ty.as_builtin().unwrap() { + BuiltinType::Int(_) => true, + BuiltinType::Uint(_) => false, + _ => { + return Err(MirEvalError::InternalError( + "three_way_compare expects an integral type".into(), + )) + } + }; + let rhs = rhs.get(self)?; + let lhs = lhs.get(self)?; + let mut result = Ordering::Equal; + for (l, r) in lhs.iter().zip(rhs).rev() { + let it = l.cmp(r); + if it != Ordering::Equal { + result = it; + break; + } + } + if signed { + if let Some((&l, &r)) = lhs.iter().zip(rhs).next_back() { + if l != r { + result = (l as i8).cmp(&(r as i8)); + } + } + } + if let Some(LangItemTarget::EnumId(e)) = + self.db.lang_item(self.crate_id, LangItem::Ordering) + { + let ty = self.db.ty(e.into()); + let r = self + .compute_discriminant(ty.skip_binders().clone(), &[result as i8 as u8])?; + destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?; + Ok(()) + } else { + Err(MirEvalError::InternalError("Ordering enum not found".into())) + } + } + "aggregate_raw_ptr" => { + let [data, meta] = args else { + return Err(MirEvalError::InternalError( + "aggregate_raw_ptr args are not provided".into(), + )); + }; + destination.write_from_interval(self, data.interval)?; + Interval { + addr: destination.addr.offset(data.interval.size), + size: destination.size - data.interval.size, + } + .write_from_interval(self, meta.interval)?; + Ok(()) + } _ if needs_override => not_supported!("intrinsic {name} is not implemented"), _ => return Ok(false), } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 1b543ddf8110e..8df7c59aa7ad7 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -347,6 +347,7 @@ define_symbols! { option, Option, Ord, + Ordering, Output, CallRefFuture, CallOnceFuture, From a373fb214d9f1305b96144607077c65443f11109 Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Mon, 3 Feb 2025 11:05:49 +0100 Subject: [PATCH 57/75] Expose symbol of `CrateName` --- src/tools/rust-analyzer/crates/base-db/src/input.rs | 10 +++++----- .../rust-analyzer/crates/hir-def/src/import_map.rs | 2 +- .../rust-analyzer/crates/hir-def/src/nameres.rs | 2 +- .../rust-analyzer/crates/hir-expand/src/name.rs | 2 +- .../hir-expand/src/prettify_macro_expansion_.rs | 4 ++-- .../rust-analyzer/crates/ide-db/src/prime_caches.rs | 13 ++++++++----- .../crates/ide/src/view_crate_graph.rs | 3 ++- .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/project-model/src/project_json.rs | 2 +- .../rust-analyzer/crates/project-model/src/tests.rs | 2 +- .../crates/rust-analyzer/src/cli/diagnostics.rs | 4 ++-- .../rust-analyzer/src/cli/unresolved_references.rs | 4 ++-- .../crates/rust-analyzer/src/main_loop.rs | 8 +++++--- .../rust-analyzer/crates/test-fixture/src/lib.rs | 10 +--------- 14 files changed, 33 insertions(+), 34 deletions(-) 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 a0fc8c31eaf62..c2cea07190586 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -129,9 +129,9 @@ impl fmt::Display for CrateName { } impl ops::Deref for CrateName { - type Target = str; - fn deref(&self) -> &str { - self.0.as_str() + type Target = Symbol; + fn deref(&self) -> &Symbol { + &self.0 } } @@ -230,8 +230,8 @@ impl fmt::Display for CrateDisplayName { } impl ops::Deref for CrateDisplayName { - type Target = str; - fn deref(&self) -> &str { + type Target = Symbol; + fn deref(&self) -> &Symbol { &self.crate_name } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index 34635997bdff3..6137bd34d64a0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -519,7 +519,7 @@ mod tests { crate_graph[krate] .display_name .as_ref() - .is_some_and(|it| &**it.crate_name() == crate_name) + .is_some_and(|it| it.crate_name().as_str() == crate_name) }) .expect("could not find crate"); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 39d383f0159b1..3b6e3c5916e32 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -337,7 +337,7 @@ impl DefMap { pub(crate) fn crate_def_map_query(db: &dyn DefDatabase, crate_id: CrateId) -> Arc<DefMap> { let crate_graph = db.crate_graph(); let krate = &crate_graph[crate_id]; - let name = krate.display_name.as_deref().unwrap_or_default(); + let name = krate.display_name.as_deref().map(Symbol::as_str).unwrap_or_default(); let _p = tracing::info_span!("crate_def_map_query", ?name).entered(); let module_data = ModuleData::new( 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 848870c3a3844..21e5fb5ef9e1c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -262,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_root(&self.name) + Name::new_symbol_root((*self.name).clone()) } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 6ff7831fd81a2..c744fbce77b7c 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -41,9 +41,9 @@ pub fn prettify_macro_expansion( } else if let Some(dep) = target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) { - make::tokens::ident(&dep.name) + make::tokens::ident(dep.name.as_str()) } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name { - make::tokens::ident(crate_name.crate_name()) + make::tokens::ident(crate_name.crate_name().as_str()) } else { return dollar_crate.clone(); } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 238b2b3c7d573..22dc3d9e29d65 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -6,7 +6,7 @@ mod topologic_sort; use std::time::Duration; -use hir::db::DefDatabase; +use hir::{db::DefDatabase, Symbol}; use itertools::Itertools; use crate::{ @@ -22,7 +22,7 @@ use crate::{ #[derive(Debug)] pub struct ParallelPrimeCachesProgress { /// the crates that we are currently priming. - pub crates_currently_indexing: Vec<String>, + pub crates_currently_indexing: Vec<Symbol>, /// the total number of crates we want to prime. pub crates_total: usize, /// the total number of crates that have finished priming @@ -49,7 +49,7 @@ pub fn parallel_prime_caches( }; enum ParallelPrimeCacheWorkerProgress { - BeginCrate { crate_id: CrateId, crate_name: String }, + BeginCrate { crate_id: CrateId, crate_name: Symbol }, EndCrate { crate_id: CrateId }, } @@ -112,7 +112,11 @@ pub fn parallel_prime_caches( for crate_id in &mut crates_to_prime { let krate = &graph[crate_id]; - let name = krate.display_name.as_deref().unwrap_or_default().to_owned(); + let name = krate + .display_name + .as_deref() + .cloned() + .unwrap_or_else(|| Symbol::integer(crate_id.into_raw().into_u32() as usize)); if krate.origin.is_lang() { additional_phases.push((crate_id, name.clone(), PrimingPhase::ImportMap)); } else if krate.origin.is_local() { @@ -195,7 +199,6 @@ pub fn parallel_prime_caches( } ParallelPrimeCacheWorkerProgress::EndCrate { crate_id } => { crates_currently_indexing.swap_remove(&crate_id); - crates_to_prime.mark_done(crate_id); crates_done += 1; } }; diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 24a986b85bf47..eb6eb7da1e90a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -84,7 +84,8 @@ impl<'a> dot::Labeller<'a, CrateId, Edge<'a>> for DotCrateGraph { } fn node_label(&'a self, n: &CrateId) -> LabelText<'a> { - let name = self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name); + let name = + self.graph[*n].display_name.as_ref().map_or("(unnamed crate)", |name| name.as_str()); LabelText::LabelStr(name.into()) } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 1b543ddf8110e..fce4feb01d74f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -480,6 +480,7 @@ define_symbols! { u64, u8, unadjusted, + unknown, Unknown, unpin, unreachable_2015, 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 a396396761041..feee40a1fc9b2 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 @@ -508,5 +508,5 @@ fn serialize_crate_name<S>(name: &CrateName, se: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { - se.serialize_str(name) + se.serialize_str(name.as_str()) } 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 f111383112593..2856086543666 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -230,7 +230,7 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { let crate_data = &crate_graph[crate_id]; // Assert that the project crate with `is_proc_macro` has a dependency // on the proc_macro sysroot crate. - crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); + crate_data.dependencies.iter().find(|&dep| *dep.name.deref() == sym::proc_macro).unwrap(); } #[test] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index 28f25975d64aa..6a3ceb640b91a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -4,7 +4,7 @@ use project_model::{CargoConfig, RustLibSource}; use rustc_hash::FxHashSet; -use hir::{db::HirDatabase, Crate, HirFileIdExt, Module}; +use hir::{db::HirDatabase, sym, Crate, HirFileIdExt, Module}; use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity}; use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase}; use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; @@ -60,7 +60,7 @@ impl flags::Diagnostics { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { let crate_name = - module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); println!( "processing crate: {crate_name}, module: {}", _vfs.file_path(file_id.into()) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs index 986bd018b424d..021b1bff39301 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -1,5 +1,5 @@ //! Reports references in code that the IDE layer cannot resolve. -use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; +use hir::{db::HirDatabase, sym, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; use ide::{AnalysisHost, RootDatabase, TextRange}; use ide_db::{ base_db::{SourceDatabase, SourceRootDatabase}, @@ -66,7 +66,7 @@ impl flags::UnresolvedReferences { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) { let crate_name = - module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned(); let file_path = vfs.file_path(file_id.into()); eprintln!("processing crate: {crate_name}, module: {file_path}",); 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 d4a90181efedd..ebc65373b5226 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 @@ -339,14 +339,16 @@ impl GlobalState { message = match &*report.crates_currently_indexing { [crate_name] => Some(format!( - "{}/{} ({crate_name})", - report.crates_done, report.crates_total + "{}/{} ({})", + report.crates_done, + report.crates_total, + crate_name.as_str(), )), [crate_name, rest @ ..] => Some(format!( "{}/{} ({} + {} more)", report.crates_done, report.crates_total, - crate_name, + crate_name.as_str(), rest.len() )), _ => None, diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 866379d940e4f..ca596583590a6 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -258,15 +258,7 @@ impl ChangeFixture { let to_id = crates[&to]; let sysroot = crate_graph[to_id].origin.is_lang(); crate_graph - .add_dep( - from_id, - Dependency::with_prelude( - CrateName::new(&to).unwrap(), - to_id, - prelude, - sysroot, - ), - ) + .add_dep(from_id, Dependency::with_prelude(to.clone(), to_id, prelude, sysroot)) .unwrap(); } } From 1ce2d7eacaca018a741bdf39b852438f38da91ba Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Tue, 4 Feb 2025 14:43:01 +0100 Subject: [PATCH 58/75] Prevent panics from tearing down worker threads --- .../crates/rust-analyzer/src/task_pool.rs | 6 ++++-- .../crates/stdx/src/thread/pool.rs | 21 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 2bcd8505e8100..c5de69bb9fc83 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -1,6 +1,8 @@ //! A thin wrapper around [`stdx::thread::Pool`] which threads a sender through spawned jobs. //! It is used in [`crate::global_state::GlobalState`] throughout the main loop. +use std::panic::UnwindSafe; + use crossbeam_channel::Sender; use stdx::thread::{Pool, ThreadIntent}; @@ -18,7 +20,7 @@ impl<T> TaskPool<T> { pub(crate) fn spawn<F>(&mut self, intent: ThreadIntent, task: F) where - F: FnOnce() -> T + Send + 'static, + F: FnOnce() -> T + Send + UnwindSafe + 'static, T: Send + 'static, { self.pool.spawn(intent, { @@ -29,7 +31,7 @@ impl<T> TaskPool<T> { pub(crate) fn spawn_with_sender<F>(&mut self, intent: ThreadIntent, task: F) where - F: FnOnce(Sender<T>) + Send + 'static, + F: FnOnce(Sender<T>) + Send + UnwindSafe + 'static, T: Send + 'static, { self.pool.spawn(intent, { diff --git a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs index 2ddd7da74c291..9acc1de922af9 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/thread/pool.rs @@ -7,9 +7,12 @@ //! The thread pool is implemented entirely using //! the threading utilities in [`crate::thread`]. -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, +use std::{ + panic::{self, UnwindSafe}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; use crossbeam_channel::{Receiver, Sender}; @@ -25,13 +28,13 @@ pub struct Pool { // so that the channel is actually closed // before we join the worker threads! job_sender: Sender<Job>, - _handles: Vec<JoinHandle>, + _handles: Box<[JoinHandle]>, extant_tasks: Arc<AtomicUsize>, } struct Job { requested_intent: ThreadIntent, - f: Box<dyn FnOnce() + Send + 'static>, + f: Box<dyn FnOnce() + Send + UnwindSafe + 'static>, } impl Pool { @@ -47,6 +50,7 @@ impl Pool { let handle = Builder::new(INITIAL_INTENT) .stack_size(STACK_SIZE) .name("Worker".into()) + .allow_leak(true) .spawn({ let extant_tasks = Arc::clone(&extant_tasks); let job_receiver: Receiver<Job> = job_receiver.clone(); @@ -58,7 +62,8 @@ impl Pool { current_intent = job.requested_intent; } extant_tasks.fetch_add(1, Ordering::SeqCst); - (job.f)(); + // discard the panic, we should've logged the backtrace already + _ = panic::catch_unwind(job.f); extant_tasks.fetch_sub(1, Ordering::SeqCst); } } @@ -68,12 +73,12 @@ impl Pool { handles.push(handle); } - Pool { _handles: handles, extant_tasks, job_sender } + Pool { _handles: handles.into_boxed_slice(), extant_tasks, job_sender } } pub fn spawn<F>(&self, intent: ThreadIntent, f: F) where - F: FnOnce() + Send + 'static, + F: FnOnce() + Send + UnwindSafe + 'static, { let f = Box::new(move || { if cfg!(debug_assertions) { From 9bf4fe4ab7281e213f78e02bcc8ebd421be83745 Mon Sep 17 00:00:00 2001 From: Havard Eidnes <he@NetBSD.org> Date: Tue, 4 Feb 2025 15:22:49 +0000 Subject: [PATCH 59/75] line-index: don't try to use neon on big-endian aarch64. --- src/tools/rust-analyzer/lib/line-index/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 6f0455ee98bad..bc87ada3eb55c 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -235,7 +235,7 @@ fn analyze_source_file_dispatch( } } -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] fn analyze_source_file_dispatch( src: &str, lines: &mut Vec<TextSize>, @@ -347,7 +347,7 @@ unsafe fn analyze_source_file_sse2( } #[target_feature(enable = "neon")] -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] #[inline] // See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon // @@ -362,7 +362,7 @@ unsafe fn move_mask(v: std::arch::aarch64::uint8x16_t) -> u64 { } #[target_feature(enable = "neon")] -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] unsafe fn analyze_source_file_neon( src: &str, lines: &mut Vec<TextSize>, @@ -441,7 +441,11 @@ unsafe fn analyze_source_file_neon( } } -#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] +#[cfg(not(any( + target_arch = "x86", + target_arch = "x86_64", + all(target_arch = "aarch64", target_endian = "little") +)))] // The target (or compiler version) does not support SSE2 ... fn analyze_source_file_dispatch( src: &str, From 54670912dad92dcb7cc8fbc91f4ce1c3fc4cb49d Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Tue, 4 Feb 2025 18:30:50 +0200 Subject: [PATCH 60/75] Fix IDE resolution of `use` inside a body We stopped the expression search too early because `use` is an item. --- .../crates/hir/src/source_analyzer.rs | 6 +++++- .../crates/ide/src/goto_definition.rs | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) 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 ca239826d4fcf..23e7518883b56 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -1252,7 +1252,11 @@ fn scope_for( node: InFile<&SyntaxNode>, ) -> Option<ScopeId> { node.ancestors_with_macros(db.upcast()) - .take_while(|it| !ast::Item::can_cast(it.kind()) || ast::MacroCall::can_cast(it.kind())) + .take_while(|it| { + !ast::Item::can_cast(it.kind()) + || ast::MacroCall::can_cast(it.kind()) + || ast::Use::can_cast(it.kind()) + }) .filter_map(|it| it.map(ast::Expr::cast).transpose()) .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr()) .find_map(|it| scopes.scope_for(it)) 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 45f694d2fb6e3..bdafb701ff534 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -3272,4 +3272,22 @@ fn f() { "#, ); } + + #[test] + fn use_inside_body() { + check( + r#" +fn main() { + mod nice_module { + pub(super) struct NiceStruct; + // ^^^^^^^^^^ + } + + use nice_module::NiceStruct$0; + + let _ = NiceStruct; +} + "#, + ); + } } From 9d81503252ccae45e19888e6f7c1e2a9dee8d0ee Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Tue, 4 Feb 2025 19:10:04 +0200 Subject: [PATCH 61/75] Fix a failing test The reason this test passed previously is not because it was working as intended, but because prior to the previous commit we did not resolve the `use` at all! Now, `use self as _` is invalid code anyway (it prints E0429), and because we fallback to the value namespace if we can't resolve in the type namespace (which is a reasonable behavior), this test now actually fails. I don't think we want to change the fallback, so I removed `use self as _` and instead added a new test, where the value can be resolved in the type namespace. --- .../rust-analyzer/crates/ide/src/rename.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 3d7ff5f76a01a..3e8295e3f08ee 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2001,19 +2001,37 @@ impl Foo { "foo", r#" fn f($0self) -> i32 { - use self as _; self.i } "#, r#" fn f(foo: _) -> i32 { - use self as _; foo.i } "#, ); } + #[test] + fn no_type_value_ns_confuse() { + // Test that we don't rename items from different namespaces. + check( + "bar", + r#" +struct foo {} +fn f(foo$0: i32) -> i32 { + use foo as _; +} +"#, + r#" +struct foo {} +fn f(bar: i32) -> i32 { + use foo as _; +} +"#, + ); + } + #[test] fn test_self_in_path_to_parameter() { check( From 578ae16c93268d280d0aca36a3642fa6aade72fa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman <chayimfr@gmail.com> Date: Tue, 4 Feb 2025 21:28:19 +0200 Subject: [PATCH 62/75] Add a FIXME for feature(adt_const_params) --- src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs | 1 + 1 file changed, 1 insertion(+) 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 836a236444cd2..7839589994b2f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -124,6 +124,7 @@ pub(crate) fn path_to_const<'g>( ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)), expected_ty, )), + // FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors. _ => None, } } From f57caa1cbb452e5dc551fbbf4576fbdd1b6895fb Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 5 Feb 2025 08:22:20 +0100 Subject: [PATCH 63/75] Bump proc-macro2 in xtask --- src/tools/rust-analyzer/Cargo.lock | 4 ++-- src/tools/rust-analyzer/xtask/Cargo.toml | 3 +-- src/tools/rust-analyzer/xtask/src/codegen/grammar.rs | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 3ba7df3ad5103..bf408d1a52b8c 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1408,9 +1408,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index b505ee835b281..ebd8903ad8ac3 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -16,8 +16,7 @@ xflags = "0.3.0" time = { version = "0.3", default-features = false } zip = { version = "0.6", default-features = false, features = ["deflate", "time"] } stdx.workspace = true -# https://github.com/dtolnay/proc-macro2/issues/475 -proc-macro2 = "=1.0.86" +proc-macro2 = "1.0.93" quote = "1.0.20" ungrammar = "1.16.1" either.workspace = true diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index d5fe32367899e..e20dda7fec3bb 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -397,6 +397,9 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { if "{}[]()".contains(token) { let c = token.chars().next().unwrap(); quote! { #c } + // underscore is an identifier in the proc-macro api + } else if *token == "_" { + quote! { _ } } else { let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint)); quote! { #(#cs)* } From 9bcb2b08be762f2c984b706e459a443588e73dcc Mon Sep 17 00:00:00 2001 From: Lukas Wirth <lukastw97@gmail.com> Date: Wed, 5 Feb 2025 11:11:14 +0100 Subject: [PATCH 64/75] Use interior mutability for loaded `ProcMacrorv::expanders` --- .../proc-macro-srv-cli/src/main_loop.rs | 10 ++--- .../crates/proc-macro-srv/src/lib.rs | 39 ++++++++++++------- .../crates/proc-macro-srv/src/tests/utils.rs | 2 +- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index ba1fcd8e336a5..569070766f1c6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -21,18 +21,16 @@ pub(crate) fn run() -> io::Result<()> { } } - let read_request = - |buf: &mut String| msg::Request::read(read_json, &mut io::stdin().lock(), buf); - + let mut buf = String::new(); + let mut read_request = || msg::Request::read(read_json, &mut io::stdin().lock(), &mut buf); let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock()); let env = EnvSnapshot::default(); - let mut srv = proc_macro_srv::ProcMacroSrv::new(&env); - let mut buf = String::new(); + let srv = proc_macro_srv::ProcMacroSrv::new(&env); let mut span_mode = SpanMode::Id; - while let Some(req) = read_request(&mut buf)? { + while let Some(req) = read_request()? { let res = match req { msg::Request::ListMacros { dylib_path } => { msg::Response::ListMacros(srv.list_macros(&dylib_path).map(|macros| { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 7ae75713ebfe9..f28821b4afc5c 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 @@ -35,6 +35,7 @@ use std::{ ffi::OsString, fs, path::{Path, PathBuf}, + sync::{Arc, Mutex, PoisonError}, thread, }; @@ -53,7 +54,7 @@ pub enum ProcMacroKind { pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION"); pub struct ProcMacroSrv<'env> { - expanders: HashMap<Utf8PathBuf, dylib::Expander>, + expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>, env: &'env EnvSnapshot, } @@ -67,7 +68,7 @@ const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv<'_> { pub fn expand<S: ProcMacroSrvSpan>( - &mut self, + &self, lib: impl AsRef<Utf8Path>, env: Vec<(String, String)>, current_dir: Option<impl AsRef<Path>>, @@ -118,29 +119,37 @@ impl ProcMacroSrv<'_> { } pub fn list_macros( - &mut self, + &self, dylib_path: &Utf8Path, ) -> Result<Vec<(String, ProcMacroKind)>, String> { let expander = self.expander(dylib_path)?; Ok(expander.list_macros()) } - fn expander(&mut self, path: &Utf8Path) -> Result<&dylib::Expander, String> { + fn expander(&self, path: &Utf8Path) -> Result<Arc<dylib::Expander>, String> { let expander = || { - dylib::Expander::new(path) - .map_err(|err| format!("Cannot create expander for {path}: {err}",)) + let expander = dylib::Expander::new(path) + .map_err(|err| format!("Cannot create expander for {path}: {err}",)); + expander.map(Arc::new) }; - Ok(match self.expanders.entry(path.to_path_buf()) { - Entry::Vacant(v) => v.insert(expander()?), - Entry::Occupied(mut e) => { - let time = fs::metadata(path).and_then(|it| it.modified()).ok(); - if Some(e.get().modified_time()) != time { - e.insert(expander()?); + Ok( + match self + .expanders + .lock() + .unwrap_or_else(PoisonError::into_inner) + .entry(path.to_path_buf()) + { + Entry::Vacant(v) => v.insert(expander()?).clone(), + Entry::Occupied(mut e) => { + let time = fs::metadata(path).and_then(|it| it.modified()).ok(); + if Some(e.get().modified_time()) != time { + e.insert(expander()?); + } + e.get().clone() } - e.into_mut() - } - }) + }, + ) } } 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 4ce4544243ab5..1b085520d5656 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 @@ -107,7 +107,7 @@ fn assert_expand_impl( pub(crate) fn list() -> Vec<String> { let dylib_path = proc_macro_test_dylib_path(); let env = EnvSnapshot::default(); - let mut srv = ProcMacroSrv::new(&env); + let srv = ProcMacroSrv::new(&env); let res = srv.list_macros(&dylib_path).unwrap(); res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect() } From 98b16ead7c9a958c8332ee13e9772ee7e93fdda0 Mon Sep 17 00:00:00 2001 From: jnyfah <jnyfaah@gmail.com> Date: Thu, 6 Feb 2025 15:55:56 +0100 Subject: [PATCH 65/75] closure parameter inlay hints --- .../crates/ide/src/inlay_hints.rs | 2 ++ .../crates/ide/src/inlay_hints/bind_pat.rs | 35 +++++++++++++++++++ .../ide/src/inlay_hints/closure_captures.rs | 2 +- .../crates/ide/src/static_index.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + .../crates/rust-analyzer/src/config.rs | 5 +++ .../docs/book/src/configuration_generated.md | 5 +++ .../rust-analyzer/editors/code/package.json | 10 ++++++ 8 files changed, 60 insertions(+), 1 deletion(-) 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 5955876d44681..63039b1cd34e0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -294,6 +294,7 @@ pub struct InlayHintsConfig { pub param_names_for_lifetime_elision_hints: bool, pub hide_named_constructor_hints: bool, pub hide_closure_initialization_hints: bool, + pub hide_closure_parameter_hints: bool, pub range_exclusive_hints: bool, pub closure_style: ClosureStyle, pub max_length: Option<usize>, @@ -860,6 +861,7 @@ mod tests { binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, max_length: None, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 01a1a4545c478..fd91cbb0979d7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -36,6 +36,11 @@ pub(super) fn hints( if it.ty().is_some() { return None; } + if config.hide_closure_parameter_hints { + if it.syntax().ancestors().any(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { + return None; + } + } Some(it.colon_token()) }, ast::LetStmt(it) => { @@ -949,6 +954,36 @@ fn bar(f: impl FnOnce(u8) -> u8) -> impl FnOnce(u8) -> u8 { ); } + #[test] + fn skip_closure_parameter_hints() { + check_with_config( + InlayHintsConfig { + type_hints: true, + hide_closure_parameter_hints: true, + ..DISABLED_CONFIG + }, + r#" +//- minicore: fn +struct Foo; +impl Foo { + fn foo(self: Self) {} + fn bar(self: &Self) {} +} +fn main() { + let closure = |x, y| x + y; + // ^^^^^^^ impl Fn(i32, i32) -> {unknown} + closure(2, 3); + let point = (10, 20); + // ^^^^^ (i32, i32) + let (x, y) = point; + // ^ i32 ^ i32 + Foo::foo(Foo); + Foo::bar(&Foo); +} +"#, + ); + } + #[test] fn hint_truncation() { check_with_config( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 3e91618d08e6f..9b981c0a3acf7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -1,4 +1,4 @@ -//! Implementation of "closure return type" inlay hints. +//! Implementation of "closure captures" inlay hints. //! //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; 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 3121bb7a8e31e..07553a87d28fc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -154,6 +154,7 @@ impl StaticIndex<'_> { implicit_drop_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: hir::ClosureStyle::ImplFn, param_names_for_lifetime_elision_hints: false, binding_mode_hints: false, 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 cd709afe091f3..4fc6180920f5f 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 @@ -1072,6 +1072,7 @@ impl flags::AnalysisStats { param_names_for_lifetime_elision_hints: true, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, + hide_closure_parameter_hints: false, closure_style: hir::ClosureStyle::ImplFn, max_length: Some(25), closing_brace_hints_min_lines: Some(20), 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 7656c07c94851..e915e55722bbb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -208,6 +208,8 @@ config_data! { /// Whether to hide inlay type hints for `let` statements that initialize to a closure. /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. inlayHints_typeHints_hideClosureInitialization: bool = false, + /// Whether to hide inlay parameter type hints for closures. + inlayHints_typeHints_hideClosureParameter:bool = false, /// Whether to hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = false, @@ -1666,6 +1668,9 @@ impl Config { hide_closure_initialization_hints: self .inlayHints_typeHints_hideClosureInitialization() .to_owned(), + hide_closure_parameter_hints: self + .inlayHints_typeHints_hideClosureParameter() + .to_owned(), closure_style: match self.inlayHints_closureStyle() { ClosureStyle::ImplFn => hir::ClosureStyle::ImplFn, ClosureStyle::RustAnalyzer => hir::ClosureStyle::RANotation, diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 49eb7248898e5..55678926609c4 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -782,6 +782,11 @@ This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjus Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. +**rust-analyzer.inlayHints.typeHints.hideClosureParameter** (default: false) + + Whether to hide inlay parameter type hints for closures. + + **rust-analyzer.inlayHints.typeHints.hideNamedConstructor** (default: false) Whether to hide inlay type hints for constructors. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 57f4254b68996..0a60376770527 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -2253,6 +2253,16 @@ } } }, + { + "title": "inlayHints", + "properties": { + "rust-analyzer.inlayHints.typeHints.hideClosureParameter": { + "markdownDescription": "Whether to hide inlay parameter type hints for closures.", + "default": false, + "type": "boolean" + } + } + }, { "title": "inlayHints", "properties": { From 57b241532a605c9b531b755df4823e581ee715ca Mon Sep 17 00:00:00 2001 From: jnyfah <jnyfaah@gmail.com> Date: Thu, 6 Feb 2025 16:18:15 +0100 Subject: [PATCH 66/75] collapsing if statement --- .../rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index fd91cbb0979d7..4d62561df7a9f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -36,10 +36,8 @@ pub(super) fn hints( if it.ty().is_some() { return None; } - if config.hide_closure_parameter_hints { - if it.syntax().ancestors().any(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { - return None; - } + if config.hide_closure_parameter_hints && it.syntax().ancestors().any(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { + return None; } Some(it.colon_token()) }, From e8955a826629117349433a53991d27cdc6d36fc0 Mon Sep 17 00:00:00 2001 From: David Richey <davidrichey@fb.com> Date: Thu, 6 Feb 2025 12:22:58 -0600 Subject: [PATCH 67/75] fix: Don't emit empty scip occurrence for builtins --- .../crates/rust-analyzer/src/cli/scip.rs | 65 +++++++++---------- 1 file changed, 31 insertions(+), 34 deletions(-) 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 ee7b6989751e3..fe75872105aec 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 @@ -139,45 +139,42 @@ impl flags::Scip { let mut occurrences = Vec::new(); let mut symbols = Vec::new(); - tokens.into_iter().for_each(|(text_range, id)| { + for (text_range, id) in tokens.into_iter() { let token = si.tokens.get(id).unwrap(); - let (symbol, enclosing_symbol, is_inherent_impl) = - if let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = - symbol_generator.token_symbols(id, token) - { - (symbol, enclosing_symbol, is_inherent_impl) - } else { - ("".to_owned(), None, false) - }; + let Some(TokenSymbols { symbol, enclosing_symbol, is_inherent_impl }) = + symbol_generator.token_symbols(id, token) + else { + // token did not have a moniker, so there is no reasonable occurrence to emit + // see ide::moniker::def_to_moniker + continue; + }; - if !symbol.is_empty() { - let is_defined_in_this_document = match token.definition { - Some(def) => def.file_id == file_id, - _ => false, - }; - if is_defined_in_this_document { - if token_ids_emitted.insert(id) { - // token_ids_emitted does deduplication. This checks that this results - // in unique emitted symbols, as otherwise references are ambiguous. - let should_emit = record_error_if_symbol_already_used( + let is_defined_in_this_document = match token.definition { + Some(def) => def.file_id == file_id, + _ => false, + }; + if is_defined_in_this_document { + if token_ids_emitted.insert(id) { + // token_ids_emitted does deduplication. This checks that this results + // in unique emitted symbols, as otherwise references are ambiguous. + let should_emit = record_error_if_symbol_already_used( + symbol.clone(), + is_inherent_impl, + relative_path.as_str(), + &line_index, + text_range, + ); + if should_emit { + symbols.push(compute_symbol_info( symbol.clone(), - is_inherent_impl, - relative_path.as_str(), - &line_index, - text_range, - ); - if should_emit { - symbols.push(compute_symbol_info( - symbol.clone(), - enclosing_symbol, - token, - )); - } + enclosing_symbol, + token, + )); } - } else { - token_ids_referenced.insert(id); } + } else { + token_ids_referenced.insert(id); } // If the range of the def and the range of the token are the same, this must be the definition. @@ -202,7 +199,7 @@ impl flags::Scip { special_fields: Default::default(), enclosing_range: Vec::new(), }); - }); + } if occurrences.is_empty() { continue; From bacc9dfa4b2f85a002e491a0592bf61b01d4caf4 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla <modulo641@gmail.com> Date: Fri, 7 Feb 2025 09:50:03 +0900 Subject: [PATCH 68/75] fix: Resolve projection types before checking casts --- .../crates/hir-ty/src/infer/cast.rs | 1 + .../src/handlers/invalid_cast.rs | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 21d0be6ed5f76..eb193686e967f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -374,6 +374,7 @@ enum PointerKind { fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> { let ty = table.resolve_ty_shallow(ty); + let ty = table.normalize_associated_types_in(ty); if table.is_sized(&ty) { return Ok(Some(PointerKind::Thin)); diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index c7cdcf4982027..5730508436d2c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -1129,4 +1129,39 @@ fn main() { "#, ); } + + #[test] + fn regression_18682() { + check_diagnostics( + r#" +//- minicore: coerce_unsized +struct Flexible { + body: [u8], +} + +trait Field { + type Type: ?Sized; +} + +impl Field for Flexible { + type Type = [u8]; +} + +trait KnownLayout { + type MaybeUninit: ?Sized; +} + + +impl<T> KnownLayout for [T] { + type MaybeUninit = [T]; +} + +struct ZerocopyKnownLayoutMaybeUninit(<<Flexible as Field>::Type as KnownLayout>::MaybeUninit); + +fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit { + ptr as *mut _ +} +"#, + ); + } } From d3b80e1d5009b269b1d04fbb23058954adf7d5c6 Mon Sep 17 00:00:00 2001 From: jnyfah <jnyfaah@gmail.com> Date: Fri, 7 Feb 2025 10:50:45 +0100 Subject: [PATCH 69/75] minor changes --- src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 4d62561df7a9f..c2986a9aa6626 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -36,7 +36,7 @@ pub(super) fn hints( if it.ty().is_some() { return None; } - if config.hide_closure_parameter_hints && it.syntax().ancestors().any(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { + if config.hide_closure_parameter_hints && it.syntax().ancestors().nth(2).is_none_or(|n| matches!(ast::Expr::cast(n), Some(ast::Expr::ClosureExpr(_)))) { return None; } Some(it.colon_token()) From 2c22ae3a9af7c656ca341c76c96bc27e4f506988 Mon Sep 17 00:00:00 2001 From: Benjamin Brienen <benjamin.brienen@outlook.com> Date: Fri, 7 Feb 2025 20:18:21 +0100 Subject: [PATCH 70/75] Update PRIVACY.md --- src/tools/rust-analyzer/PRIVACY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/PRIVACY.md b/src/tools/rust-analyzer/PRIVACY.md index 89e252be731d0..ef9c2437ab7d3 100644 --- a/src/tools/rust-analyzer/PRIVACY.md +++ b/src/tools/rust-analyzer/PRIVACY.md @@ -1 +1 @@ -See the [Privacy](https://rust-analyzer.github.io/manual.html#privacy) section of the user manual. +See the [Privacy](https://rust-analyzer.github.io/book/privacy.html) section of the user manual. From e7d5d306df59623bcb8ca3c3892c710f3b634634 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes <me@wilfred.me.uk> Date: Fri, 7 Feb 2025 12:01:51 -0800 Subject: [PATCH 71/75] Update README.md links for mdbook manual --- src/tools/rust-analyzer/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/README.md b/src/tools/rust-analyzer/README.md index fb885c5be7615..56883723c93e0 100644 --- a/src/tools/rust-analyzer/README.md +++ b/src/tools/rust-analyzer/README.md @@ -9,7 +9,7 @@ It is a part of a larger rls-2.0 effort to create excellent IDE support for Rust ## Quick Start -https://rust-analyzer.github.io/manual.html#installation +https://rust-analyzer.github.io/book/installation.html ## Documentation @@ -18,12 +18,13 @@ if you are just curious about how things work under the hood, check the [./docs/ folder. If you want to **use** rust-analyzer's language server with your editor of -choice, check [the manual](https://rust-analyzer.github.io/manual.html) folder. +choice, check [the manual](https://rust-analyzer.github.io/book/). It also contains some tips & tricks to help you be more productive when using rust-analyzer. ## Security and Privacy -See the corresponding sections of [the manual](https://rust-analyzer.github.io/manual.html#security). +See the [security](https://rust-analyzer.github.io/book/security.html) and +[privacy](https://rust-analyzer.github.io/book/privacy.html) sections of the manual. ## Communication From b6fd709f440dabd1811a905b1a613c3eddb3465a Mon Sep 17 00:00:00 2001 From: Wilfred Hughes <me@wilfred.me.uk> Date: Fri, 7 Feb 2025 12:25:25 -0800 Subject: [PATCH 72/75] Organise chapters in mdbook sidebar --- src/tools/rust-analyzer/docs/book/src/SUMMARY.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md index 7e6cd5972d408..b3ed1e6df0a7a 100644 --- a/src/tools/rust-analyzer/docs/book/src/SUMMARY.md +++ b/src/tools/rust-analyzer/docs/book/src/SUMMARY.md @@ -4,10 +4,10 @@ - [Installation](installation.md) - [Troubleshooting](troubleshooting.md) - [Configuration](configuration.md) -- [Non-Cargo Based Projects](non_cargo_based_projects.md) + - [Non-Cargo Based Projects](non_cargo_based_projects.md) - [Security](security.md) - [Privacy](privacy.md) - [Features](features.md) -- [Assists (Code Actions)](assists.md) -- [Diagnostics](diagnostics.md) + - [Assists (Code Actions)](assists.md) + - [Diagnostics](diagnostics.md) - [Editor Features](editor_features.md) From 82162a7ac8aabcd7ada7ae78d9c917d87622f80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=B1=AA?= <504595380@qq.com> Date: Sun, 9 Feb 2025 17:49:26 +0800 Subject: [PATCH 73/75] doc: error url --- src/tools/rust-analyzer/docs/book/src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/docs/book/src/README.md b/src/tools/rust-analyzer/docs/book/src/README.md index 545e4a3e62df8..71f34e0346646 100644 --- a/src/tools/rust-analyzer/docs/book/src/README.md +++ b/src/tools/rust-analyzer/docs/book/src/README.md @@ -10,7 +10,7 @@ implement semantic features like completion or goto definition by talking to an external language server process. To improve this document, send a pull request: -[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/docs/book/README.md) +[https://github.com/rust-lang/rust-analyzer](https://github.com/rust-lang/rust-analyzer/blob/master/docs/book/README.md) The manual is written in markdown and includes some extra files which are generated from the source code. Run From 879dc387cde7ac4b85aa5a185158c9c3c488c5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Mon, 10 Feb 2025 07:49:25 +0200 Subject: [PATCH 74/75] Preparing for merge from rust-lang/rust --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 2d9a927c638bc..f414893b6cdf2 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -9a1d156f38c51441ee51e5a068f1d0caf4bb0f27 +d9a4a47b8b3dc0bdff83360cea2013200d60d49c From 24d7a1490a8193350b006b7a5f71edd97a63afd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= <lnicola@dend.ro> Date: Mon, 10 Feb 2025 07:52:04 +0200 Subject: [PATCH 75/75] Bump rustc crates --- src/tools/rust-analyzer/Cargo.lock | 25 +++++++++++++------------ src/tools/rust-analyzer/Cargo.toml | 10 +++++----- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index bf408d1a52b8c..98268d8f84449 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1514,9 +1514,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5246e9e1f450333a990877eabbc36fe0567e7cedd56d5365db319e14079cf2a" +checksum = "2fa4333df7b71217edb44a36702cafd2bcfc9850677bdf78b32c9f2c98e5df40" dependencies = [ "bitflags 2.7.0", "ra-ap-rustc_index", @@ -1525,9 +1525,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fd8e4f5b34c434ec111efb0e0614954db048b9307d3b2e4cc3c915da9d2160" +checksum = "d200275ff3d952cc11108f4dc6a692473659758623d63f2bdcea6104a7f1cec8" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1535,9 +1535,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d34973fe081392bd1edb022e865e9952fcaa093f9cdae183edce64472e5e889" +checksum = "06eb63df8c1ce2dcb07647305bed811c9c5ebd157def01a81c1b9479b8592b3b" dependencies = [ "proc-macro2", "quote", @@ -1546,19 +1546,20 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fa42c582e21b35e8f61a5afe3c63a9c722d995826762eb19b18beeccf5157f" +checksum = "b7a4d402b2f85650e8c1f78e2e2defc241b03948d6e30d9f5254c9b82755cc4d" dependencies = [ + "memchr", "unicode-properties", "unicode-xid", ] [[package]] name = "ra-ap-rustc_parse_format" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740383328d7033393e5385f4a6073b880d5811b0fc0fd2559e481f905940f2f8" +checksum = "a23a382dbe392beb26360c1a8ce9193155ef74eeac59bcda0fa0a233e047323a" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1566,9 +1567,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.91.0" +version = "0.94.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39f544728f32cebffb1a8b92ba3c1f3dcb4144081438d192137ed197d479a9d" +checksum = "d746955d67f315ab79767f1d0bbc17fee4f0970d4a00b9ad76bf09cc7d3cd17e" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.0.0", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index c42ae171d8668..44c628e8294d1 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -87,11 +87,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.91", default-features = false } -ra-ap-rustc_parse_format = { version = "0.91", default-features = false } -ra-ap-rustc_index = { version = "0.91", default-features = false } -ra-ap-rustc_abi = { version = "0.91", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.91", default-features = false } +ra-ap-rustc_lexer = { version = "0.94", default-features = false } +ra-ap-rustc_parse_format = { version = "0.94", default-features = false } +ra-ap-rustc_index = { version = "0.94", default-features = false } +ra-ap-rustc_abi = { version = "0.94", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.94", default-features = false } # local crates that aren't published to crates.io. These should not have versions.