diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index caa8f28d8e2f6..7432a82080d01 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -569,12 +569,6 @@ dependencies = [ "hashbrown 0.15.4", ] -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -1020,6 +1014,15 @@ dependencies = [ "triomphe", ] +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset", +] + [[package]] name = "itertools" version = "0.12.1" @@ -1427,6 +1430,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "papaya" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f" +dependencies = [ + "equivalent", + "seize", +] + [[package]] name = "parking_lot" version = "0.12.4" @@ -1955,16 +1968,18 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "salsa" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8fff508e3d6ef42a32607f7538e17171a877a12015e32036f46e99d00c95781" +checksum = "2e235afdb8e510f38a07138fbe5a0b64691894358a9c0cbd813b1aade110efc9" dependencies = [ "boxcar", "crossbeam-queue", - "dashmap", + "crossbeam-utils", "hashbrown 0.15.4", "hashlink", "indexmap", + "intrusive-collections", + "papaya", "parking_lot", "portable-atomic", "rayon", @@ -1978,17 +1993,16 @@ dependencies = [ [[package]] name = "salsa-macro-rules" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea72b3c06f2ce6350fe3a0eeb7aaaf842d1d8352b706973c19c4f02e298a87c" +checksum = "2edb86a7e9c91f6d30c9ce054312721dbe773a162db27bbfae834d16177b30ce" [[package]] name = "salsa-macros" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92025bc160b27814a207cb78d680973af17f863c7f4fc56cf3a535e22f378" +checksum = "d0778d6e209051bc4e75acfe83bcd7848601ec3dbe9c3dbb982829020e9128af" dependencies = [ - "heck", "proc-macro2", "quote", "syn", @@ -2025,6 +2039,16 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "seize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b8d813387d566f627f3ea1b914c068aac94c40ae27ec43f5f33bde65abefe7" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "semver" version = "1.0.26" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 0a8e6feb46edd..d268ce5b0bbff 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -49,6 +49,8 @@ debug = 2 # ungrammar = { path = "../ungrammar" } # salsa = { path = "../salsa" } +# salsa-macros = { path = "../salsa/components/salsa-macros" } +# salsa-macro-rules = { path = "../salsa/components/salsa-macro-rules" } [workspace.dependencies] # local crates @@ -136,8 +138,8 @@ rayon = "1.10.0" rowan = "=0.15.15" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.22.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] } -salsa-macros = "0.22.0" +salsa = { version = "0.23.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] } +salsa-macros = "0.23.0" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" } diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 2a87b152487b1..8c9393bcc93ad 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -6,6 +6,7 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. +use std::error::Error; use std::hash::BuildHasherDefault; use std::{fmt, mem, ops}; @@ -22,7 +23,49 @@ use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet}; use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb}; -pub type ProcMacroPaths = FxHashMap>; +pub type ProcMacroPaths = + FxHashMap>; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ProcMacroLoadingError { + Disabled, + FailedToBuild, + MissingDylibPath, + NotYetBuilt, + NoProcMacros, + ProcMacroSrvError(Box), +} +impl ProcMacroLoadingError { + pub fn is_hard_error(&self) -> bool { + match self { + ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false, + ProcMacroLoadingError::FailedToBuild + | ProcMacroLoadingError::MissingDylibPath + | ProcMacroLoadingError::NoProcMacros + | ProcMacroLoadingError::ProcMacroSrvError(_) => true, + } + } +} + +impl Error for ProcMacroLoadingError {} +impl fmt::Display for ProcMacroLoadingError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"), + ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"), + ProcMacroLoadingError::MissingDylibPath => { + write!(f, "proc-macro crate build data is missing a dylib path") + } + ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"), + ProcMacroLoadingError::NoProcMacros => { + write!(f, "proc macro library has no proc macros") + } + ProcMacroLoadingError::ProcMacroSrvError(msg) => { + write!(f, "proc macro server error: {msg}") + } + } + } +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct SourceRootId(pub u32); diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 478fae67c807c..ad17f1730bef7 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -14,8 +14,9 @@ pub use crate::{ input::{ BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap, - DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroPaths, ReleaseChannel, - SourceRoot, SourceRootId, TargetLayoutLoadResult, UniqueCrateData, + DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError, + ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult, + UniqueCrateData, }, }; use dashmap::{DashMap, mapref::entry::Entry}; @@ -33,7 +34,7 @@ pub type FxIndexSet = indexmap::IndexSet; #[macro_export] macro_rules! impl_intern_key { ($id:ident, $loc:ident) => { - #[salsa_macros::interned(no_lifetime)] + #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { pub loc: $loc, @@ -43,7 +44,7 @@ macro_rules! impl_intern_key { impl ::std::fmt::Debug for $id { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { f.debug_tuple(stringify!($id)) - .field(&format_args!("{:04x}", self.0.as_u32())) + .field(&format_args!("{:04x}", self.0.index())) .finish() } } @@ -167,7 +168,7 @@ impl Files { } } -#[salsa_macros::interned(no_lifetime, debug, constructor=from_span)] +#[salsa_macros::interned(no_lifetime, debug, constructor=from_span, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct EditionedFileId { pub editioned_file_id: span::EditionedFileId, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index 85bd193223fef..51612f341a1ee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -93,7 +93,7 @@ pub type TypeSource = InFile; pub type LifetimePtr = AstPtr; pub type LifetimeSource = InFile; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, PartialEq, Eq)] pub struct ExpressionStore { pub exprs: Arena, pub pats: Arena, @@ -114,7 +114,7 @@ pub struct ExpressionStore { ident_hygiene: FxHashMap, } -#[derive(Debug, Eq, PartialEq, Default)] +#[derive(Debug, Eq, Default)] pub struct ExpressionStoreSourceMap { // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected). @@ -127,19 +127,20 @@ pub struct ExpressionStoreSourceMap { label_map: FxHashMap, label_map_back: ArenaMap, - binding_definitions: FxHashMap>, - - /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). - /// Instead, we use id of expression (`92`) to identify the field. - field_map_back: FxHashMap, - pat_field_map_back: FxHashMap, - types_map_back: ArenaMap, types_map: FxHashMap, lifetime_map_back: ArenaMap, lifetime_map: FxHashMap, + binding_definitions: + ArenaMap() / size_of::()]>>, + + /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). + /// Instead, we use id of expression (`92`) to identify the field. + field_map_back: FxHashMap, + pat_field_map_back: FxHashMap, + template_map: Option>, pub expansions: FxHashMap, MacroCallId>, @@ -149,6 +150,43 @@ pub struct ExpressionStoreSourceMap { pub diagnostics: Vec, } +impl PartialEq for ExpressionStoreSourceMap { + fn eq(&self, other: &Self) -> bool { + // we only need to compare one of the two mappings + // as the other is a reverse mapping and thus will compare + // the same as normal mapping + let Self { + expr_map: _, + expr_map_back, + pat_map: _, + pat_map_back, + label_map: _, + label_map_back, + types_map_back, + types_map: _, + lifetime_map_back, + lifetime_map: _, + // If this changed, our pattern data must have changed + binding_definitions: _, + // If this changed, our expression data must have changed + field_map_back: _, + // If this changed, our pattern data must have changed + pat_field_map_back: _, + template_map, + expansions, + diagnostics, + } = self; + *expr_map_back == other.expr_map_back + && *pat_map_back == other.pat_map_back + && *label_map_back == other.label_map_back + && *types_map_back == other.types_map_back + && *lifetime_map_back == other.lifetime_map_back + && *template_map == other.template_map + && *expansions == other.expansions + && *diagnostics == other.diagnostics + } +} + /// The body of an item (function, const etc.). #[derive(Debug, Eq, PartialEq, Default)] pub struct ExpressionStoreBuilder { @@ -698,7 +736,7 @@ impl ExpressionStoreSourceMap { } pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] { - self.binding_definitions.get(&binding).map_or(&[], Deref::deref) + self.binding_definitions.get(binding).map_or(&[], Deref::deref) } pub fn node_label(&self, node: InFile<&ast::Label>) -> Option { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs index db83e73a0b95f..19c7ce0ce042b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs @@ -29,8 +29,8 @@ pub enum Path { // This type is being used a lot, make sure it doesn't grow unintentionally. #[cfg(target_arch = "x86_64")] const _: () = { - assert!(size_of::() == 16); - assert!(size_of::>() == 16); + assert!(size_of::() == 24); + assert!(size_of::>() == 24); }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index a46711c67e874..2dd0b9bdb864d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -535,7 +535,7 @@ fn foo() { let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap(); let pat_src = source_map - .pat_syntax(*source_map.binding_definitions[&resolved.binding()].first().unwrap()) + .pat_syntax(*source_map.binding_definitions[resolved.binding()].first().unwrap()) .unwrap(); let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax()); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index eb3b92d31f1c1..eacc3f3cedfed 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -149,7 +149,7 @@ pub enum TypeRef { } #[cfg(target_arch = "x86_64")] -const _: () = assert!(size_of::() == 16); +const _: () = assert!(size_of::() == 24); pub type TypeRefId = Idx; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs index 777953d3f212b..0013c2a25679a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs @@ -746,3 +746,83 @@ struct Struct9<#[pointee] T, U>(T) where T: ?Sized; 623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]], ); } + +#[test] +fn union_derive() { + check_errors( + r#" +//- minicore: clone, copy, default, fmt, hash, ord, eq, derive + +#[derive(Copy)] +union Foo1 { _v: () } +#[derive(Clone)] +union Foo2 { _v: () } +#[derive(Default)] +union Foo3 { _v: () } +#[derive(Debug)] +union Foo4 { _v: () } +#[derive(Hash)] +union Foo5 { _v: () } +#[derive(Ord)] +union Foo6 { _v: () } +#[derive(PartialOrd)] +union Foo7 { _v: () } +#[derive(Eq)] +union Foo8 { _v: () } +#[derive(PartialEq)] +union Foo9 { _v: () } + "#, + expect![[r#" + 78..118: this trait cannot be derived for unions + 119..157: this trait cannot be derived for unions + 158..195: this trait cannot be derived for unions + 196..232: this trait cannot be derived for unions + 233..276: this trait cannot be derived for unions + 313..355: this trait cannot be derived for unions"#]], + ); +} + +#[test] +fn default_enum_without_default_attr() { + check_errors( + r#" +//- minicore: default, derive + +#[derive(Default)] +enum Foo { + Bar, +} + "#, + expect!["1..41: `#[derive(Default)]` on enum with no `#[default]`"], + ); +} + +#[test] +fn generic_enum_default() { + check( + r#" +//- minicore: default, derive + +#[derive(Default)] +enum Foo { + Bar(T), + #[default] + Baz, +} +"#, + expect![[r#" + +#[derive(Default)] +enum Foo { + Bar(T), + #[default] + Baz, +} + +impl $crate::default::Default for Foo where { + fn default() -> Self { + Foo::Baz + } +}"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs index ba75dca3d3de1..338851b715bf0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs @@ -172,7 +172,7 @@ fn no() {} "ast_id_map_shim", "parse_shim", "real_span_map_shim", - "of_", + "EnumVariants::of_", ] "#]], expect![[r#" @@ -181,7 +181,7 @@ fn no() {} "ast_id_map_shim", "file_item_tree_query", "real_span_map_shim", - "of_", + "EnumVariants::of_", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs index d135584a08095..15e68ff95cd57 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs @@ -458,6 +458,7 @@ fn expand_simple_derive( invoc_span: Span, tt: &tt::TopSubtree, trait_path: tt::TopSubtree, + allow_unions: bool, make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree, ) -> ExpandResult { let info = match parse_adt(db, tt, invoc_span) { @@ -469,6 +470,12 @@ fn expand_simple_derive( ); } }; + if !allow_unions && matches!(info.shape, AdtShape::Union) { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(invoc_span)), + ExpandError::other(invoc_span, "this trait cannot be derived for unions"), + ); + } ExpandResult::ok(expand_simple_derive_with_parsed( invoc_span, info, @@ -535,7 +542,14 @@ fn copy_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>}) + expand_simple_derive( + db, + span, + tt, + quote! {span => #krate::marker::Copy }, + true, + |_| quote! {span =>}, + ) } fn clone_expand( @@ -544,7 +558,7 @@ fn clone_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, true, |adt| { if matches!(adt.shape, AdtShape::Union) { let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; return quote! {span => @@ -599,41 +613,63 @@ fn default_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| { - let body = match &adt.shape { - AdtShape::Struct(fields) => { - let name = &adt.name; - fields.as_pattern_map( - quote!(span =>#name), + let adt = match parse_adt(db, tt, span) { + Ok(info) => info, + Err(e) => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }), + e, + ); + } + }; + let (body, constrain_to_trait) = match &adt.shape { + AdtShape::Struct(fields) => { + let name = &adt.name; + let body = fields.as_pattern_map( + quote!(span =>#name), + span, + |_| quote!(span =>#krate::default::Default::default()), + ); + (body, true) + } + AdtShape::Enum { default_variant, variants } => { + if let Some(d) = default_variant { + let (name, fields) = &variants[*d]; + let adt_name = &adt.name; + let body = fields.as_pattern_map( + quote!(span =>#adt_name :: #name), span, |_| quote!(span =>#krate::default::Default::default()), - ) + ); + (body, false) + } else { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "`#[derive(Default)]` on enum with no `#[default]`"), + ); } - AdtShape::Enum { default_variant, variants } => { - if let Some(d) = default_variant { - let (name, fields) = &variants[*d]; - let adt_name = &adt.name; - fields.as_pattern_map( - quote!(span =>#adt_name :: #name), - span, - |_| quote!(span =>#krate::default::Default::default()), - ) - } else { - // FIXME: Return expand error here - quote!(span =>) + } + AdtShape::Union => { + return ExpandResult::new( + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + ExpandError::other(span, "this trait cannot be derived for unions"), + ); + } + }; + ExpandResult::ok(expand_simple_derive_with_parsed( + span, + adt, + quote! {span => #krate::default::Default }, + |_adt| { + quote! {span => + fn default() -> Self { + #body } } - AdtShape::Union => { - // FIXME: Return expand error here - quote!(span =>) - } - }; - quote! {span => - fn default() -> Self { - #body - } - } - }) + }, + constrain_to_trait, + tt::TopSubtree::empty(tt::DelimSpan::from_single(span)), + )) } fn debug_expand( @@ -642,7 +678,7 @@ fn debug_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, false, |adt| { let for_variant = |name: String, v: &VariantShape| match v { VariantShape::Struct(fields) => { let for_fields = fields.iter().map(|it| { @@ -697,10 +733,7 @@ fn debug_expand( } }) .collect(), - AdtShape::Union => { - // FIXME: Return expand error here - vec![] - } + AdtShape::Union => unreachable!(), }; quote! {span => fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { @@ -718,11 +751,7 @@ fn hash_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| { - if matches!(adt.shape, AdtShape::Union) { - // FIXME: Return expand error here - return quote! {span =>}; - } + expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, false, |adt| { if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span }; return quote! {span => @@ -769,7 +798,14 @@ fn eq_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>}) + expand_simple_derive( + db, + span, + tt, + quote! {span => #krate::cmp::Eq }, + true, + |_| quote! {span =>}, + ) } fn partial_eq_expand( @@ -778,11 +814,7 @@ fn partial_eq_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| { - if matches!(adt.shape, AdtShape::Union) { - // FIXME: Return expand error here - return quote! {span =>}; - } + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, false, |adt| { let name = &adt.name; let (self_patterns, other_patterns) = self_and_other_patterns(adt, name, span); @@ -854,7 +886,7 @@ fn ord_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, false, |adt| { fn compare( krate: &tt::Ident, left: tt::TopSubtree, @@ -873,10 +905,6 @@ fn ord_expand( } } } - if matches!(adt.shape, AdtShape::Union) { - // FIXME: Return expand error here - return quote!(span =>); - } let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span); let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map( |(pat1, pat2, fields)| { @@ -916,7 +944,7 @@ fn partial_ord_expand( tt: &tt::TopSubtree, ) -> ExpandResult { let krate = &dollar_crate(span); - expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| { + expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, false, |adt| { fn compare( krate: &tt::Ident, left: tt::TopSubtree, @@ -935,10 +963,6 @@ fn partial_ord_expand( } } } - if matches!(adt.shape, AdtShape::Union) { - // FIXME: Return expand error here - return quote!(span =>); - } let left = quote!(span =>#krate::intrinsics::discriminant_value(self)); let right = quote!(span =>#krate::intrinsics::discriminant_value(other)); diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index f9abe4f5566e7..800b40a9e7e5b 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -7,6 +7,7 @@ use intern::{ Symbol, sym::{self}, }; +use itertools::Itertools; use mbe::{DelimiterKind, expect_fragment}; use span::{Edition, FileId, Span}; use stdx::format_to; @@ -681,11 +682,19 @@ fn relative_file( } fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { - let delimiter = tt.top_subtree().delimiter; - tt.iter() - .next() - .ok_or(delimiter.open.cover(delimiter.close)) - .and_then(|tt| match tt { + let mut tt = TtElement::Subtree(tt.top_subtree(), tt.iter()); + (|| { + // FIXME: We wrap expression fragments in parentheses which can break this expectation + // here + // Remove this once we handle none delims correctly + while let TtElement::Subtree(sub, tt_iter) = &mut tt + && let DelimiterKind::Parenthesis | DelimiterKind::Invisible = sub.delimiter.kind + { + tt = + tt_iter.exactly_one().map_err(|_| sub.delimiter.open.cover(sub.delimiter.close))?; + } + + match tt { TtElement::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, @@ -698,35 +707,11 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> { kind: tt::LitKind::StrRaw(_), suffix: _, })) => Ok((text.clone(), *span)), - // FIXME: We wrap expression fragments in parentheses which can break this expectation - // here - // Remove this once we handle none delims correctly - TtElement::Subtree(tt, mut tt_iter) - if tt.delimiter.kind == DelimiterKind::Parenthesis => - { - tt_iter - .next() - .and_then(|tt| match tt { - TtElement::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::Str, - suffix: _, - })) => Some((unescape_symbol(text), *span)), - TtElement::Leaf(tt::Leaf::Literal(tt::Literal { - symbol: text, - span, - kind: tt::LitKind::StrRaw(_), - suffix: _, - })) => Some((text.clone(), *span)), - _ => None, - }) - .ok_or(delimiter.open.cover(delimiter.close)) - } TtElement::Leaf(l) => Err(*l.span()), TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)), - }) - .map_err(|span| ExpandError::other(span, "expected string literal")) + } + })() + .map_err(|span| ExpandError::other(span, "expected string literal")) } fn include_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 7e9928c41ff5e..888c1405a6bb1 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -145,7 +145,7 @@ pub trait ExpandDatabase: RootQueryDb { fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext; } -#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext)] +#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext, revisions = usize::MAX)] pub struct SyntaxContextWrapper { pub data: SyntaxContext, } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index a73a22370d240..6730b337d356f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -315,11 +315,11 @@ impl> InFile { } /// Falls back to the macro call range if the node cannot be mapped up fully. - pub fn original_file_range_with_macro_call_body( + pub fn original_file_range_with_macro_call_input( self, db: &dyn db::ExpandDatabase, ) -> FileRange { - self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db) + self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_input(db) } pub fn original_syntax_node_rooted( @@ -465,7 +465,7 @@ impl InFile { } } - pub fn original_node_file_range_with_macro_call_body( + pub fn original_node_file_range_with_macro_call_input( self, db: &dyn db::ExpandDatabase, ) -> FileRange { @@ -476,7 +476,7 @@ impl InFile { Some(it) => it, _ => { let loc = db.lookup_intern_macro_call(mac_file); - loc.kind.original_call_range_with_body(db) + loc.kind.original_call_range_with_input(db) } } } @@ -497,6 +497,18 @@ impl InFile { } } } + + pub fn original_node_file_range_rooted_opt( + self, + db: &dyn db::ExpandDatabase, + ) -> Option { + match self.file_id { + HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }), + HirFileId::MacroFile(mac_file) => { + map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) + } + } + } } impl InFile { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 6ecac1463f5c4..ac61b22009706 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -199,9 +199,9 @@ impl ExpandErrorKind { }, &ExpandErrorKind::MissingProcMacroExpander(def_crate) => { match db.proc_macros_for_crate(def_crate).as_ref().and_then(|it| it.get_error()) { - Some((e, hard_err)) => RenderedExpandError { - message: e.to_owned(), - error: hard_err, + Some(e) => RenderedExpandError { + message: e.to_string(), + error: e.is_hard_error(), kind: RenderedExpandError::GENERAL_KIND, }, None => RenderedExpandError { @@ -688,8 +688,11 @@ impl MacroCallKind { /// Returns the original file range that best describes the location of this macro call. /// - /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives. - pub fn original_call_range_with_body(self, db: &dyn ExpandDatabase) -> FileRange { + /// This spans the entire macro call, including its input. That is for + /// - fn_like! {}, it spans the path and token tree + /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item + /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item + pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { match kind.file_id() { @@ -712,8 +715,8 @@ impl MacroCallKind { /// Returns the original file range that best describes the location of this macro call. /// /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros - /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives - /// get only the specific derive that is being referred to. + /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the + /// attribute's range, and derives get only the specific derive that is being referred to. pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange { let mut kind = self; let file_id = loop { @@ -726,7 +729,14 @@ impl MacroCallKind { }; let range = match kind { - MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(), + MacroCallKind::FnLike { ast_id, .. } => { + let node = ast_id.to_node(db); + node.path() + .unwrap() + .syntax() + .text_range() + .cover(node.excl_token().unwrap().text_range()) + } MacroCallKind::Derive { ast_id, derive_attr_index, .. } => { // FIXME: should be the range of the macro name, not the whole derive // FIXME: handle `cfg_attr` @@ -1056,7 +1066,7 @@ impl ExpandTo { intern::impl_internable!(ModPath, attrs::AttrInput); -#[salsa_macros::interned(no_lifetime, debug)] +#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { pub loc: MacroCallLoc, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index 217d991d110d5..679f61112ad53 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -179,9 +179,10 @@ impl Name { self.symbol.as_str() } + #[inline] pub fn display<'a>( &'a self, - db: &dyn crate::db::ExpandDatabase, + db: &dyn salsa::Database, edition: Edition, ) -> impl fmt::Display + 'a { _ = db; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs index 6134c3a36b931..6431d46d39e97 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -46,7 +46,7 @@ pub fn prettify_macro_expansion( } else if let Some(crate_name) = ¯o_def_crate.extra_data(db).display_name { make::tokens::ident(crate_name.crate_name().as_str()) } else { - return dollar_crate.clone(); + dollar_crate.clone() } }); if replacement.text() == "$crate" { diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 1c8ebb6f5355f..f97d721dfa884 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -4,7 +4,7 @@ use core::fmt; use std::any::Any; use std::{panic::RefUnwindSafe, sync}; -use base_db::{Crate, CrateBuilderId, CratesIdMap, Env}; +use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError}; use intern::Symbol; use rustc_hash::FxHashMap; use span::Span; @@ -53,8 +53,8 @@ pub enum ProcMacroExpansionError { System(String), } -pub type ProcMacroLoadResult = Result, (String, bool)>; -type StoredProcMacroLoadResult = Result, (Box, bool)>; +pub type ProcMacroLoadResult = Result, ProcMacroLoadingError>; +type StoredProcMacroLoadResult = Result, ProcMacroLoadingError>; #[derive(Default, Debug)] pub struct ProcMacrosBuilder(FxHashMap>); @@ -77,9 +77,7 @@ impl ProcMacrosBuilder { proc_macros_crate, match proc_macro { Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))), - Err((e, hard_err)) => { - Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err)))) - } + Err(e) => Arc::new(CrateProcMacros(Err(e))), }, ); } @@ -139,8 +137,8 @@ impl CrateProcMacros { ) } - pub fn get_error(&self) -> Option<(&str, bool)> { - self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err)) + pub fn get_error(&self) -> Option<&ProcMacroLoadingError> { + self.0.as_ref().err() } /// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 1029969992c67..5d3be07f3db2d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -236,15 +236,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { fn trait_impls_in_deps(&self, krate: Crate) -> Arc<[Arc]>; // Interned IDs for Chalk integration - #[salsa::interned] - fn intern_type_or_const_param_id( - &self, - param_id: TypeOrConstParamId, - ) -> InternedTypeOrConstParamId; - - #[salsa::interned] - fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId; - #[salsa::interned] fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId; @@ -282,9 +273,8 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug { #[salsa::invoke(crate::variance::variances_of)] #[salsa::cycle( - // cycle_fn = crate::variance::variances_of_cycle_fn, - // cycle_initial = crate::variance::variances_of_cycle_initial, - cycle_result = crate::variance::variances_of_cycle_initial, + cycle_fn = crate::variance::variances_of_cycle_fn, + cycle_initial = crate::variance::variances_of_cycle_initial, )] fn variances_of(&self, def: GenericDefId) -> Option>; @@ -329,9 +319,31 @@ fn hir_database_is_dyn_compatible() { fn _assert_dyn_compatible(_: &dyn HirDatabase) {} } -impl_intern_key!(InternedTypeOrConstParamId, TypeOrConstParamId); +#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedTypeOrConstParamId { + pub loc: TypeOrConstParamId, +} +impl ::std::fmt::Debug for InternedTypeOrConstParamId { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.debug_tuple(stringify!(InternedTypeOrConstParamId)) + .field(&format_args!("{:04x}", self.0.index())) + .finish() + } +} -impl_intern_key!(InternedLifetimeParamId, LifetimeParamId); +#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)] +#[derive(PartialOrd, Ord)] +pub struct InternedLifetimeParamId { + pub loc: LifetimeParamId, +} +impl ::std::fmt::Debug for InternedLifetimeParamId { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + f.debug_tuple(stringify!(InternedLifetimeParamId)) + .field(&format_args!("{:04x}", self.0.index())) + .finish() + } +} impl_intern_key!(InternedConstParamId, ConstParamId); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index 0bce32a67782f..c3ab5aff3dbab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -25,7 +25,6 @@ use crate::{ db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, - lang_items::is_box, }; use self::pat_util::EnumerateAndAdjustIterator; @@ -77,7 +76,7 @@ pub(crate) enum PatKind { subpatterns: Vec, }, - /// `box P`, `&P`, `&mut P`, etc. + /// `&P`, `&mut P`, etc. Deref { subpattern: Pat, }, @@ -406,7 +405,6 @@ impl HirDisplay for Pat { } PatKind::Deref { subpattern } => { match self.ty.kind(Interner) { - TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?, &TyKind::Ref(mutbl, ..) => { write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })? } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index 7cf22c64d0f8e..22b7f5ac9fd89 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -21,7 +21,7 @@ use crate::{ inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, }; -use super::{FieldPat, Pat, PatKind, is_box}; +use super::{FieldPat, Pat, PatKind}; use Constructor::*; @@ -170,8 +170,6 @@ impl<'db> MatchCheckCtx<'db> { } PatKind::Deref { subpattern } => { ctor = match pat.ty.kind(Interner) { - // This is a box pattern. - TyKind::Adt(adt, _) if is_box(self.db, adt.0) => Struct, TyKind::Ref(..) => Ref, _ => { never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty); @@ -194,23 +192,6 @@ impl<'db> MatchCheckCtx<'db> { ctor = Struct; arity = substs.len(Interner); } - TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_, - // _)` or a box pattern. As a hack to avoid an ICE with the former, we - // ignore other fields than the first one. This will trigger an error later - // anyway. - // See https://github.com/rust-lang/rust/issues/82772 , - // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977 - // The problem is that we can't know from the type whether we'll match - // normally or through box-patterns. We'll have to figure out a proper - // solution when we introduce generalized deref patterns. Also need to - // prevent mixing of those two options. - fields.retain(|ipat| ipat.idx == 0); - ctor = Struct; - arity = 1; - } &TyKind::Adt(AdtId(adt), _) => { ctor = match pat.kind.as_ref() { PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => { @@ -277,12 +258,6 @@ impl<'db> MatchCheckCtx<'db> { }) .collect(), }, - TyKind::Adt(adt, _) if is_box(self.db, adt.0) => { - // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside - // of `std`). So this branch is only reachable when the feature is enabled and - // the pattern is a box pattern. - PatKind::Deref { subpattern: subpatterns.next().unwrap() } - } TyKind::Adt(adt, substs) => { let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap(); let subpatterns = self @@ -343,14 +318,8 @@ impl PatCx for MatchCheckCtx<'_> { Struct | Variant(_) | UnionField => match *ty.kind(Interner) { TyKind::Tuple(arity, ..) => arity, TyKind::Adt(AdtId(adt), ..) => { - if is_box(self.db, adt) { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - 1 - } else { - let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); - variant.fields(self.db).fields().len() - } + let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); + variant.fields(self.db).fields().len() } _ => { never!("Unexpected type for `Single` constructor: {:?}", ty); @@ -383,29 +352,22 @@ impl PatCx for MatchCheckCtx<'_> { tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect() } TyKind::Ref(.., rty) => single(rty.clone()), - &TyKind::Adt(AdtId(adt), ref substs) => { - if is_box(self.db, adt) { - // The only legal patterns of type `Box` (outside `std`) are `_` and box - // patterns. If we're here we can assume this is a box pattern. - let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone(); - single(subst_ty) - } else { - let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); - - let visibilities = LazyCell::new(|| self.db.field_visibilities(variant)); - - self.list_variant_fields(ty, variant) - .map(move |(fid, ty)| { - let is_visible = || { - matches!(adt, hir_def::AdtId::EnumId(..)) - || visibilities[fid].is_visible_from(self.db, self.module) - }; - let is_uninhabited = self.is_uninhabited(&ty); - let private_uninhabited = is_uninhabited && !is_visible(); - (ty, PrivateUninhabitedField(private_uninhabited)) - }) - .collect() - } + &TyKind::Adt(AdtId(adt), ..) => { + let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap(); + + let visibilities = LazyCell::new(|| self.db.field_visibilities(variant)); + + self.list_variant_fields(ty, variant) + .map(move |(fid, ty)| { + let is_visible = || { + matches!(adt, hir_def::AdtId::EnumId(..)) + || visibilities[fid].is_visible_from(self.db, self.module) + }; + let is_uninhabited = self.is_uninhabited(&ty); + let private_uninhabited = is_uninhabited && !is_visible(); + (ty, PrivateUninhabitedField(private_uninhabited)) + }) + .collect() } ty_kind => { never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 507bab292083c..810fe76f23113 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1432,10 +1432,10 @@ impl HirDisplay for Ty { match f.closure_style { ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"), ClosureStyle::ClosureWithId => { - return write!(f, "{{closure#{:?}}}", id.0.as_u32()); + return write!(f, "{{closure#{:?}}}", id.0.index()); } ClosureStyle::ClosureWithSubst => { - write!(f, "{{closure#{:?}}}", id.0.as_u32())?; + write!(f, "{{closure#{:?}}}", id.0.index())?; return hir_fmt_generics(f, substs.as_slice(Interner), None, None); } _ => (), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index ce53198e966f3..e880438e3a78e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -460,19 +460,17 @@ pub struct InferenceResult { /// Whenever a tuple field expression access a tuple field, we allocate a tuple id in /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of /// that which allows us to resolve a [`TupleFieldId`]s type. - pub tuple_field_access_types: FxHashMap, + tuple_field_access_types: FxHashMap, /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead. - pub diagnostics: Vec, - pub type_of_expr: ArenaMap, + diagnostics: Vec, + pub(crate) type_of_expr: ArenaMap, /// For each pattern record the type it resolves to. /// /// **Note**: When a pattern type is resolved it may still contain /// unresolved or missing subpatterns or subpatterns of mismatched types. - pub type_of_pat: ArenaMap, - pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, - /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. - pub type_of_for_iterator: FxHashMap, + pub(crate) type_of_pat: ArenaMap, + pub(crate) type_of_binding: ArenaMap, + pub(crate) type_of_rpit: ArenaMap, type_mismatches: FxHashMap, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to @@ -483,7 +481,7 @@ pub struct InferenceResult { // FIXME: Move this into `InferenceContext` standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. - pub pat_adjustments: FxHashMap>, + pub(crate) pat_adjustments: FxHashMap>, /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. /// /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an @@ -497,12 +495,12 @@ pub struct InferenceResult { /// } /// ``` /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. - pub binding_modes: ArenaMap, - pub expr_adjustments: FxHashMap>, + pub(crate) binding_modes: ArenaMap, + pub(crate) expr_adjustments: FxHashMap>, pub(crate) closure_info: FxHashMap, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, - pub coercion_casts: FxHashSet, + pub(crate) coercion_casts: FxHashSet, } impl InferenceResult { @@ -566,6 +564,26 @@ impl InferenceResult { pub fn is_erroneous(&self) -> bool { self.has_errors && self.type_of_expr.iter().count() == 0 } + + pub fn diagnostics(&self) -> &[InferenceDiagnostic] { + &self.diagnostics + } + + pub fn tuple_field_access_type(&self, id: TupleId) -> &Substitution { + &self.tuple_field_access_types[&id] + } + + pub fn pat_adjustment(&self, id: PatId) -> Option<&[Ty]> { + self.pat_adjustments.get(&id).map(|it| &**it) + } + + pub fn expr_adjustment(&self, id: ExprId) -> Option<&[Adjustment]> { + self.expr_adjustments.get(&id).map(|it| &**it) + } + + pub fn binding_mode(&self, id: PatId) -> Option { + self.binding_modes.get(id).copied() + } } impl Index for InferenceResult { @@ -772,7 +790,6 @@ impl<'db> InferenceContext<'db> { type_of_pat, type_of_binding, type_of_rpit, - type_of_for_iterator, type_mismatches, has_errors, standard_types: _, @@ -832,11 +849,6 @@ impl<'db> InferenceContext<'db> { *has_errors = *has_errors || ty.contains_unknown(); } type_of_rpit.shrink_to_fit(); - for ty in type_of_for_iterator.values_mut() { - *ty = table.resolve_completely(ty.clone()); - *has_errors = *has_errors || ty.contains_unknown(); - } - type_of_for_iterator.shrink_to_fit(); *has_errors |= !type_mismatches.is_empty(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 65a273cdf8d64..c3029bf2b59ad 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -1229,10 +1229,11 @@ impl InferenceContext<'_> { self.select_from_expr(*expr); } } - Expr::Let { pat: _, expr } => { + Expr::Let { pat, expr } => { self.walk_expr(*expr); - let place = self.place_of_expr(*expr); - self.ref_expr(*expr, place); + if let Some(place) = self.place_of_expr(*expr) { + self.consume_with_pat(place, *pat); + } } Expr::UnaryOp { expr, op: _ } | Expr::Array(Array::Repeat { initializer: expr, repeat: _ }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index d40d52c134d4b..d43c99fc28271 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -731,9 +731,32 @@ impl InferenceContext<'_> { &Pat::Expr(expr) => { Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No)) } - Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)), + Pat::Path(path) => { + let resolver_guard = + self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr); + let resolution = self.resolver.resolve_path_in_value_ns_fully( + self.db, + path, + self.body.pat_path_hygiene(target), + ); + self.resolver.reset_to_guard(resolver_guard); + + if matches!( + resolution, + Some( + ValueNs::ConstId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + ) + ) { + None + } else { + Some(self.infer_expr_path(path, target.into(), tgt_expr)) + } + } _ => None, }; + let is_destructuring_assignment = lhs_ty.is_none(); if let Some(lhs_ty) = lhs_ty { self.write_pat_ty(target, lhs_ty.clone()); @@ -747,7 +770,15 @@ impl InferenceContext<'_> { self.inside_assignment = false; self.resolver.reset_to_guard(resolver_guard); } - self.result.standard_types.unit.clone() + if is_destructuring_assignment && self.diverges.is_always() { + // Ordinary assignments always return `()`, even when they diverge. + // However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail + // expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring + // assignments into blocks. + self.table.new_maybe_never_var() + } else { + self.result.standard_types.unit.clone() + } } Expr::Range { lhs, rhs, range_type } => { let lhs_ty = diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs index 6936d8193eb02..9d3d2044c43e4 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs @@ -13,7 +13,8 @@ use salsa::{ use crate::{ AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId, Interner, OpaqueTyId, - PlaceholderIndex, chalk_db, db::HirDatabase, + PlaceholderIndex, chalk_db, + db::{HirDatabase, InternedLifetimeParamId, InternedTypeOrConstParamId}, }; pub trait ToChalk { @@ -125,30 +126,32 @@ pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId { pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = FromId::from_id(unsafe { Id::from_u32(idx.idx.try_into().unwrap()) }); - db.lookup_intern_type_or_const_param_id(interned_id) + let interned_id = + InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) }); + interned_id.loc(db) } pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> PlaceholderIndex { - let interned_id = db.intern_type_or_const_param_id(id); + let interned_id = InternedTypeOrConstParamId::new(db, id); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, - idx: interned_id.as_id().as_u32() as usize, + idx: interned_id.as_id().index() as usize, } } pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId { assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT); // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound. - let interned_id = FromId::from_id(unsafe { Id::from_u32(idx.idx.try_into().unwrap()) }); - db.lookup_intern_lifetime_param_id(interned_id) + let interned_id = + InternedLifetimeParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) }); + interned_id.loc(db) } pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex { - let interned_id = db.intern_lifetime_param_id(id); + let interned_id = InternedLifetimeParamId::new(db, id); PlaceholderIndex { ui: chalk_ir::UniverseIndex::ROOT, - idx: interned_id.as_id().as_u32() as usize, + idx: interned_id.as_id().index() as usize, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index d049c678e2db5..b5de0e52f5b63 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -166,10 +166,10 @@ impl TestDB { self.events.lock().unwrap().take().unwrap() } - pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec { + pub(crate) fn log_executed(&self, f: impl FnOnce()) -> (Vec, Vec) { let events = self.log(f); - events - .into_iter() + let executed = events + .iter() .filter_map(|e| match e.kind { // This is pretty horrible, but `Debug` is the only way to inspect // QueryDescriptor at the moment. @@ -181,6 +181,7 @@ impl TestDB { } _ => None, }) - .collect() + .collect(); + (executed, events) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 7fb981752de8c..dbc68eeba1e64 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -446,7 +446,7 @@ fn main() { } #[test] -fn let_binding_is_a_ref_capture() { +fn let_binding_is_a_ref_capture_in_ref_binding() { check_closure_captures( r#" //- minicore:copy @@ -454,12 +454,36 @@ struct S; fn main() { let mut s = S; let s_ref = &mut s; + let mut s2 = S; + let s_ref2 = &mut s2; let closure = || { if let ref cb = s_ref { + } else if let ref mut cb = s_ref2 { } }; } "#, - expect!["83..135;49..54;112..117 ByRef(Shared) s_ref &'? &'? mut S"], + expect![[r#" + 129..225;49..54;149..155 ByRef(Shared) s_ref &'? &'? mut S + 129..225;93..99;188..198 ByRef(Mut { kind: Default }) s_ref2 &'? mut &'? mut S"#]], + ); +} + +#[test] +fn let_binding_is_a_value_capture_in_binding() { + check_closure_captures( + r#" +//- minicore:copy, option +struct Box(i32); +fn main() { + let b = Some(Box(0)); + let closure = || { + if let Some(b) = b { + let _move = b; + } + }; +} +"#, + expect!["73..149;37..38;103..104 ByValue b Option"], ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index 0377ce95f190c..3159499e86707 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -1,6 +1,7 @@ use base_db::SourceDatabase; use expect_test::Expect; use hir_def::{DefWithBodyId, ModuleDefId}; +use salsa::EventKind; use test_fixture::WithFixture; use crate::{db::HirDatabase, test_db::TestDB}; @@ -567,11 +568,11 @@ fn main() { "ast_id_map_shim", "parse_shim", "real_span_map_shim", - "query_with_diagnostics_", + "TraitItems::query_with_diagnostics_", "body_shim", "body_with_source_map_shim", "attrs_shim", - "of_", + "ImplItems::of_", "infer_shim", "trait_signature_shim", "trait_signature_with_source_map_shim", @@ -596,8 +597,8 @@ fn main() { "struct_signature_with_source_map_shim", "generic_predicates_shim", "value_ty_shim", - "firewall_", - "query_", + "VariantFields::firewall_", + "VariantFields::query_", "lang_item", "inherent_impls_in_crate_shim", "impl_signature_shim", @@ -674,11 +675,11 @@ fn main() { "file_item_tree_query", "real_span_map_shim", "crate_local_def_map", - "query_with_diagnostics_", + "TraitItems::query_with_diagnostics_", "body_with_source_map_shim", "attrs_shim", "body_shim", - "of_", + "ImplItems::of_", "infer_shim", "attrs_shim", "trait_signature_with_source_map_shim", @@ -697,7 +698,7 @@ fn main() { "function_signature_with_source_map_shim", "expr_scopes_shim", "struct_signature_with_source_map_shim", - "query_", + "VariantFields::query_", "inherent_impls_in_crate_shim", "impl_signature_with_source_map_shim", "impl_signature_shim", @@ -718,10 +719,23 @@ fn execute_assert_events( required: &[(&str, usize)], expect: Expect, ) { - let events = db.log_executed(f); - for (event, count) in required { - let n = events.iter().filter(|it| it.contains(event)).count(); - assert_eq!(n, *count, "Expected {event} to be executed {count} times, but only got {n}"); - } - expect.assert_debug_eq(&events); + let (executed, events) = db.log_executed(f); + salsa::attach(db, || { + for (event, count) in required { + let n = executed.iter().filter(|it| it.contains(event)).count(); + assert_eq!( + n, + *count, + "Expected {event} to be executed {count} times, but only got {n}:\n \ + Executed: {executed:#?}\n \ + Event log: {events:#?}", + events = events + .iter() + .filter(|event| !matches!(event.kind, EventKind::WillCheckCancellation)) + .map(|event| { format!("{:?}", event.kind) }) + .collect::>(), + ); + } + expect.assert_debug_eq(&executed); + }); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 1ca4c9b2ad5a3..6a9135622deb6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -785,3 +785,31 @@ fn make_up_a_pointer() -> *const T { "#]], ) } + +#[test] +fn diverging_destructuring_assignment() { + check_infer_with_mismatches( + r#" +fn foo() { + let n = match 42 { + 0 => _ = loop {}, + _ => 0, + }; +} + "#, + expect![[r#" + 9..84 '{ ... }; }': () + 19..20 'n': i32 + 23..81 'match ... }': i32 + 29..31 '42': i32 + 42..43 '0': i32 + 42..43 '0': i32 + 47..48 '_': ! + 47..58 '_ = loop {}': i32 + 51..58 'loop {}': ! + 56..58 '{}': () + 68..69 '_': i32 + 73..74 '0': i32 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 43e8f3747ab8c..b154e59878571 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -3751,7 +3751,7 @@ fn foo() { } let v: bool = true; m!(); - // ^^^^ i32 + // ^^ i32 } "#, ); @@ -3765,39 +3765,39 @@ fn foo() { let v: bool; macro_rules! m { () => { v } } m!(); - // ^^^^ bool + // ^^ bool let v: char; macro_rules! m { () => { v } } m!(); - // ^^^^ char + // ^^ char { let v: u8; macro_rules! m { () => { v } } m!(); - // ^^^^ u8 + // ^^ u8 let v: i8; macro_rules! m { () => { v } } m!(); - // ^^^^ i8 + // ^^ i8 let v: i16; macro_rules! m { () => { v } } m!(); - // ^^^^ i16 + // ^^ i16 { let v: u32; macro_rules! m { () => { v } } m!(); - // ^^^^ u32 + // ^^ u32 let v: u64; macro_rules! m { () => { v } } m!(); - // ^^^^ u64 + // ^^ u64 } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs index 08a215fecf623..87d9df611bda1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs @@ -54,14 +54,14 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option>, -// _count: u32, -// _def: GenericDefId, -// ) -> salsa::CycleRecoveryAction>> { -// salsa::CycleRecoveryAction::Iterate -// } +pub(crate) fn variances_of_cycle_fn( + _db: &dyn HirDatabase, + _result: &Option>, + _count: u32, + _def: GenericDefId, +) -> salsa::CycleRecoveryAction>> { + salsa::CycleRecoveryAction::Iterate +} pub(crate) fn variances_of_cycle_initial( db: &dyn HirDatabase, @@ -965,7 +965,7 @@ struct S3(S); struct FixedPoint(&'static FixedPoint<(), T, U>, V); "#, expect![[r#" - FixedPoint[T: bivariant, U: bivariant, V: bivariant] + FixedPoint[T: covariant, U: covariant, V: covariant] "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index aba2e032b320e..c1e814ec223e3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -36,16 +36,16 @@ pub use hir_ty::{ }; macro_rules! diagnostics { - ($($diag:ident $(<$lt:lifetime>)?,)*) => { + ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => { #[derive(Debug)] - pub enum AnyDiagnostic<'db> {$( + pub enum $AnyDiagnostic<$db> {$( $diag(Box<$diag $(<$lt>)?>), )*} $( - impl<'db> From<$diag $(<$lt>)?> for AnyDiagnostic<'db> { - fn from(d: $diag $(<$lt>)?) -> AnyDiagnostic<'db> { - AnyDiagnostic::$diag(Box::new(d)) + impl<$db> From<$diag $(<$lt>)?> for $AnyDiagnostic<$db> { + fn from(d: $diag $(<$lt>)?) -> $AnyDiagnostic<$db> { + $AnyDiagnostic::$diag(Box::new(d)) } } )* @@ -66,7 +66,7 @@ macro_rules! diagnostics { // }, ... // ] -diagnostics![ +diagnostics![AnyDiagnostic<'db> -> AwaitOutsideOfAsync, BreakOutsideOfLoop, CastToUnsized<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index e8a18169712cb..5c6f622e6c3e9 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1260,7 +1260,9 @@ impl TupleField { } pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> { - let ty = db.infer(self.owner).tuple_field_access_types[&self.tuple] + let ty = db + .infer(self.owner) + .tuple_field_access_type(self.tuple) .as_slice(Interner) .get(self.index as usize) .and_then(|arg| arg.ty(Interner)) @@ -1927,7 +1929,7 @@ impl DefWithBody { expr_store_diagnostics(db, acc, &source_map); let infer = db.infer(self.into()); - for d in &infer.diagnostics { + for d in infer.diagnostics() { acc.extend(AnyDiagnostic::inference_diagnostic( db, self.into(), diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index f18ca7cb2017e..0662bfddcf866 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -254,7 +254,7 @@ impl<'db> SourceAnalyzer<'db> { // expressions nor patterns). let expr_id = self.expr_id(expr.clone())?.as_expr()?; let infer = self.infer()?; - infer.expr_adjustments.get(&expr_id).map(|v| &**v) + infer.expr_adjustment(expr_id) } pub(crate) fn type_of_type( @@ -286,7 +286,7 @@ impl<'db> SourceAnalyzer<'db> { let infer = self.infer()?; let coerced = expr_id .as_expr() - .and_then(|expr_id| infer.expr_adjustments.get(&expr_id)) + .and_then(|expr_id| infer.expr_adjustment(expr_id)) .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone())); let ty = infer[expr_id].clone(); let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty); @@ -302,12 +302,11 @@ impl<'db> SourceAnalyzer<'db> { let infer = self.infer()?; let coerced = match expr_or_pat_id { ExprOrPatId::ExprId(idx) => infer - .expr_adjustments - .get(&idx) + .expr_adjustment(idx) .and_then(|adjusts| adjusts.last().cloned()) .map(|adjust| adjust.target), ExprOrPatId::PatId(idx) => { - infer.pat_adjustments.get(&idx).and_then(|adjusts| adjusts.last().cloned()) + infer.pat_adjustment(idx).and_then(|adjusts| adjusts.last().cloned()) } }; @@ -345,7 +344,7 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option { let id = self.pat_id(&pat.clone().into())?; let infer = self.infer()?; - infer.binding_modes.get(id.as_pat()?).map(|bm| match bm { + infer.binding_mode(id.as_pat()?).map(|bm| match bm { hir_ty::BindingMode::Move => BindingMode::Move, hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut), hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => { @@ -362,8 +361,7 @@ impl<'db> SourceAnalyzer<'db> { let infer = self.infer()?; Some( infer - .pat_adjustments - .get(&pat_id.as_pat()?)? + .pat_adjustment(pat_id.as_pat()?)? .iter() .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone())) .collect(), @@ -736,7 +734,7 @@ impl<'db> SourceAnalyzer<'db> { let variant = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?; let variant_data = variant.fields(db); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; - let (adt, subst) = self.infer()?.type_of_pat.get(pat_id.as_pat()?)?.as_adt()?; + let (adt, subst) = self.infer()?[pat_id.as_pat()?].as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); Some(( @@ -765,7 +763,8 @@ impl<'db> SourceAnalyzer<'db> { }, }; - let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT, Some(store))?; + let body_owner = self.resolver.body_owner(); + let res = resolve_hir_value_path(db, &self.resolver, body_owner, path, HygieneId::ROOT)?; match res { PathResolution::Def(def) => Some(def), _ => None, @@ -1249,7 +1248,7 @@ impl<'db> SourceAnalyzer<'db> { let infer = self.infer()?; let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?; - let substs = infer.type_of_pat[pat_id].as_adt()?.1; + let substs = infer[pat_id].as_adt()?.1; let (variant, missing_fields, _exhaustive) = record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?; @@ -1785,8 +1784,8 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H } fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> { - match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { + match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) { Some(adjustment) => Some(&adjustment.target), - None => infer.type_of_expr.get(id), + None => Some(&infer[id]), } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs index b71de5e00c6ad..c80b78fd97056 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs @@ -175,7 +175,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) -> // ast::TuplePat(it) => (), // FIXME // ast::SlicePat(it) => (), - _ => return None, + _ => None, } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs index 6316a8f0db24d..603be4d66733d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs @@ -3,8 +3,7 @@ use ide_db::{assists::AssistId, defs::Definition}; use stdx::to_upper_snake_case; use syntax::{ AstNode, - ast::{self, HasName, make}, - ted, + ast::{self, HasName, syntax_factory::SyntaxFactory}, }; use crate::{ @@ -69,15 +68,18 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) "Promote local to constant", let_stmt.syntax().text_range(), |edit| { + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(let_stmt.syntax()); let name = to_upper_snake_case(&name.to_string()); let usages = Definition::Local(local).usages(&ctx.sema).all(); if let Some(usages) = usages.references.get(&ctx.file_id()) { - let name_ref = make::name_ref(&name); + let name_ref = make.name_ref(&name); for usage in usages { let Some(usage_name) = usage.name.as_name_ref().cloned() else { continue }; if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage_name) { - let name_expr = make::expr_path(make::path_from_text(&name)); + let path = make.ident_path(&name); + let name_expr = make.expr_path(path); utils::replace_record_field_expr(ctx, edit, record_field, name_expr); } else { let usage_range = usage.range; @@ -86,15 +88,17 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>) } } - let item = make::item_const(None, make::name(&name), make::ty(&ty), initializer) - .clone_for_update(); - let let_stmt = edit.make_mut(let_stmt); + let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer); if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) { - edit.add_tabstop_before(cap, name); + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(name.syntax().clone(), tabstop); } - ted::replace(let_stmt.syntax(), item.syntax()); + editor.replace(let_stmt.syntax(), item.syntax()); + + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index e933bcc40dbbb..62914ee7f3835 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,8 +1,5 @@ use ide_db::syntax_helpers::suggest_name; -use syntax::{ - ast::{self, AstNode, make}, - ted, -}; +use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory}; use crate::{AssistContext, AssistId, Assists}; @@ -60,21 +57,25 @@ pub(crate) fn replace_is_method_with_if_let_method( message, call_expr.syntax().text_range(), |edit| { - let call_expr = edit.make_mut(call_expr); + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(call_expr.syntax()); - let var_pat = make::ident_pat(false, false, make::name(&var_name)); - let pat = make::tuple_struct_pat(make::ext::ident_path(text), [var_pat.into()]); - let let_expr = make::expr_let(pat.into(), receiver).clone_for_update(); + let var_pat = make.ident_pat(false, false, make.name(&var_name)); + let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]); + let let_expr = make.expr_let(pat.into(), receiver); if let Some(cap) = ctx.config.snippet_cap { if let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() { if let Some(first_var) = pat.fields().next() { - edit.add_placeholder_snippet(cap, first_var); + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(first_var.syntax(), placeholder); } } } - ted::replace(call_expr.syntax(), let_expr.syntax()); + editor.replace(call_expr.syntax(), let_expr.syntax()); + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index 109269bd6e611..504e12f93df61 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -1,8 +1,7 @@ use ide_db::assists::AssistId; use syntax::{ AstNode, T, - ast::{self, make}, - ted, + ast::{self, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, Assists}; @@ -37,8 +36,7 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) RCur, } - let makro = ctx.find_node_at_offset::()?.clone_for_update(); - let makro_text_range = makro.syntax().text_range(); + let makro = ctx.find_node_at_offset::()?; let cursor_offset = ctx.offset(); let semicolon = makro.semicolon_token(); @@ -71,24 +69,28 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) }, token_tree.syntax().text_range(), |builder| { + let make = SyntaxFactory::with_mappings(); + let mut editor = builder.make_editor(makro.syntax()); + match token { MacroDelims::LPar | MacroDelims::RPar => { - ted::replace(ltoken, make::token(T!['{'])); - ted::replace(rtoken, make::token(T!['}'])); + editor.replace(ltoken, make.token(T!['{'])); + editor.replace(rtoken, make.token(T!['}'])); if let Some(sc) = semicolon { - ted::remove(sc); + editor.delete(sc); } } MacroDelims::LBra | MacroDelims::RBra => { - ted::replace(ltoken, make::token(T!['('])); - ted::replace(rtoken, make::token(T![')'])); + editor.replace(ltoken, make.token(T!['('])); + editor.replace(rtoken, make.token(T![')'])); } MacroDelims::LCur | MacroDelims::RCur => { - ted::replace(ltoken, make::token(T!['['])); - ted::replace(rtoken, make::token(T![']'])); + editor.replace(ltoken, make.token(T!['['])); + editor.replace(rtoken, make.token(T![']'])); } } - builder.replace(makro_text_range, makro.syntax().text()); + editor.add_mappings(make.finish_with_mappings()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs index 5aedff5cc775d..7b0f2dc65a783 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs @@ -1,8 +1,7 @@ use syntax::{ Direction, SyntaxKind, T, - algo::neighbor, - ast::{self, AstNode, edit::IndentLevel, make}, - ted::{self, Position}, + ast::{self, AstNode, edit::IndentLevel, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, Position}, }; use crate::{AssistContext, AssistId, Assists}; @@ -33,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists}; // ``` pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let pipe_token = ctx.find_token_syntax_at_offset(T![|])?; - let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update(); + let or_pat = ast::OrPat::cast(pipe_token.parent()?)?; if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) { return None; } @@ -44,13 +43,14 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // without `OrPat`. let new_parent = match_arm.syntax().parent()?; - let old_parent_range = new_parent.text_range(); acc.add( AssistId::refactor_rewrite("unmerge_match_arm"), "Unmerge match arm", pipe_token.text_range(), |edit| { + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(&new_parent); let pats_after = pipe_token .siblings_with_tokens(Direction::Next) .filter_map(|it| ast::Pat::cast(it.into_node()?)) @@ -59,11 +59,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let new_pat = if pats_after.len() == 1 { pats_after[0].clone() } else { - make::or_pat(pats_after, or_pat.leading_pipe().is_some()).into() + make.or_pat(pats_after, or_pat.leading_pipe().is_some()).into() }; - let new_match_arm = - make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update(); - + let new_match_arm = make.match_arm(new_pat, match_arm.guard(), match_arm_body); let mut pipe_index = pipe_token.index(); if pipe_token .prev_sibling_or_token() @@ -71,10 +69,13 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O { pipe_index -= 1; } - or_pat.syntax().splice_children( - pipe_index..or_pat.syntax().children_with_tokens().count(), - Vec::new(), - ); + for child in or_pat + .syntax() + .children_with_tokens() + .skip_while(|child| child.index() < pipe_index) + { + editor.delete(child.syntax_element()); + } let mut insert_after_old_arm = Vec::new(); @@ -86,33 +87,19 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O // body is a block, but we don't bother to check that. // - Missing after the arm with arms after, if the arm body is a block. In this case // we don't want to insert a comma at all. - let has_comma_after = - std::iter::successors(match_arm.syntax().last_child_or_token(), |it| { - it.prev_sibling_or_token() - }) - .map(|it| it.kind()) - .find(|it| !it.is_trivia()) - == Some(T![,]); - let has_arms_after = neighbor(&match_arm, Direction::Next).is_some(); - if !has_comma_after && !has_arms_after { - insert_after_old_arm.push(make::token(T![,]).into()); + let has_comma_after = match_arm.comma_token().is_some(); + if !has_comma_after && !match_arm.expr().unwrap().is_block_like() { + insert_after_old_arm.push(make.token(T![,]).into()); } let indent = IndentLevel::from_node(match_arm.syntax()); - insert_after_old_arm.push(make::tokens::whitespace(&format!("\n{indent}")).into()); + insert_after_old_arm.push(make.whitespace(&format!("\n{indent}")).into()); insert_after_old_arm.push(new_match_arm.syntax().clone().into()); - ted::insert_all_raw(Position::after(match_arm.syntax()), insert_after_old_arm); - - if has_comma_after { - ted::insert_raw( - Position::last_child_of(new_match_arm.syntax()), - make::token(T![,]), - ); - } - - edit.replace(old_parent_range, new_parent.to_string()); + editor.insert_all(Position::after(match_arm.syntax()), insert_after_old_arm); + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -258,7 +245,7 @@ fn main() { let x = X::A; let y = match x { X::A => 1i32, - X::B => 1i32 + X::B => 1i32, }; } "#, @@ -276,7 +263,7 @@ enum X { A, B } fn main() { let x = X::A; match x { - X::A $0| X::B => {}, + X::A $0| X::B => {} } } "#, @@ -287,8 +274,8 @@ enum X { A, B } fn main() { let x = X::A; match x { - X::A => {}, - X::B => {}, + X::A => {} + X::B => {} } } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index e1b94673e7756..5183566d136b5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -2,8 +2,7 @@ use ide_db::source_change::SourceChangeBuilder; use itertools::Itertools; use syntax::{ NodeOrToken, SyntaxToken, T, TextRange, algo, - ast::{self, AstNode, make}, - ted::{self, Position}, + ast::{self, AstNode, make, syntax_factory::SyntaxFactory}, }; use crate::{AssistContext, AssistId, Assists}; @@ -173,40 +172,45 @@ fn wrap_derive( } } let handle_source_change = |edit: &mut SourceChangeBuilder| { - let new_derive = make::attr_outer(make::meta_token_tree( - make::ext::ident_path("derive"), - make::token_tree(T!['('], new_derive), - )) - .clone_for_update(); - let meta = make::meta_token_tree( - make::ext::ident_path("cfg_attr"), - make::token_tree( + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(attr.syntax()); + let new_derive = make.attr_outer( + make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)), + ); + let meta = make.meta_token_tree( + make.ident_path("cfg_attr"), + make.token_tree( T!['('], vec![ - NodeOrToken::Token(make::token(T![,])), - NodeOrToken::Token(make::tokens::whitespace(" ")), - NodeOrToken::Token(make::tokens::ident("derive")), - NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)), + NodeOrToken::Token(make.token(T![,])), + NodeOrToken::Token(make.whitespace(" ")), + NodeOrToken::Token(make.ident("derive")), + NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)), ], ), ); - // Remove the derive attribute - let edit_attr = edit.make_syntax_mut(attr.syntax().clone()); - - ted::replace(edit_attr, new_derive.syntax().clone()); - let cfg_attr = make::attr_outer(meta).clone_for_update(); - ted::insert_all_raw( - Position::after(new_derive.syntax().clone()), - vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()], + let cfg_attr = make.attr_outer(meta); + editor.replace_with_many( + attr.syntax(), + vec![ + new_derive.syntax().clone().into(), + make.whitespace("\n").into(), + cfg_attr.syntax().clone().into(), + ], ); + if let Some(snippet_cap) = ctx.config.snippet_cap { if let Some(first_meta) = cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) { - edit.add_tabstop_after_token(snippet_cap, first_meta) + let tabstop = edit.make_tabstop_after(snippet_cap); + editor.add_annotation(first_meta, tabstop); } } + + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }; acc.add( @@ -221,10 +225,10 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> let range = attr.syntax().text_range(); let path = attr.path()?; let handle_source_change = |edit: &mut SourceChangeBuilder| { - let mut raw_tokens = vec![ - NodeOrToken::Token(make::token(T![,])), - NodeOrToken::Token(make::tokens::whitespace(" ")), - ]; + let make = SyntaxFactory::with_mappings(); + let mut editor = edit.make_editor(attr.syntax()); + let mut raw_tokens = + vec![NodeOrToken::Token(make.token(T![,])), NodeOrToken::Token(make.whitespace(" "))]; path.syntax().descendants_with_tokens().for_each(|it| { if let NodeOrToken::Token(token) = it { raw_tokens.push(NodeOrToken::Token(token)); @@ -232,9 +236,9 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> }); if let Some(meta) = attr.meta() { if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) { - raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); raw_tokens.push(NodeOrToken::Token(eq)); - raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" "))); + raw_tokens.push(NodeOrToken::Token(make.whitespace(" "))); expr.syntax().descendants_with_tokens().for_each(|it| { if let NodeOrToken::Token(token) = it { @@ -245,26 +249,24 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) -> raw_tokens.extend(tt.token_trees_and_tokens()); } } - let meta = make::meta_token_tree( - make::ext::ident_path("cfg_attr"), - make::token_tree(T!['('], raw_tokens), - ); - let cfg_attr = if attr.excl_token().is_some() { - make::attr_inner(meta) - } else { - make::attr_outer(meta) - } - .clone_for_update(); - let attr_syntax = edit.make_syntax_mut(attr.syntax().clone()); - ted::replace(attr_syntax, cfg_attr.syntax()); + let meta = + make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens)); + let cfg_attr = + if attr.excl_token().is_some() { make.attr_inner(meta) } else { make.attr_outer(meta) }; + + editor.replace(attr.syntax(), cfg_attr.syntax()); if let Some(snippet_cap) = ctx.config.snippet_cap { if let Some(first_meta) = cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token()) { - edit.add_tabstop_after_token(snippet_cap, first_meta) + let tabstop = edit.make_tabstop_after(snippet_cap); + editor.add_annotation(first_meta, tabstop); } } + + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }; acc.add( AssistId::refactor("wrap_unwrap_cfg_attr"), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 1a91053f93ca3..87a4c2ef7584e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1,5 +1,7 @@ //! Assorted functions shared by several assists. +use std::slice; + pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{ DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, @@ -912,7 +914,7 @@ fn handle_as_ref_str( ) -> Option<(ReferenceConversionType, bool)> { let str_type = hir::BuiltinType::str().ty(db); - ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type.clone()]) + ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&str_type)) .then_some((ReferenceConversionType::AsRefStr, could_deref_to_target(ty, &str_type, db))) } @@ -924,7 +926,7 @@ fn handle_as_ref_slice( let type_argument = ty.type_arguments().next()?; let slice_type = hir::Type::new_slice(type_argument); - ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type.clone()]).then_some(( + ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some(( ReferenceConversionType::AsRefSlice, could_deref_to_target(ty, &slice_type, db), )) @@ -937,10 +939,11 @@ fn handle_dereferenced( ) -> Option<(ReferenceConversionType, bool)> { let type_argument = ty.type_arguments().next()?; - ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument.clone()]).then_some(( - ReferenceConversionType::Dereferenced, - could_deref_to_target(ty, &type_argument, db), - )) + ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&type_argument)) + .then_some(( + ReferenceConversionType::Dereferenced, + could_deref_to_target(ty, &type_argument, db), + )) } fn handle_option_as_ref( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 411902f1117ae..46a36300459d5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -878,6 +878,7 @@ mod derive { expect![[r#" de Clone macro Clone de Clone, Copy + de Debug macro Debug de Default macro Default de PartialEq macro PartialEq de PartialEq, Eq @@ -900,6 +901,7 @@ mod derive { expect![[r#" de Clone macro Clone de Clone, Copy + de Debug macro Debug de Default macro Default de Eq de Eq, PartialOrd, Ord @@ -921,6 +923,7 @@ mod derive { expect![[r#" de Clone macro Clone de Clone, Copy + de Debug macro Debug de Default macro Default de Eq de Eq, PartialOrd, Ord @@ -942,6 +945,7 @@ mod derive { expect![[r#" de Clone macro Clone de Clone, Copy + de Debug macro Debug de Default macro Default de PartialOrd de PartialOrd, Ord diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 994150b1ac4c2..8e687385086fc 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -106,6 +106,18 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:AsRef") } + pub fn core_convert_AsMut(&self) -> Option { + self.find_trait("core:convert:AsMut") + } + + pub fn core_borrow_Borrow(&self) -> Option { + self.find_trait("core:borrow:Borrow") + } + + pub fn core_borrow_BorrowMut(&self) -> Option { + self.find_trait("core:borrow:BorrowMut") + } + pub fn core_ops_ControlFlow(&self) -> Option { self.find_enum("core:ops:ControlFlow") } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs index 5356614dce52a..e6618573e091e 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs @@ -272,5 +272,5 @@ fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol { .display_name .as_deref() .cloned() - .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize)) + .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).index() as usize)) } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 7d460f72492c7..4efb83ba3235a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -317,7 +317,7 @@ impl Definition { }; return match def { Some(def) => SearchScope::file_range( - def.as_ref().original_file_range_with_macro_call_body(db), + def.as_ref().original_file_range_with_macro_call_input(db), ), None => SearchScope::single_file(file_id), }; @@ -332,7 +332,7 @@ impl Definition { }; return match def { Some(def) => SearchScope::file_range( - def.as_ref().original_file_range_with_macro_call_body(db), + def.as_ref().original_file_range_with_macro_call_input(db), ), None => SearchScope::single_file(file_id), }; @@ -341,7 +341,7 @@ impl Definition { if let Definition::SelfType(impl_) = self { return match impl_.source(db).map(|src| src.syntax().cloned()) { Some(def) => SearchScope::file_range( - def.as_ref().original_file_range_with_macro_call_body(db), + def.as_ref().original_file_range_with_macro_call_input(db), ), None => SearchScope::single_file(file_id), }; @@ -360,7 +360,7 @@ impl Definition { }; return match def { Some(def) => SearchScope::file_range( - def.as_ref().original_file_range_with_macro_call_body(db), + def.as_ref().original_file_range_with_macro_call_input(db), ), None => SearchScope::single_file(file_id), }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs index 546512a6cf926..c39e00e178f81 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -242,8 +242,8 @@ macro_rules! outer { fn f() { outer!(); -} //^^^^^^^^ error: leftover tokens - //^^^^^^^^ error: Syntax Error in Expansion: expected expression +} //^^^^^^ error: leftover tokens + //^^^^^^ error: Syntax Error in Expansion: expected expression "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index 8a5d82b48c019..7da799e0d490b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -66,7 +66,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option Option> { + let file_id = d.file_id.file_id()?; let add_assoc_item_def = |builder: &mut SourceChangeBuilder| -> Option<()> { let db = ctx.sema.db; let root = db.parse_or_expand(d.file_id); @@ -90,12 +91,14 @@ fn quickfix_for_redundant_assoc_item( let trait_def = d.trait_.source(db)?.value; let l_curly = trait_def.assoc_item_list()?.l_curly_token()?.text_range(); let where_to_insert = - hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted(db).range; + hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted_opt(db)?; + if where_to_insert.file_id != file_id { + return None; + } - builder.insert(where_to_insert.end(), redundant_item_def); + builder.insert(where_to_insert.range.end(), redundant_item_def); Some(()) }; - let file_id = d.file_id.file_id()?; let mut source_change_builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db)); add_assoc_item_def(&mut source_change_builder)?; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs index 1f2d671249de9..dcca85d4db33e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs @@ -120,8 +120,7 @@ fn assoc_func_fix( let call = ast::MethodCallExpr::cast(expr.syntax().clone())?; let range = InFile::new(expr_ptr.file_id, call.syntax().text_range()) - .original_node_file_range_rooted(db) - .range; + .original_node_file_range_rooted_opt(db)?; let receiver = call.receiver()?; let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original; @@ -174,18 +173,16 @@ fn assoc_func_fix( let assoc_func_call_expr_string = make::expr_call(assoc_func_path, args).to_string(); - let file_id = ctx.sema.original_range_opt(call.receiver()?.syntax())?.file_id; - Some(Assist { id: AssistId::quick_fix("method_call_to_assoc_func_call_fix"), label: Label::new(format!( "Use associated func call instead: `{assoc_func_call_expr_string}`" )), group: None, - target: range, + target: range.range, source_change: Some(SourceChange::from_text_edit( - file_id.file_id(ctx.sema.db), - TextEdit::replace(range, assoc_func_call_expr_string), + range.file_id.file_id(ctx.sema.db), + TextEdit::replace(range.range, assoc_func_call_expr_string), )), command: None, }) @@ -300,7 +297,7 @@ macro_rules! m { } fn main() { m!(()); - // ^^^^^^ error: no method `foo` on type `()` + // ^^ error: no method `foo` on type `()` } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs index 4b8d07a253375..7a0405939d10c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs +++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs @@ -592,7 +592,7 @@ macro_rules! call { "#, expect!["callee Function FileId(0) 22..37 30..36"], expect![[r#" - caller Function FileId(0) 38..52 : FileId(0):44..50 + caller Function FileId(0) 38..43 : FileId(0):44..50 caller Function FileId(1) 130..136 130..136 : FileId(0):44..50 callee Function FileId(0) 38..52 44..50 : FileId(0):44..50"#]], expect![[]], diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 2c983287d89c3..f58202a421302 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -60,7 +60,7 @@ pub(crate) fn rewrite_links( let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb)) .into_offset_iter(); - let doc = map_links(doc, |target, title, range| { + let doc = map_links(doc, |target, title, range, link_type| { // This check is imperfect, there's some overlap between valid intra-doc links // and valid URLs so we choose to be too eager to try to resolve what might be // a URL. @@ -78,7 +78,7 @@ pub(crate) fn rewrite_links( .map(|(_, attr_id)| attr_id.is_inner_attr()) .unwrap_or(false); if let Some((target, title)) = - rewrite_intra_doc_link(db, definition, target, title, is_inner_doc) + rewrite_intra_doc_link(db, definition, target, title, is_inner_doc, link_type) { (None, target, title) } else if let Some(target) = rewrite_url_link(db, definition, target) { @@ -417,6 +417,7 @@ fn rewrite_intra_doc_link( target: &str, title: &str, is_inner_doc: bool, + link_type: LinkType, ) -> Option<(String, String)> { let (link, ns) = parse_intra_doc_link(target); @@ -438,7 +439,21 @@ fn rewrite_intra_doc_link( url = url.join(&file).ok()?; url.set_fragment(frag); - Some((url.into(), strip_prefixes_suffixes(title).to_owned())) + // We want to strip the keyword prefix from the title, but only if the target is implicitly the same + // as the title. + let title = match link_type { + LinkType::Email + | LinkType::Autolink + | LinkType::Shortcut + | LinkType::Collapsed + | LinkType::Reference + | LinkType::Inline => title.to_owned(), + LinkType::ShortcutUnknown | LinkType::CollapsedUnknown | LinkType::ReferenceUnknown => { + strip_prefixes_suffixes(title).to_owned() + } + }; + + Some((url.into(), title)) } /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). @@ -470,7 +485,7 @@ fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { /// Rewrites a markdown document, applying 'callback' to each link. fn map_links<'e>( events: impl Iterator, Range)>, - callback: impl Fn(&str, &str, Range) -> (Option, String, String), + callback: impl Fn(&str, &str, Range, LinkType) -> (Option, String, String), ) -> impl Iterator> { let mut in_link = false; // holds the origin link target on start event and the rewritten one on end event @@ -497,7 +512,7 @@ fn map_links<'e>( } Event::Text(s) if in_link => { let (link_type, link_target_s, link_name) = - callback(&end_link_target.take().unwrap(), &s, range); + callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap()); end_link_target = Some(CowStr::Boxed(link_target_s.into())); if !matches!(end_link_type, Some(LinkType::Autolink)) { end_link_type = link_type; @@ -506,7 +521,7 @@ fn map_links<'e>( } Event::Code(s) if in_link => { let (link_type, link_target_s, link_name) = - callback(&end_link_target.take().unwrap(), &s, range); + callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap()); end_link_target = Some(CowStr::Boxed(link_target_s.into())); if !matches!(end_link_type, Some(LinkType::Autolink)) { end_link_type = link_type; diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index fd465f31d4324..29fc68bb50f19 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -1082,7 +1082,7 @@ macro_rules! define_fn { } define_fn!(); -//^^^^^^^^^^^^^ +//^^^^^^^^^^ fn bar() { $0foo(); } @@ -3228,7 +3228,7 @@ mod bar { use crate::m; m!(); - // ^^^^^ + // ^^ fn qux() { Foo$0; @@ -3848,6 +3848,78 @@ fn main() { _ => {}, } } +"#, + ); + } + + #[test] + fn goto_const_from_match_pat_with_tuple_struct() { + check( + r#" +struct Tag(u8); +struct Path {} + +const Path: u8 = 0; + // ^^^^ +fn main() { + match Tag(Path) { + Tag(Path$0) => {} + _ => {} + } +} + +"#, + ); + } + + #[test] + fn goto_const_from_match_pat() { + check( + r#" +type T1 = u8; +const T1: u8 = 0; + // ^^ +fn main() { + let x = 0; + match x { + T1$0 => {} + _ => {} + } +} +"#, + ); + } + + #[test] + fn goto_struct_from_match_pat() { + check( + r#" +struct T1; + // ^^ +fn main() { + let x = 0; + match x { + T1$0 => {} + _ => {} + } +} +"#, + ); + } + + #[test] + fn no_goto_trait_from_match_pat() { + check( + r#" +trait T1 {} +fn main() { + let x = 0; + match x { + T1$0 => {} + // ^^ + _ => {} + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index a281a491525c3..f63499aa0fd4f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10927,3 +10927,34 @@ fn main() { "#]], ); } + +#[test] +fn keyword_inside_link() { + check( + r#" +enum Foo { + MacroExpansion, +} + +/// I return a [macro expansion](Foo::MacroExpansion). +fn bar$0() -> Foo { + Foo::MacroExpansion +} + "#, + expect![[r#" + *bar* + + ```rust + ra_test_fixture + ``` + + ```rust + fn bar() -> Foo + ``` + + --- + + I return a [macro expansion](https://docs.rs/ra_test_fixture/*/ra_test_fixture/enum.Foo.html#variant.MacroExpansion). + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index f2844a2eaa614..49b43fc37f24a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -109,50 +109,90 @@ pub(super) fn hints( } has_adjustments = true; - // FIXME: Add some nicer tooltips to each of these - let (text, coercion) = match kind { + let (text, coercion, detailed_tooltip) = match kind { Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { allow_edit = false; - ("", "never to any") - } - Adjust::Deref(None) => ("*", "dereference"), - Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => { - ("*", "`Deref` dereference") - } - Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => { - ("*", "`DerefMut` dereference") - } - Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"), - Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"), - Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => { - ("&raw const ", "const pointer borrow") - } - Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => { - ("&raw mut ", "mut pointer borrow") + ( + "", + "never to any", + "Coerces the never type `!` into any other type. This happens in code paths that never return, like after `panic!()` or `return`.", + ) } + Adjust::Deref(None) => ( + "*", + "dereference", + "Built-in dereference of a reference to access the underlying value. The compiler inserts `*` to get the value from `&T`.", + ), + Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => ( + "*", + "`Deref` dereference", + "Dereference via the `Deref` trait. Used for types like `Box` or `Rc` so they act like plain `T`.", + ), + Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => ( + "*", + "`DerefMut` dereference", + "Mutable dereference using the `DerefMut` trait. Enables smart pointers to give mutable access to their inner values.", + ), + Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ( + "&", + "shared borrow", + "Inserts `&` to create a shared reference. Lets you use a value without moving or cloning it.", + ), + Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ( + "&mut ", + "mutable borrow", + "Inserts `&mut` to create a unique, mutable reference. Lets you modify a value without taking ownership.", + ), + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => ( + "&raw const ", + "const raw pointer", + "Converts a reference to a raw const pointer `*const T`. Often used when working with FFI or unsafe code.", + ), + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => ( + "&raw mut ", + "mut raw pointer", + "Converts a mutable reference to a raw mutable pointer `*mut T`. Allows mutation in unsafe contexts.", + ), // some of these could be represented via `as` casts, but that's not too nice and // handling everything as a prefix expr makes the `(` and `)` insertion easier Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => { allow_edit = false; match cast { - PointerCast::ReifyFnPointer => { - ("", "fn item to fn pointer") - } + PointerCast::ReifyFnPointer => ( + "", + "fn item to fn pointer", + "Converts a named function to a function pointer `fn()`. Useful when passing functions as values.", + ), PointerCast::UnsafeFnPointer => ( "", "safe fn pointer to unsafe fn pointer", + "Coerces a safe function pointer to an unsafe one. Allows calling it in an unsafe context.", + ), + PointerCast::ClosureFnPointer(Safety::Unsafe) => ( + "", + "closure to unsafe fn pointer", + "Converts a non-capturing closure to an unsafe function pointer. Required for use in `extern` or unsafe APIs.", + ), + PointerCast::ClosureFnPointer(Safety::Safe) => ( + "", + "closure to fn pointer", + "Converts a non-capturing closure to a function pointer. Lets closures behave like plain functions.", + ), + PointerCast::MutToConstPointer => ( + "", + "mut ptr to const ptr", + "Coerces `*mut T` to `*const T`. Safe because const pointers restrict what you can do.", + ), + PointerCast::ArrayToPointer => ( + "", + "array to pointer", + "Converts an array to a pointer to its first element. Similar to how arrays decay to pointers in C.", + ), + PointerCast::Unsize => ( + "", + "unsize coercion", + "Converts a sized type to an unsized one. Used for things like turning arrays into slices or concrete types into trait objects.", ), - PointerCast::ClosureFnPointer(Safety::Unsafe) => { - ("", "closure to unsafe fn pointer") - } - PointerCast::ClosureFnPointer(Safety::Safe) => { - ("", "closure to fn pointer") - } - PointerCast::MutToConstPointer => { - ("", "mut ptr to const ptr") - } - PointerCast::ArrayToPointer => ("", ""), - PointerCast::Unsize => ("", "unsize"), } } _ => continue, @@ -162,9 +202,11 @@ pub(super) fn hints( linked_location: None, tooltip: Some(config.lazy_tooltip(|| { InlayTooltip::Markdown(format!( - "`{}` → `{}` ({coercion} coercion)", + "`{}` → `{}`\n\n**{}**\n\n{}", source.display(sema.db, display_target), target.display(sema.db, display_target), + coercion, + detailed_tooltip )) })), }; diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index d2216e66ccbe6..05253b6794891 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -91,8 +91,6 @@ pub(super) fn hints( match_ast! { match parent { ast::Fn(it) => { - // FIXME: this could include parameters, but `HirDisplay` prints too much info - // and doesn't respect the max length either, so the hints end up way too long (format!("fn {}", it.name()?), it.name().map(name)) }, ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)), diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 4c7c597e684e6..7dc18141bdbc1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -844,7 +844,7 @@ pub(crate) fn orig_range_with_focus_r( // *should* contain the name _ => { let kind = call_kind(); - let range = kind.clone().original_call_range_with_body(db); + let range = kind.clone().original_call_range_with_input(db); //If the focus range is in the attribute/derive body, we // need to point the call site to the entire body, if not, fall back // to the name range of the attribute/derive call diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index f48150b369f2a..9d1a5bae96fbc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -351,7 +351,7 @@ pub(crate) fn runnable_fn( ) .call_site(); - let file_range = fn_source.syntax().original_file_range_with_macro_call_body(sema.db); + let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db); let update_test = UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range); @@ -425,7 +425,7 @@ pub(crate) fn runnable_impl( let impl_source = sema.source(*def)?; let impl_syntax = impl_source.syntax(); - let file_range = impl_syntax.original_file_range_with_macro_call_body(sema.db); + let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db); let update_test = UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range); @@ -1241,10 +1241,10 @@ generate_main!(); [ "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"\", kind: Module })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..310, name: \"foo_test\", kind: Function })", - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"foo_test2\", kind: Function }, true)", - "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..344, name: \"main\", kind: Function })", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"foo_test2\", kind: Function }, true)", + "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..341, name: \"main\", kind: Function })", ] "#]], ); @@ -1272,10 +1272,10 @@ foo!(); "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo0\", kind: Function }, true)", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo1\", kind: Function }, true)", - "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo2\", kind: Function }, true)", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo0\", kind: Function }, true)", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo1\", kind: Function }, true)", + "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo2\", kind: Function }, true)", ] "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs index 7985279679c43..25deffe10eb89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs +++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs @@ -79,7 +79,7 @@ impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> { } fn node_id(&'a self, n: &Crate) -> Id<'a> { - let id = n.as_id().as_u32(); + let id = n.as_id().index(); Id::new(format!("_{id:?}")).unwrap() } diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 52f59679b587b..26ee698af0812 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -11,7 +11,7 @@ use hir_expand::proc_macro::{ }; use ide_db::{ ChangeWithProcMacros, FxHashMap, RootDatabase, - base_db::{CrateGraphBuilder, Env, SourceRoot, SourceRootId}, + base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId}, prime_caches, }; use itertools::Itertools; @@ -69,6 +69,23 @@ pub fn load_workspace( extra_env: &FxHashMap>, load_config: &LoadCargoConfig, ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option)> { + let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); + let mut db = RootDatabase::new(lru_cap); + + let (vfs, proc_macro_server) = load_workspace_into_db(ws, extra_env, load_config, &mut db)?; + + Ok((db, vfs, proc_macro_server)) +} + +// This variant of `load_workspace` allows deferring the loading of rust-analyzer +// into an existing database, which is useful in certain third-party scenarios, +// now that `salsa` supports extending foreign databases (e.g. `RootDatabase`). +pub fn load_workspace_into_db( + ws: ProjectWorkspace, + extra_env: &FxHashMap>, + load_config: &LoadCargoConfig, + db: &mut RootDatabase, +) -> anyhow::Result<(vfs::Vfs, Option)> { let (sender, receiver) = unbounded(); let mut vfs = vfs::Vfs::default(); let mut loader = { @@ -78,23 +95,27 @@ pub fn load_workspace( tracing::debug!(?load_config, "LoadCargoConfig"); let proc_macro_server = match &load_config.with_proc_macro_server { - ProcMacroServerChoice::Sysroot => ws - .find_sysroot_proc_macro_srv() - .and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into)) - .map_err(|e| (e, true)), + ProcMacroServerChoice::Sysroot => ws.find_sysroot_proc_macro_srv().map(|it| { + it.and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into)).map_err( + |e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()), + ) + }), ProcMacroServerChoice::Explicit(path) => { - ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true)) - } - ProcMacroServerChoice::None => { - Err((anyhow::format_err!("proc macro server disabled"), false)) + Some(ProcMacroClient::spawn(path, extra_env).map_err(|e| { + ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()) + })) } + ProcMacroServerChoice::None => Some(Err(ProcMacroLoadingError::Disabled)), }; match &proc_macro_server { - Ok(server) => { - tracing::info!(path=%server.server_path(), "Proc-macro server started") + Some(Ok(server)) => { + tracing::info!(manifest=%ws.manifest_or_root(), path=%server.server_path(), "Proc-macro server started") } - Err((e, _)) => { - tracing::info!(%e, "Failed to start proc-macro server") + Some(Err(e)) => { + tracing::info!(manifest=%ws.manifest_or_root(), %e, "Failed to start proc-macro server") + } + None => { + tracing::info!(manifest=%ws.manifest_or_root(), "No proc-macro server started") } } @@ -111,22 +132,24 @@ pub fn load_workspace( ); let proc_macros = { let proc_macro_server = match &proc_macro_server { - Ok(it) => Ok(it), - Err((e, hard_err)) => Err((e.to_string(), *hard_err)), + Some(Ok(it)) => Ok(it), + Some(Err(e)) => { + Err(ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())) + } + None => Err(ProcMacroLoadingError::ProcMacroSrvError( + "proc-macro-srv is not running, workspace is missing a sysroot".into(), + )), }; proc_macros .into_iter() .map(|(crate_id, path)| { ( crate_id, - path.map_or_else( - |e| Err((e, true)), - |(_, path)| { - proc_macro_server.as_ref().map_err(Clone::clone).and_then( - |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]), - ) - }, - ), + path.map_or_else(Err, |(_, path)| { + proc_macro_server.as_ref().map_err(Clone::clone).and_then( + |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]), + ) + }), ) }) .collect() @@ -139,18 +162,20 @@ pub fn load_workspace( version: 0, }); - let db = load_crate_graph( + load_crate_graph_into_db( crate_graph, proc_macros, project_folders.source_root_config, &mut vfs, &receiver, + db, ); if load_config.prefill_caches { - prime_caches::parallel_prime_caches(&db, 1, &|_| ()); + prime_caches::parallel_prime_caches(db, 1, &|_| ()); } - Ok((db, vfs, proc_macro_server.ok())) + + Ok((vfs, proc_macro_server.and_then(Result::ok))) } #[derive(Default)] @@ -391,11 +416,13 @@ pub fn load_proc_macro( path: &AbsPath, ignored_macros: &[Box], ) -> ProcMacroLoadResult { - let res: Result, String> = (|| { + let res: Result, _> = (|| { let dylib = MacroDylib::new(path.to_path_buf()); - let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; + let vec = server.load_dylib(dylib).map_err(|e| { + ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str()) + })?; if vec.is_empty() { - return Err("proc macro library returned no proc macros".to_owned()); + return Err(ProcMacroLoadingError::NoProcMacros); } Ok(vec .into_iter() @@ -412,20 +439,19 @@ pub fn load_proc_macro( } Err(e) => { tracing::warn!("proc-macro loading for {path} failed: {e}"); - Err((e, true)) + Err(e) } } } -fn load_crate_graph( +fn load_crate_graph_into_db( crate_graph: CrateGraphBuilder, proc_macros: ProcMacrosBuilder, source_root_config: SourceRootConfig, vfs: &mut vfs::Vfs, receiver: &Receiver, -) -> RootDatabase { - let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::().ok()); - let mut db = RootDatabase::new(lru_cap); + db: &mut RootDatabase, +) { let mut analysis_change = ChangeWithProcMacros::default(); db.enable_proc_attr_macros(); @@ -462,7 +488,6 @@ fn load_crate_graph( analysis_change.set_proc_macros(proc_macros); db.apply_change(analysis_change); - db } fn expander_to_proc_macro( diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index bbaa8f4f292e4..499caa622c4d0 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -312,7 +312,9 @@ impl WorkspaceBuildScripts { match message { Message::BuildScriptExecuted(mut message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!("running build-script: {name}")); + progress(format!( + "building compile-time-deps: build script {name} run" + )); let cfgs = { let mut acc = Vec::new(); for cfg in &message.cfgs { @@ -343,7 +345,9 @@ impl WorkspaceBuildScripts { } Message::CompilerArtifact(message) => { with_output_for(&message.package_id.repr, &mut |name, data| { - progress(format!("building proc-macros: {name}")); + progress(format!( + "building compile-time-deps: proc-macro {name} built" + )); if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) { // Skip rmeta file @@ -409,13 +413,6 @@ impl WorkspaceBuildScripts { cmd.arg("--target-dir").arg(target_dir); } - // --all-targets includes tests, benches and examples in addition to the - // default lib and bins. This is an independent concept from the --target - // flag below. - if config.all_targets { - cmd.arg("--all-targets"); - } - if let Some(target) = &config.target { cmd.args(["--target", target]); } @@ -463,14 +460,26 @@ impl WorkspaceBuildScripts { cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.arg("-Zunstable-options"); cmd.arg("--compile-time-deps"); - } else if config.wrap_rustc_in_build_scripts { - // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use - // that to compile only proc macros and build scripts during the initial - // `cargo check`. - // We don't need this if we are using `--compile-time-deps` flag. - let myself = std::env::current_exe()?; - cmd.env("RUSTC_WRAPPER", myself); - cmd.env("RA_RUSTC_WRAPPER", "1"); + // we can pass this unconditionally, because we won't actually build the + // binaries, and as such, this will succeed even on targets without libtest + cmd.arg("--all-targets"); + } else { + // --all-targets includes tests, benches and examples in addition to the + // default lib and bins. This is an independent concept from the --target + // flag below. + if config.all_targets { + cmd.arg("--all-targets"); + } + + if config.wrap_rustc_in_build_scripts { + // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use + // that to compile only proc macros and build scripts during the initial + // `cargo check`. + // We don't need this if we are using `--compile-time-deps` flag. + let myself = std::env::current_exe()?; + cmd.env("RUSTC_WRAPPER", myself); + cmd.env("RA_RUSTC_WRAPPER", "1"); + } } Ok(cmd) } diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 4b34fc0071128..9f19260d30996 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -163,18 +163,18 @@ impl Sysroot { } } - pub fn discover_proc_macro_srv(&self) -> anyhow::Result { - let Some(root) = self.root() else { - return Err(anyhow::format_err!("no sysroot",)); - }; - ["libexec", "lib"] - .into_iter() - .map(|segment| root.join(segment).join("rust-analyzer-proc-macro-srv")) - .find_map(|server_path| probe_for_binary(server_path.into())) - .map(AbsPathBuf::assert) - .ok_or_else(|| { - anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", root) - }) + pub fn discover_proc_macro_srv(&self) -> Option> { + let root = self.root()?; + Some( + ["libexec", "lib"] + .into_iter() + .map(|segment| root.join(segment).join("rust-analyzer-proc-macro-srv")) + .find_map(|server_path| probe_for_binary(server_path.into())) + .map(AbsPathBuf::assert) + .ok_or_else(|| { + anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", root) + }), + ) } fn assemble( @@ -209,6 +209,7 @@ impl Sysroot { pub fn load_workspace( &self, sysroot_source_config: &RustSourceWorkspaceConfig, + no_deps: bool, current_dir: &AbsPath, progress: &dyn Fn(String), ) -> Option { @@ -224,6 +225,7 @@ impl Sysroot { &library_manifest, current_dir, cargo_config, + no_deps, progress, ) { Ok(loaded) => return Some(loaded), @@ -318,6 +320,7 @@ impl Sysroot { library_manifest: &ManifestPath, current_dir: &AbsPath, cargo_config: &CargoMetadataConfig, + no_deps: bool, progress: &dyn Fn(String), ) -> Result { tracing::debug!("Loading library metadata: {library_manifest}"); @@ -333,7 +336,7 @@ impl Sysroot { current_dir, &cargo_config, self, - false, + no_deps, // Make sure we never attempt to write to the sysroot true, progress, diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index 4f11af2d06cf0..f229e9a650d09 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -240,7 +240,7 @@ fn smoke_test_real_sysroot_cargo() { let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo")); std::fs::create_dir_all(&cwd).unwrap(); let loaded_sysroot = - sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &cwd, &|_| ()); + sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &cwd, &|_| ()); if let Some(loaded_sysroot) = loaded_sysroot { sysroot.set_workspace(loaded_sysroot); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 5bc64df535c35..43db84b4fa36e 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -7,8 +7,8 @@ use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread}; use anyhow::Context; use base_db::{ CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, - CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroPaths, - TargetLayoutLoadResult, + CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError, + ProcMacroPaths, TargetLayoutLoadResult, }; use cfg::{CfgAtom, CfgDiff, CfgOptions}; use intern::{Symbol, sym}; @@ -391,6 +391,7 @@ impl ProjectWorkspace { toolchain.clone(), target_dir.clone(), )), + config.no_deps, workspace_dir, progress, ) @@ -499,6 +500,7 @@ impl ProjectWorkspace { if let Some(sysroot_project) = sysroot_project { sysroot.load_workspace( &RustSourceWorkspaceConfig::Json(*sysroot_project), + config.no_deps, project_root, progress, ) @@ -510,6 +512,7 @@ impl ProjectWorkspace { toolchain.clone(), target_dir, )), + config.no_deps, project_root, progress, ) @@ -570,6 +573,7 @@ impl ProjectWorkspace { toolchain.clone(), target_dir.clone(), )), + config.no_deps, dir, &|_| (), ); @@ -744,7 +748,7 @@ impl ProjectWorkspace { } } - pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { + pub fn find_sysroot_proc_macro_srv(&self) -> Option> { self.sysroot.discover_proc_macro_srv() } @@ -1641,11 +1645,11 @@ fn add_target_crate_root( Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => { match proc_macro_dylib_path { Some(path) => Ok((cargo_name.to_owned(), path.clone())), - None if has_errors => Err("failed to build proc-macro".to_owned()), - None => Err("proc-macro crate build data is missing dylib path".to_owned()), + None if has_errors => Err(ProcMacroLoadingError::FailedToBuild), + None => Err(ProcMacroLoadingError::MissingDylibPath), } } - None => Err("build scripts have not been built".to_owned()), + None => Err(ProcMacroLoadingError::NotYetBuilt), }; proc_macros.insert(crate_id, proc_macro); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 740fcd81ea98d..f97bf832442e2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -75,8 +75,12 @@ impl Tester { }; let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); - let loaded_sysroot = - sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &path, &|_| ()); + let loaded_sysroot = sysroot.load_workspace( + &RustSourceWorkspaceConfig::default_cargo(), + false, + &path, + &|_| (), + ); if let Some(loaded_sysroot) = loaded_sysroot { sysroot.set_workspace(loaded_sysroot); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e716d14075221..51d4c29aa74ea 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -1526,7 +1526,7 @@ impl Config { CompletionConfig { enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() - && self.caps.completion_item_edit_resolve(), + && self.caps.has_completion_item_resolve_additionalTextEdits(), enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), enable_auto_iter: *self.completion_autoIter_enable(source_root), enable_auto_await: *self.completion_autoAwait_enable(source_root), @@ -2355,10 +2355,6 @@ impl Config { .and_then(|it| it.version.as_ref()) } - pub fn client_is_helix(&self) -> bool { - self.client_info.as_ref().map(|it| it.name == "helix").unwrap_or_default() - } - pub fn client_is_neovim(&self) -> bool { self.client_info.as_ref().map(|it| it.name == "Neovim").unwrap_or_default() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 0e418240db0c4..91d37bd7c9e12 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -6,6 +6,7 @@ use std::{fmt, io, process::Command, time::Duration}; use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, select_biased, unbounded}; use ide_db::FxHashSet; +use itertools::Itertools; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashMap; use serde::Deserialize as _; @@ -379,7 +380,11 @@ impl FlycheckActor { package_id = msg.package_id.repr, "artifact received" ); - self.report_progress(Progress::DidCheckCrate(msg.target.name)); + self.report_progress(Progress::DidCheckCrate(format!( + "{} ({})", + msg.target.name, + msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)), + ))); let package_id = Arc::new(msg.package_id); if self.diagnostics_cleared_for.insert(package_id.clone()) { tracing::trace!( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index a870232d4a0e1..62a28a1a685d4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -101,7 +101,7 @@ pub(crate) struct GlobalState { pub(crate) last_reported_status: lsp_ext::ServerStatusParams, // proc macros - pub(crate) proc_macro_clients: Arc<[anyhow::Result]>, + pub(crate) proc_macro_clients: Arc<[Option>]>, pub(crate) build_deps_changed: bool, // Flycheck diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index 40d05567fccd1..aea116e647db8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -6,7 +6,7 @@ use std::{ use ide_db::base_db::{ DbPanicContext, - salsa::{self, Cancelled, UnexpectedCycle}, + salsa::{self, Cancelled}, }; use lsp_server::{ExtractError, Response, ResponseError}; use serde::{Serialize, de::DeserializeOwned}; @@ -350,9 +350,6 @@ where if let Some(panic_message) = panic_message { message.push_str(": "); message.push_str(panic_message); - } else if let Some(cycle) = panic.downcast_ref::() { - tracing::error!("{cycle}"); - message.push_str(": unexpected cycle"); } else if let Ok(cancelled) = panic.downcast::() { tracing::error!("Cancellation propagated out of salsa! This is a bug"); return Err(HandlerCancelledError::Inner(*cancelled)); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs index 04e31f37fd2c7..f94e7486ff8f1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs @@ -42,7 +42,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), completion_provider: Some(CompletionOptions { resolve_provider: if config.client_is_neovim() { - config.completion_item_edit_resolve().then_some(true) + config.has_completion_item_resolve_additionalTextEdits().then_some(true) } else { Some(config.caps().completions_resolve_provider()) }, @@ -207,8 +207,8 @@ impl ClientCapabilities { serde_json::from_value(self.0.experimental.as_ref()?.get(index)?.clone()).ok() } - /// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports. - pub fn completion_item_edit_resolve(&self) -> bool { + #[allow(non_snake_case)] + pub fn has_completion_item_resolve_additionalTextEdits(&self) -> bool { (|| { Some( self.0 diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 0c0438c4b8ff1..00cf890510d45 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -783,9 +783,14 @@ impl GlobalState { DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it), }; - let handle = - discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap(); - self.discover_handle = Some(handle); + let handle = discover.spawn( + arg, + &std::env::current_dir() + .expect("Failed to get cwd during project discovery"), + ); + self.discover_handle = Some(handle.unwrap_or_else(|e| { + panic!("Failed to spawn project discovery command: {e}") + })); } } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 133d5a6cf7af4..e798aa6a8a606 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -18,7 +18,7 @@ use std::{iter, mem}; use hir::{ChangeWithProcMacros, ProcMacrosBuilder, db::DefDatabase}; use ide_db::{ FxHashMap, - base_db::{CrateGraphBuilder, ProcMacroPaths, salsa::Durability}, + base_db::{CrateGraphBuilder, ProcMacroLoadingError, ProcMacroPaths, salsa::Durability}, }; use itertools::Itertools; use load_cargo::{ProjectFolders, load_proc_macro}; @@ -194,8 +194,7 @@ impl GlobalState { format_to!(message, "{e}"); }); - let proc_macro_clients = - self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None)); + let proc_macro_clients = self.proc_macro_clients.iter().chain(iter::repeat(&None)); for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) { if let ProjectWorkspaceKind::Cargo { error: Some(error), .. } @@ -252,7 +251,8 @@ impl GlobalState { message.push_str("\n\n"); } } - _ => (), + // sysroot was explicitly not set so we didn't discover a server + None => {} } } } @@ -419,14 +419,11 @@ impl GlobalState { }; let mut builder = ProcMacrosBuilder::default(); - let proc_macro_clients = proc_macro_clients - .iter() - .map(|res| res.as_ref().map_err(|e| e.to_string())) - .chain(iter::repeat_with(|| Err("proc-macro-srv is not running".into()))); + let proc_macro_clients = proc_macro_clients.iter().chain(iter::repeat(&None)); for (client, paths) in proc_macro_clients.zip(paths) { for (crate_id, res) in paths.iter() { let expansion_res = match client { - Ok(client) => match res { + Some(Ok(client)) => match res { Ok((crate_name, path)) => { progress(format!("loading proc-macros: {path}")); let ignored_proc_macros = ignored_proc_macros @@ -438,9 +435,14 @@ impl GlobalState { load_proc_macro(client, path, ignored_proc_macros) } - Err(e) => Err((e.clone(), true)), + Err(e) => Err(e.clone()), }, - Err(ref e) => Err((e.clone(), true)), + Some(Err(e)) => Err(ProcMacroLoadingError::ProcMacroSrvError( + e.to_string().into_boxed_str(), + )), + None => Err(ProcMacroLoadingError::ProcMacroSrvError( + "proc-macro-srv is not running".into(), + )), }; builder.insert(*crate_id, expansion_res) } @@ -655,7 +657,10 @@ impl GlobalState { self.proc_macro_clients = Arc::from_iter(self.workspaces.iter().map(|ws| { let path = match self.config.proc_macro_srv() { Some(path) => path, - None => ws.find_sysroot_proc_macro_srv()?, + None => match ws.find_sysroot_proc_macro_srv()? { + Ok(path) => path, + Err(e) => return Some(Err(e)), + }, }; let env: FxHashMap<_, _> = match &ws.kind { @@ -682,14 +687,14 @@ impl GlobalState { }; info!("Using proc-macro server at {path}"); - ProcMacroClient::spawn(&path, &env).map_err(|err| { + Some(ProcMacroClient::spawn(&path, &env).map_err(|err| { tracing::error!( "Failed to run proc-macro server from path {path}, error: {err:?}", ); anyhow::format_err!( "Failed to run proc-macro server from path {path}, error: {err:?}", ) - }) + })) })) } @@ -753,14 +758,14 @@ impl GlobalState { change.set_proc_macros( crate_graph .iter() - .map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true)))) + .map(|id| (id, Err(ProcMacroLoadingError::NotYetBuilt))) .collect(), ); } else { change.set_proc_macros( crate_graph .iter() - .map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false)))) + .map(|id| (id, Err(ProcMacroLoadingError::Disabled))) .collect(), ); } diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs index 8e9597119812a..121d2e3324369 100644 --- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs +++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs @@ -107,9 +107,10 @@ impl fmt::Debug for ErasedFileAstId { } #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[repr(u8)] enum ErasedFileAstIdKind { /// This needs to not change because it's depended upon by the proc macro server. - Fixup, + Fixup = 0, // The following are associated with `ErasedHasNameFileAstId`. Enum, Struct, @@ -413,9 +414,9 @@ impl ErasedAstIdNextIndexMap { } macro_rules! register_enum_ast_id { - (impl AstIdNode for $($ident:ident),+ ) => { + (impl $AstIdNode:ident for $($ident:ident),+ ) => { $( - impl AstIdNode for ast::$ident {} + impl $AstIdNode for ast::$ident {} )+ }; } @@ -426,9 +427,9 @@ register_enum_ast_id! { } macro_rules! register_has_name_ast_id { - (impl AstIdNode for $($ident:ident = $name_method:ident),+ ) => { + (impl $AstIdNode:ident for $($ident:ident = $name_method:ident),+ ) => { $( - impl AstIdNode for ast::$ident {} + impl $AstIdNode for ast::$ident {} )+ fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option { @@ -472,9 +473,9 @@ register_has_name_ast_id! { } macro_rules! register_assoc_item_ast_id { - (impl AstIdNode for $($ident:ident = $name_callback:expr),+ ) => { + (impl $AstIdNode:ident for $($ident:ident = $name_callback:expr),+ ) => { $( - impl AstIdNode for ast::$ident {} + impl $AstIdNode for ast::$ident {} )+ fn assoc_item_ast_id( diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 7bb88ac3658cf..aef3fbf051775 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -97,6 +97,7 @@ const _: () = { const LOCATION: salsa::plumbing::Location = salsa::plumbing::Location { file: file!(), line: line!() }; const DEBUG_NAME: &'static str = "SyntaxContextData"; + const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX; type Fields<'a> = SyntaxContextData; type Struct<'a> = SyntaxContext; } @@ -108,7 +109,9 @@ const _: () = { static CACHE: zalsa_::IngredientCache> = zalsa_::IngredientCache::new(); CACHE.get_or_create(db.zalsa(), || { - db.zalsa().add_or_lookup_jar_by_type::>() + db.zalsa() + .lookup_jar_by_type::>() + .get_or_create() }) } } @@ -130,9 +133,12 @@ const _: () = { type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; fn lookup_or_create_ingredient_index( - aux: &salsa::plumbing::Zalsa, + zalsa: &salsa::plumbing::Zalsa, ) -> salsa::plumbing::IngredientIndices { - aux.add_or_lookup_jar_by_type::>().into() + zalsa + .lookup_jar_by_type::>() + .get_or_create() + .into() } #[inline] @@ -326,14 +332,14 @@ impl<'db> SyntaxContext { None } else { // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`. - unsafe { Some(salsa::Id::from_u32(self.0)) } + unsafe { Some(salsa::Id::from_index(self.0)) } } } #[inline] fn from_salsa_id(id: salsa::Id) -> Self { // SAFETY: This comes from a Salsa ID. - unsafe { Self::from_u32(id.as_u32()) } + unsafe { Self::from_u32(id.index()) } } #[inline] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 955aadaa25d3a..309332873cb80 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -842,9 +842,10 @@ pub fn ref_pat(pat: ast::Pat) -> ast::RefPat { } pub fn match_arm(pat: ast::Pat, guard: Option, expr: ast::Expr) -> ast::MatchArm { + let comma_str = if expr.is_block_like() { "" } else { "," }; return match guard { - Some(guard) => from_text(&format!("{pat} {guard} => {expr}")), - None => from_text(&format!("{pat} => {expr}")), + Some(guard) => from_text(&format!("{pat} {guard} => {expr}{comma_str}")), + None => from_text(&format!("{pat} => {expr}{comma_str}")), }; fn from_text(text: &str) -> ast::MatchArm { @@ -877,7 +878,7 @@ pub fn match_arm_list(arms: impl IntoIterator) -> ast::Mat let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| { let needs_comma = arm.comma_token().is_none() && arm.expr().is_none_or(|it| !it.is_block_like()); - let comma = if needs_comma { "," } else { "" }; + let comma = if needs_comma && arm.comma_token().is_none() { "," } else { "" }; let arm = arm.syntax(); format_to_acc!(acc, " {arm}{comma}\n") }); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs index 429e51ba3621f..17cc5f9c05706 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs @@ -1212,6 +1212,43 @@ impl SyntaxFactory { ast } + pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr { + let ast = make::attr_outer(meta.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn attr_inner(&self, meta: ast::Meta) -> ast::Attr { + let ast = make::attr_inner(meta.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + + pub fn meta_token_tree(&self, path: ast::Path, tt: ast::TokenTree) -> ast::Meta { + let ast = make::meta_token_tree(path.clone(), tt.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone()); + builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + + ast + } + pub fn token_tree( &self, delimiter: SyntaxKind, @@ -1242,6 +1279,10 @@ impl SyntaxFactory { pub fn whitespace(&self, text: &str) -> SyntaxToken { make::tokens::whitespace(text) } + + pub fn ident(&self, text: &str) -> SyntaxToken { + make::tokens::ident(text) + } } // `ext` constructors diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 31caf618be903..3fa584850f77d 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -435,7 +435,7 @@ mod tests { _ => { let var_name = 2 + 2; (var_name, true) - }"#]]; + },"#]]; expect.assert_eq(&edit.new_root.to_string()); assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs index 1d821e96e550b..e830c6a7cf688 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs @@ -435,14 +435,16 @@ impl MiniCore { continue; } - let mut active_line_region = false; - let mut inactive_line_region = false; + let mut active_line_region = 0; + let mut inactive_line_region = 0; if let Some(idx) = trimmed.find("// :!") { - inactive_line_region = true; - inactive_regions.push(&trimmed[idx + "// :!".len()..]); + let regions = trimmed[idx + "// :!".len()..].split(", "); + inactive_line_region += regions.clone().count(); + inactive_regions.extend(regions); } else if let Some(idx) = trimmed.find("// :") { - active_line_region = true; - active_regions.push(&trimmed[idx + "// :".len()..]); + let regions = trimmed[idx + "// :".len()..].split(", "); + active_line_region += regions.clone().count(); + active_regions.extend(regions); } let mut keep = true; @@ -462,11 +464,11 @@ impl MiniCore { if keep { buf.push_str(line); } - if active_line_region { - active_regions.pop().unwrap(); + if active_line_region > 0 { + active_regions.drain(active_regions.len() - active_line_region..); } - if inactive_line_region { - inactive_regions.pop().unwrap(); + if inactive_line_region > 0 { + inactive_regions.drain(inactive_regions.len() - active_line_region..); } } diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index d48063fb86f0f..dc1eba1a1abf8 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -11,10 +11,13 @@ //! add: //! asm: //! assert: +//! as_mut: sized //! as_ref: sized //! async_fn: fn, tuple, future, copy //! bool_impl: option, fn //! builtin_impls: +//! borrow: sized +//! borrow_mut: borrow //! cell: copy, drop //! clone: sized //! coerce_pointee: derive, sized, unsize, coerce_unsized, dispatch_from_dyn @@ -228,8 +231,11 @@ pub mod hash { } // region:derive - #[rustc_builtin_macro] - pub macro Hash($item:item) {} + pub(crate) mod derive { + #[rustc_builtin_macro] + pub macro Hash($item:item) {} + } + pub use derive::Hash; // endregion:derive } // endregion:hash @@ -377,11 +383,30 @@ pub mod convert { fn as_ref(&self) -> &T; } // endregion:as_ref + // region:as_mut + pub trait AsMut: crate::marker::PointeeSized { + fn as_mut(&mut self) -> &mut T; + } + // endregion:as_mut // region:infallible pub enum Infallible {} // endregion:infallible } +pub mod borrow { + // region:borrow + pub trait Borrow { + fn borrow(&self) -> &Borrowed; + } + // endregion:borrow + + // region:borrow_mut + pub trait BorrowMut: Borrow { + fn borrow_mut(&mut self) -> &mut Borrowed; + } + // endregion:borrow_mut +} + pub mod mem { // region:manually_drop use crate::marker::PointeeSized; @@ -1264,8 +1289,11 @@ pub mod fmt { } // region:derive - #[rustc_builtin_macro] - pub macro Debug($item:item) {} + pub(crate) mod derive { + #[rustc_builtin_macro] + pub macro Debug($item:item) {} + } + pub use derive::Debug; // endregion:derive // region:builtin_impls @@ -1931,6 +1959,8 @@ pub mod prelude { panic, // :panic result::Result::{self, Err, Ok}, // :result str::FromStr, // :str + fmt::derive::Debug, // :fmt, derive + hash::derive::Hash, // :hash, derive }; } diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index 0418c00174bdb..3246156f1cb76 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -211,6 +211,7 @@ impl<'a, S: Copy> TtIter<'a, S> { } } +#[derive(Clone)] pub enum TtElement<'a, S> { Leaf(&'a Leaf), Subtree(&'a Subtree, TtIter<'a, S>), diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs index 1228e2e1774db..0c41ede5b53aa 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs @@ -5,8 +5,8 @@ use std::fmt; use fst::{IntoStreamer, Streamer}; -use nohash_hasher::IntMap; -use rustc_hash::FxHashMap; +use indexmap::IndexMap; +use rustc_hash::{FxBuildHasher, FxHashMap}; use crate::{AnchoredPath, FileId, Vfs, VfsPath}; @@ -14,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath}; #[derive(Default, Clone, Eq, PartialEq)] pub struct FileSet { files: FxHashMap, - paths: IntMap, + paths: IndexMap, } impl FileSet { diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md index 1ada1cb24c2ce..8c06f33a9f7bf 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md @@ -694,24 +694,6 @@ interface CancelFlycheckParams {} Cancels all running flycheck processes. -## Syntax Tree - -**Method:** `rust-analyzer/syntaxTree` - -**Request:** - -```typescript -interface SyntaxTreeParams { - textDocument: TextDocumentIdentifier, - range?: Range, -} -``` - -**Response:** `string` - -Returns textual representation of a parse tree for the file/selected region. -Primarily for debugging, but very useful for all people working on rust-analyzer itself. - ## View Syntax Tree **Method:** `rust-analyzer/viewSyntaxTree`