From 032fca9572ee8d1dca368a224c09379a851afb9c Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Thu, 28 Mar 2024 20:39:21 -0700 Subject: [PATCH 01/83] Make `cargo run` always available for binaries Previously, items for `cargo test` and `cargo check` would appear as in the `Select Runnable` quick pick that appears when running `rust-analyzer: Run`, but `run` would only appear as a runnable if a `main`` function was selected in the editor. This change adds `cargo run` as an always available runnable command for binary packages. This makes it easier to develop cli / tui applications, as now users can run application from anywhere in their codebase. --- crates/rust-analyzer/src/handlers/request.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 4d4103a7ad..c5796ac071 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -836,10 +836,13 @@ pub(crate) fn handle_runnables( let config = snap.config.runnables(); match cargo_spec { Some(spec) => { - let all_targets = !snap.analysis.is_crate_no_std(spec.crate_id)?; - for cmd in ["check", "test"] { + for cmd in ["check", "run", "test"] { + if cmd == "run" && spec.target_kind != TargetKind::Bin { + continue; + } let mut cargo_args = vec![cmd.to_owned(), "--package".to_owned(), spec.package.clone()]; + let all_targets = cmd != "run" && !snap.analysis.is_crate_no_std(spec.crate_id)?; if all_targets { cargo_args.push("--all-targets".to_owned()); } From 21da6c6164f17ac39483a73e3d10ecbd8de5b53e Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 6 Apr 2024 13:57:56 +0800 Subject: [PATCH 02/83] Add config hover_show_adtFieldsOrVariants to handle hovering limitation for ADTs --- crates/hir/src/display.rs | 98 ++++++++++++++++-------------- crates/ide/src/hover.rs | 2 +- crates/ide/src/hover/render.rs | 4 +- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 6 +- 5 files changed, 60 insertions(+), 52 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 84f03d111f..b0468ea080 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -188,28 +188,12 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - let fields = self.fields(f.db); - let count = fields.len().min(limit); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - if count == 0 { - if fields.is_empty() { - f.write_str("{}")?; - } else { - f.write_str("{ /* … */ }")?; - } - } else { - f.write_str(" {\n")?; - for field in &fields[..count] { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - - if fields.len() > count { - f.write_str(" /* … */\n")?; - } - f.write_str("}")?; - } + display_fields_or_variants( + &self.fields(f.db), + has_where_clause, + limit, + f, + )?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -226,18 +210,15 @@ impl HirDisplay for Enum { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::EnumId(self.id)); write_generic_params(def_id, f)?; - let has_where_clause = write_where_clause(def_id, f)?; - let variants = self.variants(f.db); - if !variants.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for variant in variants { - f.write_str(" ")?; - variant.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; + let has_where_clause = write_where_clause(def_id, f)?; + if let Some(limit) = f.entity_limit { + display_fields_or_variants( + &self.variants(f.db), + has_where_clause, + limit, + f, + )?; } Ok(()) @@ -251,24 +232,51 @@ impl HirDisplay for Union { write!(f, "{}", self.name(f.db).display(f.db.upcast()))?; let def_id = GenericDefId::AdtId(AdtId::UnionId(self.id)); write_generic_params(def_id, f)?; + let has_where_clause = write_where_clause(def_id, f)?; - - let fields = self.fields(f.db); - if !fields.is_empty() { - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; - f.write_str("{\n")?; - for field in self.fields(f.db) { - f.write_str(" ")?; - field.hir_fmt(f)?; - f.write_str(",\n")?; - } - f.write_str("}")?; + if let Some(limit) = f.entity_limit { + display_fields_or_variants( + &self.fields(f.db), + has_where_clause, + limit, + f, + )?; } - Ok(()) } } +fn display_fields_or_variants( + fields_or_variants: &[T], + has_where_clause: bool, + limit: usize, + f: &mut HirFormatter<'_>, +)-> Result<(), HirDisplayError> { + let count = fields_or_variants.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if fields_or_variants.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str("{\n")?; + for field in &fields_or_variants[..count] { + f.write_str(" ")?; + field.hir_fmt(f)?; + f.write_str(",\n")?; + } + + if fields_or_variants.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; + } + + Ok(()) +} + impl HirDisplay for Field { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { write_visibility(self.parent.module(f.db).id, self.visibility(f.db), f)?; diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 822751c0e4..2a2be6e4e0 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -33,7 +33,7 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, - pub max_struct_field_count: Option, + pub max_adt_fields_or_variants_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 7579d4d6d8..33a8a4f695 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -410,8 +410,8 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } - Definition::Adt(Adt::Struct(struct_)) => { - struct_.display_limited(db, config.max_struct_field_count).to_string() + Definition::Adt(adt) => { + adt.display_limited(db, config.max_adt_fields_or_variants_count).to_string() } _ => def.label(db), }; diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 3fef16df25..012778f058 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,7 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_adt_fields_or_variants_count: Some(10), }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 14a4185654..3b22c2ef7f 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -312,8 +312,8 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - /// How many fields of a struct to display when hovering a struct. - hover_show_structFields: Option = None, + /// How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show all if empty. + hover_show_adtFieldsOrVariants: Option = Some(10), /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = None, @@ -1112,7 +1112,7 @@ impl Config { }, keywords: self.hover_documentation_keywords_enable().to_owned(), max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), - max_struct_field_count: self.hover_show_structFields().to_owned(), + max_adt_fields_or_variants_count: self.hover_show_adtFieldsOrVariants().to_owned(), } } From 01c3559bf3d189aa61238d022ac0e89547a719f4 Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 6 Apr 2024 14:07:03 +0800 Subject: [PATCH 03/83] Update tests and docs for hover_show_adtFieldsOrVariants --- crates/hir/src/display.rs | 23 +-- crates/ide/src/hover/tests.rs | 259 +++++++++++++++++++++++++---- crates/rust-analyzer/src/config.rs | 2 +- docs/user/generated_config.adoc | 4 +- editors/code/package.json | 6 +- 5 files changed, 235 insertions(+), 59 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index b0468ea080..ec57708a08 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -188,12 +188,7 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants( - &self.fields(f.db), - has_where_clause, - limit, - f, - )?; + display_fields_or_variants(&self.fields(f.db), has_where_clause, limit, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -213,12 +208,7 @@ impl HirDisplay for Enum { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants( - &self.variants(f.db), - has_where_clause, - limit, - f, - )?; + display_fields_or_variants(&self.variants(f.db), has_where_clause, limit, f)?; } Ok(()) @@ -235,12 +225,7 @@ impl HirDisplay for Union { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants( - &self.fields(f.db), - has_where_clause, - limit, - f, - )?; + display_fields_or_variants(&self.fields(f.db), has_where_clause, limit, f)?; } Ok(()) } @@ -251,7 +236,7 @@ fn display_fields_or_variants( has_where_clause: bool, limit: usize, f: &mut HirFormatter<'_>, -)-> Result<(), HirDisplayError> { +) -> Result<(), HirDisplayError> { let count = fields_or_variants.len().min(limit); f.write_char(if !has_where_clause { ' ' } else { '\n' })?; if count == 0 { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 67f10f0374..6e2ebd7967 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,7 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_struct_field_count: None, + max_adt_fields_or_variants_count: Some(10), }; fn check_hover_no_result(ra_fixture: &str) { @@ -51,13 +51,17 @@ fn check(ra_fixture: &str, expect: Expect) { } #[track_caller] -fn check_hover_struct_limit(count: usize, ra_fixture: &str, expect: Expect) { +fn check_hover_adt_fields_or_variants_limit( + count: Option, + ra_fixture: &str, + expect: Expect, +) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( &HoverConfig { links_in_hover: true, - max_struct_field_count: Some(count), + max_adt_fields_or_variants_count: count, ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, @@ -876,7 +880,9 @@ struct Foo$0 { field: u32 } ```rust // size = 4, align = 4 - struct Foo + struct Foo { + field: u32, + } ``` "#]], ); @@ -896,6 +902,9 @@ struct Foo$0 where u32: Copy { field: u32 } struct Foo where u32: Copy, + { + field: u32, + } ``` "#]], ); @@ -903,8 +912,8 @@ struct Foo$0 where u32: Copy { field: u32 } #[test] fn hover_record_struct_limit() { - check_hover_struct_limit( - 3, + check_hover_adt_fields_or_variants_limit( + Some(3), r#" struct Foo$0 { a: u32, b: i32, c: i32 } "#, @@ -917,7 +926,7 @@ fn hover_record_struct_limit() { ```rust // size = 12 (0xC), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -925,8 +934,8 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_limit( - 3, + check_hover_adt_fields_or_variants_limit( + Some(3), r#" struct Foo$0 { a: u32 } "#, @@ -939,14 +948,14 @@ fn hover_record_struct_limit() { ```rust // size = 4, align = 4 - struct Foo { + struct Foo { a: u32, } ``` "#]], ); - check_hover_struct_limit( - 3, + check_hover_adt_fields_or_variants_limit( + Some(3), r#" struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } "#, @@ -959,7 +968,7 @@ fn hover_record_struct_limit() { ```rust // size = 16 (0x10), align = 4 - struct Foo { + struct Foo { a: u32, b: i32, c: i32, @@ -968,6 +977,190 @@ fn hover_record_struct_limit() { ``` "#]], ); + check_hover_adt_fields_or_variants_limit( + None, + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + Some(0), + r#" + struct Foo$0 { a: u32, b: i32, c: i32 } + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4 + struct Foo { /* … */ } + ``` + "#]], + ) +} + +#[test] +fn hover_enum_limit() { + check_hover_adt_fields_or_variants_limit( + Some(10), + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + B, + } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + Some(1), + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { + A, + /* … */ + } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + Some(0), + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo { /* … */ } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + None, + r#"enum Foo$0 { A, B }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 1, align = 1, niches = 254 + enum Foo + ``` + "#]], + ); +} + +#[test] +fn hover_union_limit() { + check_hover_adt_fields_or_variants_limit( + Some(10), + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + b: i32, + } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + Some(1), + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { + a: u32, + /* … */ + } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + Some(0), + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo { /* … */ } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + None, + r#"union Foo$0 { a: u32, b: i32 }"#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 4, align = 4 + union Foo + ``` + "#]], + ); } #[test] @@ -1462,18 +1655,16 @@ impl Thing { } "#, expect![[r#" - *Self* + *Self* - ```rust - test - ``` + ```rust + test + ``` - ```rust - enum Thing { - A, - } - ``` - "#]], + ```rust + enum Thing + ``` + "#]], ); check( r#" @@ -1483,18 +1674,16 @@ impl Thing { } "#, expect![[r#" - *Self* + *Self* - ```rust - test - ``` + ```rust + test + ``` - ```rust - enum Thing { - A, - } - ``` - "#]], + ```rust + enum Thing + ``` + "#]], ); check( r#" @@ -7936,7 +8125,9 @@ struct Pedro$0<'a> { ```rust // size = 16 (0x10), align = 8, niches = 1 - struct Pedro<'a> + struct Pedro<'a> { + hola: &str, + } ``` "#]], ) diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3b22c2ef7f..0211fcfdf5 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -312,7 +312,7 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - /// How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show all if empty. + /// How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. hover_show_adtFieldsOrVariants: Option = Some(10), /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = None, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index af4483a2cc..2df318f36d 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -533,10 +533,10 @@ How to render the offset information in a memory layout hover. -- How to render the size information in a memory layout hover. -- -[[rust-analyzer.hover.show.structFields]]rust-analyzer.hover.show.structFields (default: `null`):: +[[rust-analyzer.hover.show.adtFieldsOrVariants]]rust-analyzer.hover.show.adtFieldsOrVariants (default: `10`):: + -- -How many fields of a struct to display when hovering a struct. +How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. -- [[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index c387e72a0c..504185fc22 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1152,9 +1152,9 @@ } ] }, - "rust-analyzer.hover.show.structFields": { - "markdownDescription": "How many fields of a struct to display when hovering a struct.", - "default": null, + "rust-analyzer.hover.show.adtFieldsOrVariants": { + "markdownDescription": "How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty.", + "default": 10, "type": [ "null", "integer" From c06d670f8f9333a1381792a466e9e07d32d500b0 Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 6 Apr 2024 14:36:34 +0800 Subject: [PATCH 04/83] fix: the fields or variants of ADT was not restricted by limitations when hovering on Self type --- crates/ide/src/hover/render.rs | 9 +++++++++ crates/ide/src/hover/tests.rs | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 33a8a4f695..e0a9907a41 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -413,6 +413,15 @@ pub(super) fn definition( Definition::Adt(adt) => { adt.display_limited(db, config.max_adt_fields_or_variants_count).to_string() } + Definition::SelfType(impl_def) => { + let self_ty = &impl_def.self_ty(db); + match self_ty.as_adt() { + Some(adt) => { + adt.display_limited(db, config.max_adt_fields_or_variants_count).to_string() + } + None => self_ty.display(db).to_string(), + } + } _ => def.label(db), }; let docs = def.docs(db, famous_defs); diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 6e2ebd7967..30e64f450a 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1623,6 +1623,28 @@ impl Thing { test ``` + ```rust + struct Thing { + x: u32, + } + ``` + "#]], + ); + check_hover_adt_fields_or_variants_limit( + None, + r#" +struct Thing { x: u32 } +impl Thing { + fn new() -> Self { Self$0 { x: 0 } } +} +"#, + expect![[r#" + *Self* + + ```rust + test + ``` + ```rust struct Thing ``` @@ -1643,7 +1665,9 @@ impl Thing { ``` ```rust - struct Thing + struct Thing { + x: u32, + } ``` "#]], ); @@ -1662,7 +1686,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); @@ -1681,7 +1707,9 @@ impl Thing { ``` ```rust - enum Thing + enum Thing { + A, + } ``` "#]], ); From 6bb85985d7449ede22c047f4692a72c1e51fe287 Mon Sep 17 00:00:00 2001 From: roife Date: Sun, 7 Apr 2024 13:24:33 +0800 Subject: [PATCH 05/83] fix: adjust the limitation for ADTs' fields to 5 --- crates/ide/src/hover/tests.rs | 6 +++--- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 2 +- docs/user/generated_config.adoc | 2 +- editors/code/package.json | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 30e64f450a..326c248b7a 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,7 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_adt_fields_or_variants_count: Some(10), + max_adt_fields_or_variants_count: Some(5), }; fn check_hover_no_result(ra_fixture: &str) { @@ -1018,7 +1018,7 @@ fn hover_record_struct_limit() { #[test] fn hover_enum_limit() { check_hover_adt_fields_or_variants_limit( - Some(10), + Some(5), r#"enum Foo$0 { A, B }"#, expect![[r#" *Foo* @@ -1092,7 +1092,7 @@ fn hover_enum_limit() { #[test] fn hover_union_limit() { check_hover_adt_fields_or_variants_limit( - Some(10), + Some(5), r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" *Foo* diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 012778f058..1378ab2ab0 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,7 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_adt_fields_or_variants_count: Some(10), + max_adt_fields_or_variants_count: Some(5), }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 0211fcfdf5..10bc19b988 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -313,7 +313,7 @@ config_data! { hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), /// How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. - hover_show_adtFieldsOrVariants: Option = Some(10), + hover_show_adtFieldsOrVariants: Option = Some(5), /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = None, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 2df318f36d..d275b51abc 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -533,7 +533,7 @@ How to render the offset information in a memory layout hover. -- How to render the size information in a memory layout hover. -- -[[rust-analyzer.hover.show.adtFieldsOrVariants]]rust-analyzer.hover.show.adtFieldsOrVariants (default: `10`):: +[[rust-analyzer.hover.show.adtFieldsOrVariants]]rust-analyzer.hover.show.adtFieldsOrVariants (default: `5`):: + -- How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. diff --git a/editors/code/package.json b/editors/code/package.json index 504185fc22..88a382f1fb 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1154,7 +1154,7 @@ }, "rust-analyzer.hover.show.adtFieldsOrVariants": { "markdownDescription": "How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty.", - "default": 10, + "default": 5, "type": [ "null", "integer" From 805f569adc6691e4751baeb5301d24810eadc397 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 24 Mar 2024 13:53:55 +0100 Subject: [PATCH 06/83] Handle panicking like rustc CTFE does Instead of using `core::fmt::format` to format panic messages, which may in turn panic too and cause recursive panics and other messy things, redirect `panic_fmt` to `const_panic_fmt` like CTFE, which in turn goes to `panic_display` and does the things normally. See the tests for the full call stack. --- crates/hir-ty/src/mir/eval/tests.rs | 37 +++++++++++++++++++++++++++++ crates/test-utils/src/minicore.rs | 34 +++++++++++++++++++------- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 381522c9ab..031ab51ed7 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr db.trait_environment(func_id.into()), ) .map_err(|e| MirEvalError::MirLowerError(func_id, e))?; + let (result, output) = interpret_mir(db, body, false, None); result?; Ok((output.stdout().into_owned(), output.stderr().into_owned())) @@ -87,6 +88,42 @@ fn main() { ); } +#[test] +fn panic_fmt() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection) + // -> core::panicking::const_panic_fmt + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_pass( + r#" +//- minicore: fmt, panic +fn main() { + panic!("hello, world!"); +} + "#, + ); + panic!("a"); +} + +#[test] +fn panic_display() { + // panic! + // -> panic_2021 (builtin macro redirection) + // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) + // -> Err(ConstEvalError::Panic) + check_pass( + r#" +//- minicore: fmt, panic + +fn main() { + panic!("{}", "hello, world!"); +} + "#, + ); +} + #[test] fn drop_basic() { check_pass( diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f125792d12..15bee61381 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -1356,18 +1356,36 @@ pub mod iter { // region:panic mod panic { pub macro panic_2021 { - () => ( - $crate::panicking::panic("explicit panic") - ), - ($($t:tt)+) => ( - $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)) - ), + () => ({ + const fn panic_cold_explicit() -> ! { + $crate::panicking::panic_explicit() + } + panic_cold_explicit(); + }), + // Special-case the single-argument case for const_panic. + ("{}", $arg:expr $(,)?) => ({ + #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval + const fn panic_cold_display(arg: &T) -> ! { + loop {} + } + panic_cold_display(&$arg); + }), + ($($t:tt)+) => ({ + // Semicolon to prevent temporaries inside the formatting machinery from + // being considered alive in the caller after the panic_fmt call. + $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)); + }), } } mod panicking { - #[lang = "panic_fmt"] - pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! { + #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval + pub const fn panic_display(x: &T) -> ! { + panic_fmt(format_args!("{}", *x)); + } + + #[lang = "panic_fmt"] // needed for const-evaluated panics + pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { loop {} } From 563bb6bd6cc9742847ccabe49e3a5c0790ce3307 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 25 Mar 2024 16:58:15 +0000 Subject: [PATCH 07/83] Fix missing function body in minicore --- crates/test-utils/src/minicore.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 15bee61381..8ae3a56227 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -977,7 +977,9 @@ pub mod fmt { } impl UnsafeArg { - pub unsafe fn new() -> Self; + pub unsafe fn new() -> Self { + UnsafeArg { _private: () } + } } } @@ -1488,7 +1490,6 @@ mod macros { } // endregion:unimplemented - // region:derive pub(crate) mod builtin { #[rustc_builtin_macro] From 5df690e13fc5831c03a6abf599d7233adfb213c2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 26 Mar 2024 07:34:05 +0000 Subject: [PATCH 08/83] Fixup some issues with minicore --- Cargo.toml | 2 +- crates/hir-def/src/body/lower.rs | 80 ++++++++++++++++--------------- crates/syntax/rust.ungram | 1 + crates/test-utils/src/minicore.rs | 38 +++++++++++---- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f7e3ae51df..6895dcc3c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = ["rust-analyzer team"] [profile.dev] # Disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 0 +debug = 1 [profile.dev.package] # These speed up local tests. diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 340e95dbc2..f58228c45f 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -1869,42 +1869,45 @@ impl ExprCollector<'_> { ) -> ExprId { match count { Some(FormatCount::Literal(n)) => { - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { - Some(count_is) => { - let count_is = self.alloc_expr_desugared(Expr::Path(count_is)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - *n as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_is, - args: Box::new([args]), - is_assignee_expr: false, - }) - } - None => self.missing_expr(), - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + *n as u128, + Some(BuiltinUint::Usize), + ))); + let count_is = + match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) { + Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)), + None => self.missing_expr(), + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_is, + args: Box::new([args]), + is_assignee_expr: false, + }) } Some(FormatCount::Argument(arg)) => { if let Ok(arg_index) = arg.index { let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) { - Some(count_param) => { - let count_param = self.alloc_expr_desugared(Expr::Path(count_param)); - let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( - i as u128, - Some(BuiltinUint::Usize), - ))); - self.alloc_expr_desugared(Expr::Call { - callee: count_param, - args: Box::new([args]), - is_assignee_expr: false, - }) - } + let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint( + i as u128, + Some(BuiltinUint::Usize), + ))); + let count_param = match LangItem::FormatCount.ty_rel_path( + self.db, + self.krate, + name![Param], + ) { + Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: count_param, + args: Box::new([args]), + is_assignee_expr: false, + }) } else { + // FIXME: This drops arg causing it to potentially not be resolved/type checked + // when typing? self.missing_expr() } } @@ -1925,7 +1928,8 @@ impl ExprCollector<'_> { fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId { use ArgumentType::*; use FormatTrait::*; - match LangItem::FormatArgument.ty_rel_path( + + let new_fn = match LangItem::FormatArgument.ty_rel_path( self.db, self.krate, match ty { @@ -1941,16 +1945,14 @@ impl ExprCollector<'_> { Usize => name![from_usize], }, ) { - Some(new_fn) => { - let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn)); - self.alloc_expr_desugared(Expr::Call { - callee: new_fn, - args: Box::new([arg]), - is_assignee_expr: false, - }) - } + Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)), None => self.missing_expr(), - } + }; + self.alloc_expr_desugared(Expr::Call { + callee: new_fn, + args: Box::new([arg]), + is_assignee_expr: false, + }) } // endregion: format } diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index e1765b25fd..8a775dd874 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -391,6 +391,7 @@ FormatArgsExpr = FormatArgsArg = (Name '=')? Expr +# MacroCallExpr MacroExpr = MacroCall diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 8ae3a56227..3018f2c133 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -913,6 +913,7 @@ pub mod fmt { } mod rt { + use super::*; extern "C" { type Opaque; @@ -930,8 +931,8 @@ pub mod fmt { unsafe { Argument { formatter: transmute(f), value: transmute(x) } } } - pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> { - Self::new(x, Display::fmt) + pub fn new_display<'b, T: crate::fmt::Display>(x: &'b T) -> Argument<'_> { + Self::new(x, crate::fmt::Display::fmt) } } @@ -968,7 +969,9 @@ pub mod fmt { flags: u32, precision: Count, width: Count, - ) -> Self; + ) -> Self { + Placeholder { position, fill, align, flags, precision, width } + } } #[lang = "format_unsafe_arg"] @@ -1007,6 +1010,14 @@ pub mod fmt { ) -> Arguments<'a> { Arguments { pieces, fmt: Some(fmt), args } } + + pub const fn as_str(&self) -> Option<&'static str> { + match (self.pieces, self.args) { + ([], []) => Some(""), + ([s], []) => Some(s), + _ => None, + } + } } // region:derive @@ -1156,8 +1167,8 @@ pub mod pin { pointer: P, } impl

Pin

{ - pub fn new(_pointer: P) -> Pin

{ - loop {} + pub fn new(pointer: P) -> Pin

{ + Pin { pointer } } } // region:deref @@ -1382,12 +1393,23 @@ mod panic { mod panicking { #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval - pub const fn panic_display(x: &T) -> ! { - panic_fmt(format_args!("{}", *x)); + pub const fn panic_display(x: &T) -> ! { + panic_fmt(crate::format_args!("{}", *x)); + } + + // This function is used instead of panic_fmt in const eval. + #[lang = "const_panic_fmt"] + pub const fn const_panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { + if let Some(msg) = fmt.as_str() { + // The panic_display function is hooked by const eval. + panic_display(&msg); + } else { + loop {} + } } #[lang = "panic_fmt"] // needed for const-evaluated panics - pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { + pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! { loop {} } From a9140e197c45fd67310a917a8fd1d40ebdcc0ff2 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 18 Apr 2024 14:00:59 +0200 Subject: [PATCH 09/83] Fix #[rustc_const_panic_str] functions not actually being hooked --- crates/hir-def/src/body/tests.rs | 26 ++++++++++++++------------ crates/hir-ty/src/mir/eval/shim.rs | 15 ++++++++++----- crates/test-utils/src/minicore.rs | 3 ++- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index a27ffe2167..4c8a54f7c8 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/crates/hir-def/src/body/tests.rs @@ -318,18 +318,20 @@ fn f() { expect![[r#" fn f() { - $crate::panicking::panic_fmt( - builtin#lang(Arguments::new_v1_formatted)( - &[ - "cc", - ], - &[], - &[], - unsafe { - builtin#lang(UnsafeArg::new)() - }, - ), - ); + { + $crate::panicking::panic_fmt( + builtin#lang(Arguments::new_v1_formatted)( + &[ + "cc", + ], + &[], + &[], + unsafe { + builtin#lang(UnsafeArg::new)() + }, + ), + ); + }; }"#]] .assert_eq(&body.pretty_print(&db, def)) } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index fee3dd3ada..33fae866ec 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -49,6 +49,7 @@ impl Evaluator<'_> { if self.not_special_fn_cache.borrow().contains(&def) { return Ok(false); } + let function_data = self.db.function_data(def); let is_intrinsic = match &function_data.abi { Some(abi) => *abi == Interned::new_str("rust-intrinsic"), @@ -311,16 +312,20 @@ impl Evaluator<'_> { fn detect_lang_function(&self, def: FunctionId) -> Option { use LangItem::*; - let candidate = self.db.lang_attr(def.into())?; + let attrs = self.db.attrs(def.into()); + + if attrs.by_key("rustc_const_panic_str").exists() { + // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. + return Some(LangItem::BeginPanic); + } + + let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?; // We want to execute these functions with special logic // `PanicFmt` is not detected here as it's redirected later. if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } - if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() { - // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE. - return Some(LangItem::BeginPanic); - } + None } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 3018f2c133..c50a5fa669 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -1378,8 +1378,9 @@ mod panic { // Special-case the single-argument case for const_panic. ("{}", $arg:expr $(,)?) => ({ #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval + #[rustc_do_not_const_check] // hooked by const-eval const fn panic_cold_display(arg: &T) -> ! { - loop {} + $crate::panicking::panic_display(arg) } panic_cold_display(&$arg); }), From 6de838c255426de8519e3b58e2ebd7e18ec60e4f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Apr 2024 12:42:32 +0200 Subject: [PATCH 10/83] Implement BeginPanic for mir eval --- crates/hir-ty/src/chalk_ext.rs | 5 ++++ crates/hir-ty/src/mir/eval.rs | 2 +- crates/hir-ty/src/mir/eval/shim.rs | 43 +++++++++++++++++++++++++---- crates/hir-ty/src/mir/eval/tests.rs | 1 - 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 0bf01b0bc6..d99ef6679e 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -27,6 +27,7 @@ pub trait TyExt { fn is_scalar(&self) -> bool; fn is_floating_point(&self) -> bool; fn is_never(&self) -> bool; + fn is_str(&self) -> bool; fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; @@ -87,6 +88,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::Never) } + fn is_str(&self) -> bool { + matches!(self.kind(Interner), TyKind::Str) + } + fn is_unknown(&self) -> bool { matches!(self.kind(Interner), TyKind::Error) } diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 045ffb418c..17ad85b6c5 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -1138,7 +1138,7 @@ impl Evaluator<'_> { let mut ty = self.operand_ty(lhs, locals)?; while let TyKind::Ref(_, _, z) = ty.kind(Interner) { ty = z.clone(); - let size = if ty.kind(Interner) == &TyKind::Str { + let size = if ty.is_str() { if *op != BinOp::Eq { never!("Only eq is builtin for `str`"); } diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 33fae866ec..e2ff67a092 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -132,9 +132,7 @@ impl Evaluator<'_> { return Ok(true); } if let Some(it) = self.detect_lang_function(def) { - let arg_bytes = - args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::>>()?; - let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?; + let result = self.exec_lang_item(it, generic_args, args, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -333,18 +331,52 @@ impl Evaluator<'_> { &mut self, it: LangItem, generic_args: &Substitution, - args: &[Vec], + args: &[IntervalAndTy], locals: &Locals, span: MirSpan, ) -> Result> { use LangItem::*; let mut args = args.iter(); match it { - BeginPanic => Err(MirEvalError::Panic("".to_owned())), + BeginPanic => { + let mut arg = args + .next() + .ok_or(MirEvalError::InternalError( + "argument of BeginPanic is not provided".into(), + ))? + .clone(); + while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) { + if ty.is_str() { + let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size()); + let len = from_bytes!(usize, metadata); + + return { + Err(MirEvalError::Panic( + std::str::from_utf8( + self.read_memory(Address::from_bytes(pointee)?, len)?, + ) + .unwrap() + .to_owned(), + )) + }; + } + let size = self.size_of_sized(&ty, locals, "begin panic arg")?; + let pointee = arg.interval.get(self)?; + arg = IntervalAndTy { + interval: Interval::new(Address::from_bytes(pointee)?, size), + ty: ty.clone(), + }; + } + Err(MirEvalError::Panic(format!( + "unknown-panic-payload: {:?}", + arg.ty.kind(Interner) + ))) + } SliceLen => { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of <[T]>::len() is not provided".into(), ))?; + let arg = arg.get(self)?; let ptr_size = arg.len() / 2; Ok(arg[ptr_size..].into()) } @@ -358,6 +390,7 @@ impl Evaluator<'_> { let arg = args.next().ok_or(MirEvalError::InternalError( "argument of drop_in_place is not provided".into(), ))?; + let arg = arg.interval.get(self)?.to_owned(); self.run_drop_glue_deep( ty.clone(), locals, diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 031ab51ed7..e88fbd19a4 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -104,7 +104,6 @@ fn main() { } "#, ); - panic!("a"); } #[test] From e0e28ec856d70abe55013068d2faf44807eb41a9 Mon Sep 17 00:00:00 2001 From: roife Date: Fri, 19 Apr 2024 21:31:54 +0800 Subject: [PATCH 11/83] fix: add a separate setting for enum variants --- crates/hir/src/display.rs | 66 +++++++++++++--- crates/ide/src/hover.rs | 3 +- crates/ide/src/hover/render.rs | 9 ++- crates/ide/src/hover/tests.rs | 123 +++++++++++++++++++++-------- crates/ide/src/static_index.rs | 3 +- crates/rust-analyzer/src/config.rs | 9 ++- docs/user/generated_config.adoc | 9 ++- editors/code/package.json | 13 ++- 8 files changed, 183 insertions(+), 52 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index ec57708a08..42fafd4ee8 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -188,7 +188,7 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants(&self.fields(f.db), has_where_clause, limit, f)?; + display_fields(&self.fields(f.db), has_where_clause, limit, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -208,7 +208,7 @@ impl HirDisplay for Enum { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants(&self.variants(f.db), has_where_clause, limit, f)?; + display_variants(&self.variants(f.db), has_where_clause, limit, f)?; } Ok(()) @@ -225,35 +225,83 @@ impl HirDisplay for Union { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields_or_variants(&self.fields(f.db), has_where_clause, limit, f)?; + display_fields(&self.fields(f.db), has_where_clause, limit, f)?; } Ok(()) } } -fn display_fields_or_variants( - fields_or_variants: &[T], +fn display_fields( + fields: &[Field], has_where_clause: bool, limit: usize, f: &mut HirFormatter<'_>, ) -> Result<(), HirDisplayError> { - let count = fields_or_variants.len().min(limit); + let count = fields.len().min(limit); f.write_char(if !has_where_clause { ' ' } else { '\n' })?; if count == 0 { - if fields_or_variants.is_empty() { + if fields.is_empty() { f.write_str("{}")?; } else { f.write_str("{ /* … */ }")?; } } else { f.write_str("{\n")?; - for field in &fields_or_variants[..count] { + for field in &fields[..count] { f.write_str(" ")?; field.hir_fmt(f)?; f.write_str(",\n")?; } - if fields_or_variants.len() > count { + if fields.len() > count { + f.write_str(" /* … */\n")?; + } + f.write_str("}")?; + } + + Ok(()) +} + +fn display_variants( + variants: &[Variant], + has_where_clause: bool, + limit: usize, + f: &mut HirFormatter<'_>, +) -> Result<(), HirDisplayError> { + let count = variants.len().min(limit); + f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + if count == 0 { + if variants.is_empty() { + f.write_str("{}")?; + } else { + f.write_str("{ /* … */ }")?; + } + } else { + f.write_str("{\n")?; + for variant in &variants[..count] { + f.write_str(" ")?; + write!(f, "{}", variant.name(f.db).display(f.db.upcast()))?; + match variant.kind(f.db) { + StructKind::Tuple => { + if variant.fields(f.db).is_empty() { + f.write_str("()")?; + } else { + f.write_str("( /* … */ )")?; + } + } + StructKind::Record => { + if variant.fields(f.db).is_empty() { + f.write_str(" {}")?; + } else { + f.write_str(" { /* … */ }")?; + } + } + StructKind::Unit => {} + } + f.write_str(",\n")?; + } + + if variants.len() > count { f.write_str(" /* … */\n")?; } f.write_str("}")?; diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 2a2be6e4e0..6958ae1623 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -33,7 +33,8 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, - pub max_adt_fields_or_variants_count: Option, + pub max_struct_or_union_fields_count: Option, + pub max_enum_variants_count: Option, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index e0a9907a41..1044a3f4c1 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -410,14 +410,17 @@ pub(super) fn definition( Definition::Trait(trait_) => { trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } - Definition::Adt(adt) => { - adt.display_limited(db, config.max_adt_fields_or_variants_count).to_string() + Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { + adt.display_limited(db, config.max_struct_or_union_fields_count).to_string() + } + Definition::Adt(adt @ Adt::Enum(_)) => { + adt.display_limited(db, config.max_enum_variants_count).to_string() } Definition::SelfType(impl_def) => { let self_ty = &impl_def.self_ty(db); match self_ty.as_adt() { Some(adt) => { - adt.display_limited(db, config.max_adt_fields_or_variants_count).to_string() + adt.display_limited(db, config.max_struct_or_union_fields_count).to_string() } None => self_ty.display(db).to_string(), } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 326c248b7a..f17a0fa2cd 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,7 +18,8 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_adt_fields_or_variants_count: Some(5), + max_struct_or_union_fields_count: Some(5), + max_enum_variants_count: Some(5), }; fn check_hover_no_result(ra_fixture: &str) { @@ -51,8 +52,8 @@ fn check(ra_fixture: &str, expect: Expect) { } #[track_caller] -fn check_hover_adt_fields_or_variants_limit( - count: Option, +fn check_hover_struct_or_union_fields_limit( + fields_count: impl Into>, ra_fixture: &str, expect: Expect, ) { @@ -61,7 +62,33 @@ fn check_hover_adt_fields_or_variants_limit( .hover( &HoverConfig { links_in_hover: true, - max_adt_fields_or_variants_count: count, + max_struct_or_union_fields_count: fields_count.into(), + ..HOVER_BASE_CONFIG + }, + FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, + ) + .unwrap() + .unwrap(); + + let content = analysis.db.file_text(position.file_id); + let hovered_element = &content[hover.range]; + + let actual = format!("*{hovered_element}*\n{}\n", hover.info.markup); + expect.assert_eq(&actual) +} + +#[track_caller] +fn check_hover_enum_variants_limit( + variants_count: impl Into>, + ra_fixture: &str, + expect: Expect, +) { + let (analysis, position) = fixture::position(ra_fixture); + let hover = analysis + .hover( + &HoverConfig { + links_in_hover: true, + max_enum_variants_count: variants_count.into(), ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, @@ -912,8 +939,8 @@ struct Foo$0 where u32: Copy { field: u32 } #[test] fn hover_record_struct_limit() { - check_hover_adt_fields_or_variants_limit( - Some(3), + check_hover_struct_or_union_fields_limit( + 3, r#" struct Foo$0 { a: u32, b: i32, c: i32 } "#, @@ -934,8 +961,8 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(3), + check_hover_struct_or_union_fields_limit( + 3, r#" struct Foo$0 { a: u32 } "#, @@ -954,8 +981,8 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(3), + check_hover_struct_or_union_fields_limit( + 3, r#" struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } "#, @@ -977,7 +1004,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( + check_hover_struct_or_union_fields_limit( None, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -995,8 +1022,8 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(0), + check_hover_struct_or_union_fields_limit( + 0, r#" struct Foo$0 { a: u32, b: i32, c: i32 } "#, @@ -1017,8 +1044,8 @@ fn hover_record_struct_limit() { #[test] fn hover_enum_limit() { - check_hover_adt_fields_or_variants_limit( - Some(5), + check_hover_enum_variants_limit( + 5, r#"enum Foo$0 { A, B }"#, expect![[r#" *Foo* @@ -1036,8 +1063,8 @@ fn hover_enum_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(1), + check_hover_enum_variants_limit( + 1, r#"enum Foo$0 { A, B }"#, expect![[r#" *Foo* @@ -1055,8 +1082,8 @@ fn hover_enum_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(0), + check_hover_enum_variants_limit( + 0, r#"enum Foo$0 { A, B }"#, expect![[r#" *Foo* @@ -1071,7 +1098,7 @@ fn hover_enum_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( + check_hover_enum_variants_limit( None, r#"enum Foo$0 { A, B }"#, expect![[r#" @@ -1087,12 +1114,46 @@ fn hover_enum_limit() { ``` "#]], ); + check_hover_enum_variants_limit( + 7, + r#"enum Enum$0 { + Variant {}, + Variant2 { field: i32 }, + Variant3 { field: i32, field2: i32 }, + Variant4(), + Variant5(i32), + Variant6(i32, i32), + Variant7, + Variant8, + }"#, + expect![[r#" + *Enum* + + ```rust + test + ``` + + ```rust + // size = 12 (0xC), align = 4, niches = 4294967288 + enum Enum { + Variant {}, + Variant2 { /* … */ }, + Variant3 { /* … */ }, + Variant4(), + Variant5( /* … */ ), + Variant6( /* … */ ), + Variant7, + /* … */ + } + ``` + "#]], + ); } #[test] fn hover_union_limit() { - check_hover_adt_fields_or_variants_limit( - Some(5), + check_hover_struct_or_union_fields_limit( + 5, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" *Foo* @@ -1110,8 +1171,8 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(1), + check_hover_struct_or_union_fields_limit( + 1, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" *Foo* @@ -1129,8 +1190,8 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( - Some(0), + check_hover_struct_or_union_fields_limit( + 0, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" *Foo* @@ -1145,7 +1206,7 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( + check_hover_struct_or_union_fields_limit( None, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" @@ -1630,12 +1691,12 @@ impl Thing { ``` "#]], ); - check_hover_adt_fields_or_variants_limit( + check_hover_struct_or_union_fields_limit( None, r#" struct Thing { x: u32 } impl Thing { - fn new() -> Self { Self$0 { x: 0 } } + fn new() -> Self$0 { Self { x: 0 } } } "#, expect![[r#" @@ -2599,8 +2660,8 @@ fn test_hover_layout_of_enum() { ```rust // size = 16 (0x10), align = 8, niches = 254 enum Foo { - Variant1(u8, u16), - Variant2(i32, u8, i64), + Variant1( /* … */ ), + Variant2( /* … */ ), } ``` "#]], diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 1378ab2ab0..c5623062af 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,7 +167,8 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_adt_fields_or_variants_count: Some(5), + max_struct_or_union_fields_count: Some(5), + max_enum_variants_count: Some(5), }; let tokens = tokens.filter(|token| { matches!( diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 10bc19b988..910ee70ca8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -312,8 +312,10 @@ config_data! { /// How to render the size information in a memory layout hover. hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - /// How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. - hover_show_adtFieldsOrVariants: Option = Some(5), + /// How many variants of an enum to display when hovering on. Show none if empty. + hover_show_enumVariants: Option = Some(5), + /// How many fields of a struct or union to display when hovering on. Show none if empty. + hover_show_structOrUnionFields: Option = Some(5), /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = None, @@ -1112,7 +1114,8 @@ impl Config { }, keywords: self.hover_documentation_keywords_enable().to_owned(), max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), - max_adt_fields_or_variants_count: self.hover_show_adtFieldsOrVariants().to_owned(), + max_struct_or_union_fields_count: self.hover_show_structOrUnionFields().to_owned(), + max_enum_variants_count: self.hover_show_enumVariants().to_owned(), } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index d275b51abc..acc10cdb8f 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -533,10 +533,15 @@ How to render the offset information in a memory layout hover. -- How to render the size information in a memory layout hover. -- -[[rust-analyzer.hover.show.adtFieldsOrVariants]]rust-analyzer.hover.show.adtFieldsOrVariants (default: `5`):: +[[rust-analyzer.hover.show.enumVariants]]rust-analyzer.hover.show.enumVariants (default: `5`):: + -- -How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty. +How many variants of an enum to display when hovering on. Show none if empty. +-- +[[rust-analyzer.hover.show.structOrUnionFields]]rust-analyzer.hover.show.structOrUnionFields (default: `5`):: ++ +-- +How many fields of a struct or union to display when hovering on. Show none if empty. -- [[rust-analyzer.hover.show.traitAssocItems]]rust-analyzer.hover.show.traitAssocItems (default: `null`):: + diff --git a/editors/code/package.json b/editors/code/package.json index 88a382f1fb..ffbceb8ad4 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1152,8 +1152,17 @@ } ] }, - "rust-analyzer.hover.show.adtFieldsOrVariants": { - "markdownDescription": "How many fields or variants of an ADT (struct, enum or union) to display when hovering on. Show none if empty.", + "rust-analyzer.hover.show.enumVariants": { + "markdownDescription": "How many variants of an enum to display when hovering on. Show none if empty.", + "default": 5, + "type": [ + "null", + "integer" + ], + "minimum": 0 + }, + "rust-analyzer.hover.show.structOrUnionFields": { + "markdownDescription": "How many fields of a struct or union to display when hovering on. Show none if empty.", "default": 5, "type": [ "null", From 43576989a1a577e3680bf1c3327810ce1b5a5b8b Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 20 Apr 2024 01:06:04 +0800 Subject: [PATCH 12/83] Add hovering limitations support for variants --- crates/hir/src/display.rs | 37 ++++----- crates/ide/src/hover.rs | 2 +- crates/ide/src/hover/render.rs | 9 ++- crates/ide/src/hover/tests.rs | 122 +++++++++++++++++++++++++---- crates/ide/src/static_index.rs | 2 +- crates/rust-analyzer/src/config.rs | 6 +- docs/user/generated_config.adoc | 4 +- editors/code/package.json | 4 +- 8 files changed, 138 insertions(+), 48 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 42fafd4ee8..1c3eac1590 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -188,7 +188,7 @@ impl HirDisplay for Struct { StructKind::Record => { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields(&self.fields(f.db), has_where_clause, limit, f)?; + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; } } StructKind::Unit => _ = write_where_clause(def_id, f)?, @@ -225,7 +225,7 @@ impl HirDisplay for Union { let has_where_clause = write_where_clause(def_id, f)?; if let Some(limit) = f.entity_limit { - display_fields(&self.fields(f.db), has_where_clause, limit, f)?; + display_fields(&self.fields(f.db), has_where_clause, limit, false, f)?; } Ok(()) } @@ -235,10 +235,12 @@ fn display_fields( fields: &[Field], has_where_clause: bool, limit: usize, + in_line: bool, f: &mut HirFormatter<'_>, ) -> Result<(), HirDisplayError> { let count = fields.len().min(limit); - f.write_char(if !has_where_clause { ' ' } else { '\n' })?; + let (indent, separator) = if in_line { ("", ' ') } else { (" ", '\n') }; + f.write_char(if !has_where_clause { ' ' } else { separator })?; if count == 0 { if fields.is_empty() { f.write_str("{}")?; @@ -246,15 +248,19 @@ fn display_fields( f.write_str("{ /* … */ }")?; } } else { - f.write_str("{\n")?; + f.write_char('{')?; + f.write_char(separator)?; for field in &fields[..count] { - f.write_str(" ")?; + f.write_str(indent)?; field.hir_fmt(f)?; - f.write_str(",\n")?; + f.write_char(',')?; + f.write_char(separator)?; } if fields.len() > count { - f.write_str(" /* … */\n")?; + f.write_str(indent)?; + f.write_str("/* … */")?; + f.write_char(separator)?; } f.write_str("}")?; } @@ -345,21 +351,10 @@ impl HirDisplay for Variant { } f.write_char(')')?; } - VariantData::Record(fields) => { - f.write_str(" {")?; - let mut first = true; - for (_, field) in fields.iter() { - if first { - first = false; - f.write_char(' ')?; - } else { - f.write_str(", ")?; - } - // Enum variant fields must be pub. - write!(f, "{}: ", field.name.display(f.db.upcast()))?; - field.type_ref.hir_fmt(f)?; + VariantData::Record(_) => { + if let Some(limit) = f.entity_limit { + display_fields(&self.fields(f.db), false, limit, true, f)?; } - f.write_str(" }")?; } } Ok(()) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 6958ae1623..cd76ee4d7b 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -33,7 +33,7 @@ pub struct HoverConfig { pub keywords: bool, pub format: HoverDocFormat, pub max_trait_assoc_items_count: Option, - pub max_struct_or_union_fields_count: Option, + pub max_fields_count: Option, pub max_enum_variants_count: Option, } diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 1044a3f4c1..0809ea81d7 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -411,7 +411,10 @@ pub(super) fn definition( trait_.display_limited(db, config.max_trait_assoc_items_count).to_string() } Definition::Adt(adt @ (Adt::Struct(_) | Adt::Union(_))) => { - adt.display_limited(db, config.max_struct_or_union_fields_count).to_string() + adt.display_limited(db, config.max_fields_count).to_string() + } + Definition::Variant(variant) => { + variant.display_limited(db, config.max_fields_count).to_string() } Definition::Adt(adt @ Adt::Enum(_)) => { adt.display_limited(db, config.max_enum_variants_count).to_string() @@ -419,9 +422,7 @@ pub(super) fn definition( Definition::SelfType(impl_def) => { let self_ty = &impl_def.self_ty(db); match self_ty.as_adt() { - Some(adt) => { - adt.display_limited(db, config.max_struct_or_union_fields_count).to_string() - } + Some(adt) => adt.display_limited(db, config.max_fields_count).to_string(), None => self_ty.display(db).to_string(), } } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index f17a0fa2cd..ff3258b4db 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -18,7 +18,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig { format: HoverDocFormat::Markdown, keywords: true, max_trait_assoc_items_count: None, - max_struct_or_union_fields_count: Some(5), + max_fields_count: Some(5), max_enum_variants_count: Some(5), }; @@ -52,7 +52,7 @@ fn check(ra_fixture: &str, expect: Expect) { } #[track_caller] -fn check_hover_struct_or_union_fields_limit( +fn check_hover_fields_limit( fields_count: impl Into>, ra_fixture: &str, expect: Expect, @@ -62,7 +62,7 @@ fn check_hover_struct_or_union_fields_limit( .hover( &HoverConfig { links_in_hover: true, - max_struct_or_union_fields_count: fields_count.into(), + max_fields_count: fields_count.into(), ..HOVER_BASE_CONFIG }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, @@ -939,7 +939,7 @@ struct Foo$0 where u32: Copy { field: u32 } #[test] fn hover_record_struct_limit() { - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -961,7 +961,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32 } @@ -981,7 +981,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 3, r#" struct Foo$0 { a: u32, b: i32, c: i32, d: u32 } @@ -1004,7 +1004,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( None, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -1022,7 +1022,7 @@ fn hover_record_struct_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 0, r#" struct Foo$0 { a: u32, b: i32, c: i32 } @@ -1042,6 +1042,100 @@ fn hover_record_struct_limit() { ) } +#[test] +fn hover_record_variant_limit() { + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { a: u32, b: i32, c: i32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 4, align = 4 + A { a: u32, } + ``` + "#]], + ); + check_hover_fields_limit( + 3, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32, d: u32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 16 (0x10), align = 4 + A { a: u32, b: i32, c: i32, /* … */ } + ``` + "#]], + ); + check_hover_fields_limit( + None, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A + ``` + "#]], + ); + check_hover_fields_limit( + 0, + r#" + enum Foo { A$0 { a: u32, b: i32, c: i32 } } + "#, + expect![[r#" + *A* + + ```rust + test::Foo + ``` + + ```rust + // size = 12 (0xC), align = 4 + A { /* … */ } + ``` + "#]], + ); +} + #[test] fn hover_enum_limit() { check_hover_enum_variants_limit( @@ -1152,7 +1246,7 @@ fn hover_enum_limit() { #[test] fn hover_union_limit() { - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 5, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" @@ -1171,7 +1265,7 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 1, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" @@ -1190,7 +1284,7 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( 0, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" @@ -1206,7 +1300,7 @@ fn hover_union_limit() { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( None, r#"union Foo$0 { a: u32, b: i32 }"#, expect![[r#" @@ -1691,7 +1785,7 @@ impl Thing { ``` "#]], ); - check_hover_struct_or_union_fields_limit( + check_hover_fields_limit( None, r#" struct Thing { x: u32 } @@ -6832,7 +6926,7 @@ enum Enum { ```rust // size = 4, align = 4 - RecordV { field: u32 } + RecordV { field: u32, } ``` "#]], ); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index c5623062af..df625f7404 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -167,7 +167,7 @@ impl StaticIndex<'_> { keywords: true, format: crate::HoverDocFormat::Markdown, max_trait_assoc_items_count: None, - max_struct_or_union_fields_count: Some(5), + max_fields_count: Some(5), max_enum_variants_count: Some(5), }; let tokens = tokens.filter(|token| { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 910ee70ca8..52c09fb394 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -314,8 +314,8 @@ config_data! { /// How many variants of an enum to display when hovering on. Show none if empty. hover_show_enumVariants: Option = Some(5), - /// How many fields of a struct or union to display when hovering on. Show none if empty. - hover_show_structOrUnionFields: Option = Some(5), + /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. + hover_show_fields: Option = Some(5), /// How many associated items of a trait to display when hovering a trait. hover_show_traitAssocItems: Option = None, @@ -1114,7 +1114,7 @@ impl Config { }, keywords: self.hover_documentation_keywords_enable().to_owned(), max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(), - max_struct_or_union_fields_count: self.hover_show_structOrUnionFields().to_owned(), + max_fields_count: self.hover_show_fields().to_owned(), max_enum_variants_count: self.hover_show_enumVariants().to_owned(), } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index acc10cdb8f..ebacc5b701 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -538,10 +538,10 @@ How to render the size information in a memory layout hover. -- How many variants of an enum to display when hovering on. Show none if empty. -- -[[rust-analyzer.hover.show.structOrUnionFields]]rust-analyzer.hover.show.structOrUnionFields (default: `5`):: +[[rust-analyzer.hover.show.fields]]rust-analyzer.hover.show.fields (default: `5`):: + -- -How many fields of a struct or union to display when hovering on. Show none if empty. +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`):: + diff --git a/editors/code/package.json b/editors/code/package.json index ffbceb8ad4..aed0d9f898 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -1161,8 +1161,8 @@ ], "minimum": 0 }, - "rust-analyzer.hover.show.structOrUnionFields": { - "markdownDescription": "How many fields of a struct or union to display when hovering on. Show none if empty.", + "rust-analyzer.hover.show.fields": { + "markdownDescription": "How many fields of a struct, variant or union to display when hovering on. Show none if empty.", "default": 5, "type": [ "null", From aa1f1344cc961924bdce0a76913ed54f934418e4 Mon Sep 17 00:00:00 2001 From: roife Date: Sat, 20 Apr 2024 10:07:33 +0800 Subject: [PATCH 13/83] fix: remove space within `{}` when no fields in struct --- crates/hir/src/display.rs | 24 ++++++++++++++---------- crates/ide/src/hover/tests.rs | 22 +++++++++++++++++++++- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs index 1c3eac1590..c276e87786 100644 --- a/crates/hir/src/display.rs +++ b/crates/hir/src/display.rs @@ -249,19 +249,23 @@ fn display_fields( } } else { f.write_char('{')?; - f.write_char(separator)?; - for field in &fields[..count] { - f.write_str(indent)?; - field.hir_fmt(f)?; - f.write_char(',')?; + + if !fields.is_empty() { f.write_char(separator)?; + for field in &fields[..count] { + f.write_str(indent)?; + field.hir_fmt(f)?; + f.write_char(',')?; + f.write_char(separator)?; + } + + if fields.len() > count { + f.write_str(indent)?; + f.write_str("/* … */")?; + f.write_char(separator)?; + } } - if fields.len() > count { - f.write_str(indent)?; - f.write_str("/* … */")?; - f.write_char(separator)?; - } f.write_str("}")?; } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index ff3258b4db..52e57d7889 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -1039,7 +1039,27 @@ fn hover_record_struct_limit() { struct Foo { /* … */ } ``` "#]], - ) + ); + + // No extra spaces within `{}` when there are no fields + check_hover_fields_limit( + 5, + r#" + struct Foo$0 {} + "#, + expect![[r#" + *Foo* + + ```rust + test + ``` + + ```rust + // size = 0, align = 1 + struct Foo {} + ``` + "#]], + ); } #[test] From ce18438cfded247881ff19606a70fc7fa41eb7b3 Mon Sep 17 00:00:00 2001 From: Lev Iskandarov Date: Sat, 20 Apr 2024 20:50:47 +0300 Subject: [PATCH 14/83] try to generate more meaningful names --- .../src/handlers/json_is_not_rust.rs | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 241fddbb90..659af270c6 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -7,7 +7,7 @@ use ide_db::{ helpers::mod_path_to_ast, imports::insert_use::{insert_use, ImportScope}, source_change::SourceChangeBuilder, - RootDatabase, + FxHashMap, RootDatabase, }; use itertools::Itertools; use stdx::{format_to, never}; @@ -22,15 +22,21 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsConfig, Severity}; #[derive(Default)] struct State { result: String, - struct_counts: usize, has_serialize: bool, has_deserialize: bool, + names: FxHashMap, } impl State { - fn generate_new_name(&mut self) -> ast::Name { - self.struct_counts += 1; - make::name(&format!("Struct{}", self.struct_counts)) + fn generate_new_name(&mut self, name: &str) -> ast::Name { + let name = stdx::to_camel_case(name); + let count = if let Some(count) = self.names.get(&name) { + *count + } else { + self.names.insert(name.clone(), 1); + 1 + }; + make::name(&format!("{}{}", name, count)) } fn serde_derive(&self) -> String { @@ -52,15 +58,21 @@ impl State { } } - fn build_struct(&mut self, value: &serde_json::Map) -> ast::Type { - let name = self.generate_new_name(); + fn build_struct( + &mut self, + name: &str, + value: &serde_json::Map, + ) -> ast::Type { + let name = self.generate_new_name(name); let ty = make::ty(&name.to_string()); let strukt = make::struct_( None, name, None, make::record_field_list(value.iter().sorted_unstable_by_key(|x| x.0).map( - |(name, value)| make::record_field(None, make::name(name), self.type_of(value)), + |(name, value)| { + make::record_field(None, make::name(name), self.type_of(name, value)) + }, )) .into(), ); @@ -68,7 +80,7 @@ impl State { ty } - fn type_of(&mut self, value: &serde_json::Value) -> ast::Type { + fn type_of(&mut self, name: &str, value: &serde_json::Value) -> ast::Type { match value { serde_json::Value::Null => make::ty_unit(), serde_json::Value::Bool(_) => make::ty("bool"), @@ -76,12 +88,12 @@ impl State { serde_json::Value::String(_) => make::ty("String"), serde_json::Value::Array(it) => { let ty = match it.iter().next() { - Some(x) => self.type_of(x), + Some(x) => self.type_of(name, x), None => make::ty_placeholder(), }; make::ty(&format!("Vec<{ty}>")) } - serde_json::Value::Object(x) => self.build_struct(x), + serde_json::Value::Object(x) => self.build_struct(name, x), } } } @@ -113,7 +125,7 @@ pub(crate) fn json_in_items( let serialize_resolved = scope_resolve("::serde::Serialize"); state.has_deserialize = deserialize_resolved.is_some(); state.has_serialize = serialize_resolved.is_some(); - state.build_struct(&it); + state.build_struct("Root", &it); edit.insert(range.start(), state.result); acc.push( Diagnostic::new( @@ -218,7 +230,7 @@ mod tests { } #[derive(Serialize)] - struct Struct1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + struct Root1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } "#, ); @@ -237,9 +249,9 @@ mod tests { } "#, r#" - struct Struct3{ } - struct Struct2{ kind: String, value: Struct3 } - struct Struct1{ bar: Struct2, foo: String } + struct Value1{ } + struct Bar1{ kind: String, value: Value1 } + struct Root1{ bar: Bar1, foo: String } "#, ); @@ -276,9 +288,9 @@ mod tests { use serde::Deserialize; #[derive(Serialize, Deserialize)] - struct Struct2{ x: i64, y: i64 } + struct OfObject1{ x: i64, y: i64 } #[derive(Serialize, Deserialize)] - struct Struct1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } + struct Root1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } "#, ); From 029c7108e0986f64bb88dbd0e748616a0d8aaa69 Mon Sep 17 00:00:00 2001 From: Lev Iskandarov Date: Sat, 20 Apr 2024 21:04:53 +0300 Subject: [PATCH 15/83] add test with multiple names --- .../src/handlers/json_is_not_rust.rs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index 659af270c6..b3dde977b1 100644 --- a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -30,7 +30,8 @@ struct State { impl State { fn generate_new_name(&mut self, name: &str) -> ast::Name { let name = stdx::to_camel_case(name); - let count = if let Some(count) = self.names.get(&name) { + let count = if let Some(count) = self.names.get_mut(&name) { + *count += 1; *count } else { self.names.insert(name.clone(), 1); @@ -257,6 +258,41 @@ mod tests { ); } + #[test] + fn naming() { + check_fix( + r#" + {$0 + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "example@example.com" + }, + "another_user": { + "user": { + "address": { + "street": "Main St", + "house": 3 + }, + "email": "example@example.com" + } + } + } + "#, + r#" + struct Address1{ house: i64, street: String } + struct User1{ address: Address1, email: String } + struct AnotherUser1{ user: User1 } + struct Address2{ house: i64, street: String } + struct User2{ address: Address2, email: String } + struct Root1{ another_user: AnotherUser1, user: User2 } + + "#, + ); + } + #[test] fn arrays() { check_fix( From 3b9a2af21fcae3e74d0ee499b45b090a5e9f1887 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Apr 2024 13:18:30 +0200 Subject: [PATCH 16/83] Peek for panic message in test output --- Cargo.toml | 2 +- crates/hir-ty/src/mir/eval.rs | 11 +++++++++++ crates/hir-ty/src/mir/eval/shim.rs | 2 +- crates/hir-ty/src/mir/eval/tests.rs | 13 +++++++++++-- crates/ide-diagnostics/src/tests.rs | 1 + crates/ide/src/hover/tests.rs | 8 ++++---- .../test_data/highlight_default_library.html | 2 +- .../test_data/highlight_strings.html | 4 ++-- crates/test-utils/src/minicore.rs | 5 ++++- 9 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6895dcc3c8..f7e3ae51df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = ["rust-analyzer team"] [profile.dev] # Disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 1 +debug = 0 [profile.dev.package] # These speed up local tests. diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 17ad85b6c5..2de1aa30c9 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -428,6 +428,17 @@ impl MirEvalError { } Ok(()) } + + pub fn is_panic(&self) -> Option<&str> { + let mut err = self; + while let MirEvalError::InFunction(e, _) = err { + err = e; + } + match err { + MirEvalError::Panic(msg) => Some(msg), + _ => None, + } + } } impl std::fmt::Debug for MirEvalError { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index e2ff67a092..3438712049 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -360,7 +360,7 @@ impl Evaluator<'_> { )) }; } - let size = self.size_of_sized(&ty, locals, "begin panic arg")?; + let size = self.size_of_sized(ty, locals, "begin panic arg")?; let pointee = arg.interval.get(self)?; arg = IntervalAndTy { interval: Interval::new(Address::from_bytes(pointee)?, size), diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index e88fbd19a4..4abbda56cb 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -73,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr } } +fn check_panic(ra_fixture: &str, expected_panic: &str) { + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + let file_id = *file_ids.last().unwrap(); + let e = eval_main(&db, file_id).unwrap_err(); + assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic); +} + #[test] fn function_with_extern_c_abi() { check_pass( @@ -96,13 +103,14 @@ fn panic_fmt() { // -> core::panicking::const_panic_fmt // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) // -> Err(ConstEvalError::Panic) - check_pass( + check_panic( r#" //- minicore: fmt, panic fn main() { panic!("hello, world!"); } "#, + "hello, world!", ); } @@ -112,7 +120,7 @@ fn panic_display() { // -> panic_2021 (builtin macro redirection) // -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic) // -> Err(ConstEvalError::Panic) - check_pass( + check_panic( r#" //- minicore: fmt, panic @@ -120,6 +128,7 @@ fn main() { panic!("{}", "hello, world!"); } "#, + "hello, world!", ); } diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index bb5c2b7913..cd5e95cc1e 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -293,6 +293,7 @@ fn minicore_smoke_test() { // This should be ignored since we conditionally remove code which creates single item use with braces config.disabled.insert("unused_braces".to_owned()); config.disabled.insert("unused_variables".to_owned()); + config.disabled.insert("remove-unnecessary-else".to_owned()); check_diagnostics_with_config(config, &source); } diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 67f10f0374..48a2775ce7 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -7864,8 +7864,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 6290..6498, - focus_range: 6355..6361, + full_range: 7791..7999, + focus_range: 7856..7862, name: "Future", kind: Trait, container_name: "future", @@ -7878,8 +7878,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7128..7594, - focus_range: 7172..7180, + full_range: 8629..9095, + focus_range: 8673..8681, name: "Iterator", kind: Trait, container_name: "iterator", diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 366895ce7e..893e3c0675 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -49,5 +49,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fn main() { let foo = Some(92); - let nums = iter::repeat(foo.unwrap()); + let nums = iter::repeat(foo.unwrap()); } \ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 7a07d17b27..22706dea1f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -158,9 +158,9 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd println!("{ничоси}", ничоси = 92); println!("{:x?} {} ", thingy, n2); - panic!("{}", 0); + panic!("{}", 0); panic!("more {}", 1); - assert!(true, "{}", 1); + assert!(true, "{}", 1); assert!(true, "{} asdasd", 1); toho!("{}fmt", 0); let i: u64 = 3; diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index c50a5fa669..0257ed9ab4 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -28,7 +28,7 @@ //! env: option //! eq: sized //! error: fmt -//! fmt: option, result, transmute, coerce_unsized +//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive //! fn: //! from: sized //! future: pin @@ -919,6 +919,7 @@ pub mod fmt { type Opaque; } + #[derive(Copy, Clone)] #[lang = "format_argument"] pub struct Argument<'a> { value: &'a Opaque, @@ -986,6 +987,7 @@ pub mod fmt { } } + #[derive(Copy, Clone)] #[lang = "format_arguments"] pub struct Arguments<'a> { pieces: &'a [&'static str], @@ -1421,6 +1423,7 @@ mod panicking { } // endregion:panic +#[macro_use] mod macros { // region:panic #[macro_export] From c38295993b26657a24f7820dfe780f46ec161ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Sun, 7 Apr 2024 11:41:39 +0300 Subject: [PATCH 17/83] Use josh for subtree syncs --- Cargo.lock | 49 +++++ crates/rust-analyzer/tests/slow-tests/tidy.rs | 1 + docs/dev/README.md | 20 +- rust-version | 1 + xtask/Cargo.toml | 1 + xtask/src/flags.rs | 38 +++- xtask/src/main.rs | 3 +- xtask/src/release.rs | 171 ++++++++++++++++-- 8 files changed, 251 insertions(+), 33 deletions(-) create mode 100644 rust-version diff --git a/Cargo.lock b/Cargo.lock index a6e460134f..bd3894b4d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -323,6 +323,27 @@ dependencies = [ "syn", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dissimilar" version = "1.0.7" @@ -898,6 +919,16 @@ dependencies = [ "libc", ] +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.2", + "libc", +] + [[package]] name = "limit" version = "0.0.0" @@ -1186,6 +1217,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1566,6 +1603,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "rowan" version = "0.15.15" @@ -2499,6 +2547,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", + "directories", "flate2", "itertools", "proc-macro2", diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 3443939133..4a7415b016 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -144,6 +144,7 @@ MIT OR Apache-2.0 MIT OR Apache-2.0 OR Zlib MIT OR Zlib OR Apache-2.0 MIT/Apache-2.0 +MPL-2.0 Unlicense OR MIT Unlicense/MIT Zlib OR Apache-2.0 OR MIT diff --git a/docs/dev/README.md b/docs/dev/README.md index cdab6b0992..8897f02e27 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -229,12 +229,22 @@ Release steps: * publishes the VS Code extension to the marketplace * call the GitHub API for PR details * create a new changelog in `rust-analyzer.github.io` -3. While the release is in progress, fill in the changelog -4. Commit & push the changelog +3. While the release is in progress, fill in the changelog. +4. Commit & push the changelog. 5. Run `cargo xtask publish-release-notes ` -- this will convert the changelog entry in AsciiDoc to Markdown and update the body of GitHub Releases entry. -6. Tweet -7. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree. - Self-approve the PR. +6. Tweet. +7. Make a new branch and run `cargo xtask rustc-pull`, open a PR, and merge it. + This will pull any changes from `rust-lang/rust` into `rust-analyzer`. +8. Switch to `master`, pull, then run `cargo xtask rustc-push --rust-path ../rust-rust-analyzer --rust-fork matklad/rust`. + Replace `matklad/rust` with your own fork of `rust-lang/rust`. + You can use the token to authenticate when you get prompted for a password, since `josh` will push over HTTPS, not SSH. + This will push the `rust-analyzer` changes to your fork. + You can then open a PR against `rust-lang/rust`. + +Note: besides the `rust-rust-analyzer` clone, the Josh cache (stored under `~/.cache/rust-analyzer-josh`) will contain a bare clone of `rust-lang/rust`. +This currently takes about 3.5 GB. + +This [HackMD](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg) has details about how `josh` syncs work. If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. If it fails because of something that needs to be fixed, remove the release tag (if needed), fix the problem, then start over. diff --git a/rust-version b/rust-version new file mode 100644 index 0000000000..e2a22b2395 --- /dev/null +++ b/rust-version @@ -0,0 +1 @@ +688c30dc9f8434d63bddb65bd6a4d2258d19717c diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index a83d32e414..192de86947 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -8,6 +8,7 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true +directories = "5.0" flate2 = "1.0.24" write-json = "0.1.2" xshell.workspace = true diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 9066545920..dd7bfd0bda 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -14,16 +14,16 @@ xflags::xflags! { cmd install { /// Install only VS Code plugin. optional --client - /// One of 'code', 'code-exploration', 'code-insiders', 'codium', or 'code-oss'. + /// One of `code`, `code-exploration`, `code-insiders`, `codium`, or `code-oss`. optional --code-bin name: String /// Install only the language server. optional --server - /// Use mimalloc allocator for server + /// Use mimalloc allocator for server. optional --mimalloc - /// Use jemalloc allocator for server + /// Use jemalloc allocator for server. optional --jemalloc - /// build in release with debug info set to 2 + /// build in release with debug info set to 2. optional --dev-rel } @@ -32,9 +32,21 @@ xflags::xflags! { cmd release { optional --dry-run } - cmd promote { - optional --dry-run + + cmd rustc-pull { + /// rustc commit to pull. + optional --commit refspec: String } + + cmd rustc-push { + /// rust local path, e.g. `../rust-rust-analyzer`. + required --rust-path rust_path: String + /// rust fork name, e.g. `matklad/rust`. + required --rust-fork rust_fork: String + /// branch name. + optional --branch branch: String + } + cmd dist { /// Use mimalloc allocator for server optional --mimalloc @@ -77,7 +89,8 @@ pub enum XtaskCmd { Install(Install), FuzzTests(FuzzTests), Release(Release), - Promote(Promote), + RustcPull(RustcPull), + RustcPush(RustcPush), Dist(Dist), PublishReleaseNotes(PublishReleaseNotes), Metrics(Metrics), @@ -104,8 +117,15 @@ pub struct Release { } #[derive(Debug)] -pub struct Promote { - pub dry_run: bool, +pub struct RustcPull { + pub commit: Option, +} + +#[derive(Debug)] +pub struct RustcPush { + pub rust_path: String, + pub rust_fork: String, + pub branch: Option, } #[derive(Debug)] diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 9418675a34..e070576303 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -34,7 +34,8 @@ fn main() -> anyhow::Result<()> { flags::XtaskCmd::Install(cmd) => cmd.run(sh), flags::XtaskCmd::FuzzTests(_) => run_fuzzer(sh), flags::XtaskCmd::Release(cmd) => cmd.run(sh), - flags::XtaskCmd::Promote(cmd) => cmd.run(sh), + flags::XtaskCmd::RustcPull(cmd) => cmd.run(sh), + flags::XtaskCmd::RustcPush(cmd) => cmd.run(sh), flags::XtaskCmd::Dist(cmd) => cmd.run(sh), flags::XtaskCmd::PublishReleaseNotes(cmd) => cmd.run(sh), flags::XtaskCmd::Metrics(cmd) => cmd.run(sh), diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 1a5e6dfb4c..9dcf7af00b 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -1,5 +1,12 @@ mod changelog; +use std::process::{Command, Stdio}; +use std::thread; +use std::time::Duration; + +use anyhow::{bail, Context as _}; +use directories::ProjectDirs; +use stdx::JodChild; use xshell::{cmd, Shell}; use crate::{codegen, date_iso, flags, is_release_tag, project_root}; @@ -71,26 +78,154 @@ impl flags::Release { } } -impl flags::Promote { +// git sync implementation adapted from https://github.com/rust-lang/miri/blob/62039ac/miri-script/src/commands.rs +impl flags::RustcPull { pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { - let _dir = sh.push_dir("../rust-rust-analyzer"); - cmd!(sh, "git switch master").run()?; - cmd!(sh, "git fetch upstream").run()?; - cmd!(sh, "git reset --hard upstream/master").run()?; - - let date = date_iso(sh)?; - let branch = format!("rust-analyzer-{date}"); - cmd!(sh, "git switch -c {branch}").run()?; - cmd!(sh, "git subtree pull -m ':arrow_up: rust-analyzer' -P src/tools/rust-analyzer rust-analyzer release").run()?; - - if !self.dry_run { - cmd!(sh, "git push -u origin {branch}").run()?; - cmd!( - sh, - "xdg-open https://github.com/matklad/rust/pull/new/{branch}?body=r%3F%20%40ghost" - ) - .run()?; + sh.change_dir(project_root()); + let commit = self.commit.map(Result::Ok).unwrap_or_else(|| { + let rust_repo_head = + cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?; + rust_repo_head + .split_whitespace() + .next() + .map(|front| front.trim().to_owned()) + .ok_or_else(|| anyhow::format_err!("Could not obtain Rust repo HEAD from remote.")) + })?; + // Make sure the repo is clean. + if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { + bail!("working directory must be clean before running `cargo xtask pull`"); } + // Make sure josh is running. + let josh = start_josh()?; + + // Update rust-version file. As a separate commit, since making it part of + // the merge has confused the heck out of josh in the past. + // We pass `--no-verify` to avoid running any git hooks that might exist, + // in case they dirty the repository. + sh.write_file("rust-version", format!("{commit}\n"))?; + const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rust-lang/rust"; + cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") + .run() + .context("FAILED to commit rust-version file, something went wrong")?; + + // Fetch given rustc commit. + cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") + .run() + .map_err(|e| { + // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); + e + }) + .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; + + // Merge the fetched commit. + const MERGE_COMMIT_MESSAGE: &str = "Merge from rust-lang/rust"; + cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") + .run() + .context("FAILED to merge new commits, something went wrong")?; + + drop(josh); Ok(()) } } + +impl flags::RustcPush { + pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { + let branch = self.branch.as_deref().unwrap_or("sync-from-ra"); + let rust_path = self.rust_path; + let rust_fork = self.rust_fork; + + sh.change_dir(project_root()); + let base = sh.read_file("rust-version")?.trim().to_owned(); + // Make sure the repo is clean. + if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { + bail!("working directory must be clean before running `cargo xtask push`"); + } + // Make sure josh is running. + let josh = start_josh()?; + + // Find a repo we can do our preparation in. + sh.change_dir(rust_path); + + // Prepare the branch. Pushing works much better if we use as base exactly + // the commit that we pulled from last time, so we use the `rust-version` + // file to find out which commit that would be. + println!("Preparing {rust_fork} (base: {base})..."); + if cmd!(sh, "git fetch https://github.com/{rust_fork} {branch}") + .ignore_stderr() + .read() + .is_ok() + { + bail!( + "The branch `{branch}` seems to already exist in `https://github.com/{rust_fork}`. Please delete it and try again." + ); + } + cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; + cmd!(sh, "git push https://github.com/{rust_fork} {base}:refs/heads/{branch}") + .ignore_stdout() + .ignore_stderr() // silence the "create GitHub PR" message + .run()?; + println!(); + + // Do the actual push. + sh.change_dir(project_root()); + println!("Pushing rust-analyzer changes..."); + cmd!( + sh, + "git push http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git HEAD:{branch}" + ) + .run()?; + println!(); + + // Do a round-trip check to make sure the push worked as expected. + cmd!( + sh, + "git fetch http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git {branch}" + ) + .ignore_stderr() + .read()?; + let head = cmd!(sh, "git rev-parse HEAD").read()?; + let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; + if head != fetch_head { + bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!"); + } + println!("Confirmed that the push round-trips back to rust-analyzer properly. Please create a rustc PR:"); + // https://github.com/github-linguist/linguist/compare/master...octocat:linguist:master + let fork_path = rust_fork.replace('/', ":"); + println!( + " https://github.com/rust-lang/rust/compare/{fork_path}:{branch}?quick_pull=1&title=Subtree+update+of+rust-analyzer&body=r?+@ghost" + ); + + drop(josh); + Ok(()) + } +} + +/// Used for rustc syncs. +const JOSH_FILTER: &str = + ":rev(55d9a533b309119c8acd13061581b43ae8840823:prefix=src/tools/rust-analyzer):/src/tools/rust-analyzer"; +const JOSH_PORT: &str = "42042"; + +fn start_josh() -> anyhow::Result { + // Determine cache directory. + let local_dir = { + let user_dirs = ProjectDirs::from("org", "rust-lang", "rust-analyzer-josh").unwrap(); + user_dirs.cache_dir().to_owned() + }; + + // Start josh, silencing its output. + let mut cmd = Command::new("josh-proxy"); + cmd.arg("--local").arg(local_dir); + cmd.arg("--remote").arg("https://github.com"); + cmd.arg("--port").arg(JOSH_PORT); + cmd.arg("--no-background"); + cmd.stdout(Stdio::null()); + cmd.stderr(Stdio::null()); + let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; + // Give it some time so hopefully the port is open. (100ms was not enough.) + thread::sleep(Duration::from_millis(200)); + + Ok(JodChild(josh)) +} From a2ed6837bc45f13ecd72553dd0aaa16dc7d5dd87 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 21 Apr 2024 14:40:10 +0200 Subject: [PATCH 18/83] Allow rust files to be used linkedProjects --- crates/base-db/src/lib.rs | 2 + crates/hir/src/semantics/source_to_def.rs | 5 +- crates/ide-db/src/prime_caches.rs | 2 +- crates/project-model/src/build_scripts.rs | 53 ++--- crates/project-model/src/cargo_workspace.rs | 9 +- crates/project-model/src/lib.rs | 21 +- crates/project-model/src/manifest_path.rs | 12 +- crates/project-model/src/sysroot.rs | 6 +- crates/project-model/src/tests.rs | 24 ++- crates/project-model/src/workspace.rs | 181 +++++++++--------- .../rust-analyzer/src/cli/progress_report.rs | 2 +- crates/rust-analyzer/src/cli/rustc_tests.rs | 6 +- crates/rust-analyzer/src/config.rs | 12 +- crates/rust-analyzer/src/global_state.rs | 6 +- crates/rust-analyzer/src/handlers/request.rs | 9 +- crates/rust-analyzer/src/reload.rs | 11 +- crates/rust-analyzer/tests/crate_graph.rs | 10 +- crates/rust-analyzer/tests/slow-tests/main.rs | 1 + .../rust-analyzer/tests/slow-tests/support.rs | 6 +- docs/user/generated_config.adoc | 3 +- editors/code/package.json | 2 +- 21 files changed, 203 insertions(+), 180 deletions(-) diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 2b64a07a5a..82aff6e3b8 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs @@ -51,6 +51,7 @@ pub trait FileLoader { /// Text of the file. fn file_text(&self, file_id: FileId) -> Arc; fn resolve_path(&self, path: AnchoredPath<'_>) -> Option; + /// Crates whose root's source root is the same as the source root of `file_id` fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>; } @@ -104,6 +105,7 @@ pub trait SourceDatabaseExt: SourceDatabase { #[salsa::input] fn source_root(&self, id: SourceRootId) -> Arc; + /// Crates whose root fool is in `id`. fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>; } diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 434e4b5a0c..007709a836 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -121,7 +121,7 @@ impl SourceToDefCtx<'_, '_> { let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered(); let mut mods = SmallVec::new(); for &crate_id in self.db.relevant_crates(file).iter() { - // FIXME: inner items + // Note: `mod` declarations in block modules cannot be supported here let crate_def_map = self.db.crate_def_map(crate_id); mods.extend( crate_def_map @@ -129,6 +129,9 @@ impl SourceToDefCtx<'_, '_> { .map(|local_id| crate_def_map.module_id(local_id)), ) } + if mods.is_empty() { + // FIXME: detached file + } mods } diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs index 024e8f6ae3..0db87c6bc4 100644 --- a/crates/ide-db/src/prime_caches.rs +++ b/crates/ide-db/src/prime_caches.rs @@ -1,6 +1,6 @@ //! rust-analyzer is lazy and doesn't compute anything unless asked. This //! sometimes is counter productive when, for example, the first goto definition -//! request takes longer to compute. This modules implemented prepopulation of +//! request takes longer to compute. This module implements prepopulation of //! various caches, it's not really advanced at the moment. mod topologic_sort; diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index fbd423c9ea..3fdd59967b 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -24,7 +24,7 @@ use toolchain::Tool; use crate::{ cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation, - InvocationStrategy, Package, Sysroot, TargetKind, + InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -63,7 +63,7 @@ impl WorkspaceBuildScripts { fn build_command( config: &CargoConfig, allowed_features: &FxHashSet, - workspace_root: &AbsPathBuf, + manifest_path: &ManifestPath, sysroot: Option<&Sysroot>, ) -> io::Result { let mut cmd = match config.run_build_script_command.as_deref() { @@ -79,7 +79,7 @@ impl WorkspaceBuildScripts { cmd.args(&config.extra_args); cmd.arg("--manifest-path"); - cmd.arg(workspace_root.join("Cargo.toml")); + cmd.arg(manifest_path.as_ref()); if let Some(target_dir) = &config.target_dir { cmd.arg("--target-dir").arg(target_dir); @@ -116,6 +116,10 @@ impl WorkspaceBuildScripts { } } + if manifest_path.extension().map_or(false, |ext| ext == "rs") { + cmd.arg("-Zscript"); + } + cmd } }; @@ -152,37 +156,12 @@ impl WorkspaceBuildScripts { .as_ref(); let allowed_features = workspace.workspace_features(); - - match Self::run_per_ws( - Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?, - workspace, - current_dir, - progress, - ) { - Ok(WorkspaceBuildScripts { error: Some(error), .. }) - if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) => - { - // building build scripts failed, attempt to build with --keep-going so - // that we potentially get more build data - let mut cmd = Self::build_command( - config, - &allowed_features, - &workspace.workspace_root().to_path_buf(), - sysroot, - )?; - - cmd.args(["--keep-going"]); - let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?; - res.error = Some(error); - Ok(res) - } - res => res, + let mut cmd = + Self::build_command(config, &allowed_features, workspace.manifest_path(), sysroot)?; + if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) { + cmd.args(["--keep-going"]); } + Self::run_per_ws(cmd, workspace, current_dir, progress) } /// Runs the build scripts by invoking the configured command *once*. @@ -204,7 +183,13 @@ impl WorkspaceBuildScripts { )) } }; - let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?; + let cmd = Self::build_command( + config, + &Default::default(), + // This is not gonna be used anyways, so just construct a dummy here + &ManifestPath::try_from(workspace_root.clone()).unwrap(), + None, + )?; // NB: Cargo.toml could have been modified between `cargo metadata` and // `cargo check`. We shouldn't assume that package ids we see here are // exactly those from `config`. diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index ff7cf144aa..88ba5a0da9 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -32,6 +32,7 @@ pub struct CargoWorkspace { targets: Arena, workspace_root: AbsPathBuf, target_directory: AbsPathBuf, + manifest_path: ManifestPath, } impl ops::Index for CargoWorkspace { @@ -334,7 +335,7 @@ impl CargoWorkspace { .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command())) } - pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace { + pub fn new(mut meta: cargo_metadata::Metadata, manifest_path: ManifestPath) -> CargoWorkspace { let mut pkg_by_id = FxHashMap::default(); let mut packages = Arena::default(); let mut targets = Arena::default(); @@ -448,7 +449,7 @@ impl CargoWorkspace { let target_directory = AbsPathBuf::assert(meta.target_directory); - CargoWorkspace { packages, targets, workspace_root, target_directory } + CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path } } pub fn packages(&self) -> impl ExactSizeIterator + '_ { @@ -466,6 +467,10 @@ impl CargoWorkspace { &self.workspace_root } + pub fn manifest_path(&self) -> &ManifestPath { + &self.manifest_path + } + pub fn target_directory(&self) -> &AbsPath { &self.target_directory } diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index 7f3e35ca5d..5428f061b7 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -54,11 +54,13 @@ pub use crate::{ sysroot::Sysroot, workspace::{FileLoader, PackageRoot, ProjectWorkspace}, }; +pub use cargo_metadata::Metadata; #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), CargoToml(ManifestPath), + CargoScript(ManifestPath), } impl ProjectManifest { @@ -71,7 +73,10 @@ impl ProjectManifest { if path.file_name().unwrap_or_default() == "Cargo.toml" { return Ok(ProjectManifest::CargoToml(path)); } - bail!("project root must point to Cargo.toml or rust-project.json: {path}"); + if path.extension().unwrap_or_default() == "rs" { + return Ok(ProjectManifest::CargoScript(path)); + } + bail!("project root must point to a Cargo.toml, rust-project.json or