From 4a7ec72961662ebfef0d315d66b0e9006219cea2 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Sat, 18 Jan 2025 17:38:24 +0100 Subject: [PATCH 01/77] Make niches into nices --- .../crates/ide/src/hover/render.rs | 96 ++++++++++++++++++- .../crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 96 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 46242b75dd0b..7fe344cfa426 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1082,7 +1082,19 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { - format_to!(label, "niches = {niches}, "); + if niches > 1024 { + if is_pwr2(niches) { + format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); + } else if is_pwr2plus1(niches) { + format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); + } else if is_pwr2minus1(niches) { + format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); + } else { + format_to!(label, "niches = really rather quite large, "); + } + } else { + format_to!(label, "niches = {niches}, "); + } } } label.pop(); // ' ' @@ -1210,3 +1222,85 @@ fn render_dyn_compatibility( } } } + +fn is_pwr2(val: u128) -> bool { + val.count_ones() == 1 +} + +fn is_pwr2minus1(val: u128) -> bool { + val == u128::MAX || (val + 1).count_ones() == 1 +} + +fn is_pwr2plus1(val: u128) -> bool { + val != 0 && (val - 1).count_ones() == 1 +} + +fn pwr2_to_exponent(num: u128) -> String { + const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; + (127 - num.leading_zeros()) + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as usize) + .map(|idx| DIGITS[idx]) + .collect::() +} + +#[cfg(test)] +mod tests { + use super::*; + + const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; + + #[test] + fn test_is_pwr2() { + const OUTCOMES: [bool; 10] = + [false, true, true, false, true, false, true, false, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2(*test); + assert_eq!(actual, expected, "is_pwr2({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2minus1() { + const OUTCOMES: [bool; 10] = + [true, true, false, true, false, true, false, false, false, true]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2minus1(*test); + assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2plus1() { + const OUTCOMES: [bool; 10] = + [false, false, true, true, false, false, false, true, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2plus1(*test); + assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_pwr2_to_exponent() { + const TESTERS: [u128; 9] = [ + 1, + 2, + 4, + 8, + 16, + 9223372036854775808, + 18446744073709551616, + 36893488147419103232, + 170141183460469231731687303715884105728, + ]; + const OUTCOMES: [&str; 9] = ["⁰", "¹", "²", "³", "⁴", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = pwr2_to_exponent(*test); + assert_eq!( + actual, expected, + "pwr2_to_exponent({test}) returned {actual}, expected {expected}", + ); + } + } +} 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 014b751f95b0..63eb772fde23 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1357,7 +1357,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = 4294967288 + size = 12 (0xC), align = 4, niches = really rather quite large "#]], ); } From 8b25ab0c5a7e48cc3fd10d39a56d382a04528958 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 19 Jan 2025 12:47:19 +0200 Subject: [PATCH 02/77] Fix missing upmapping in trait impls completion --- .../src/completions/item_list/trait_impl.rs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 6d1945c45341..246b12526698 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name( acc, ctx, ImplCompletionKind::All, - match name_ref { - Some(name) => name.syntax().text_range(), + match name_ref + .as_ref() + .and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax())) + { + Some(name) => name.text_range(), None => ctx.source_range(), }, impl_, @@ -516,7 +519,7 @@ fn function_declaration( mod tests { use expect_test::expect; - use crate::tests::{check_edit, check_no_kw}; + use crate::tests::{check, check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { @@ -1639,4 +1642,31 @@ impl DesugaredAsyncTrait for () { "#, ); } + + #[test] + fn within_attr_macro() { + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + f$0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + md proc_macros + kw crate:: + kw self:: + "#]], + ); + } } From 48d17b4181e3ddffad73d5af1377af66b2ddf4f3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:23:19 +0100 Subject: [PATCH 03/77] Move dual blanket impl logic from source analyzer to goto_def --- .../rust-analyzer/crates/hir-def/src/lib.rs | 1 + .../crates/hir-expand/src/name.rs | 12 +++ src/tools/rust-analyzer/crates/hir/src/lib.rs | 9 +++ .../rust-analyzer/crates/hir/src/semantics.rs | 16 +++- .../crates/hir/src/source_analyzer.rs | 74 ------------------- .../crates/ide-db/src/famous_defs.rs | 12 +++ .../crates/ide/src/goto_definition.rs | 65 +++++++++------- .../crates/intern/src/symbol/symbols.rs | 6 ++ .../crates/test-utils/src/minicore.rs | 10 --- 9 files changed, 92 insertions(+), 113 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 84c105a0a346..da9ffae8aab0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -910,6 +910,7 @@ pub enum AssocItemId { ConstId(ConstId), TypeAliasId(TypeAliasId), } + // FIXME: not every function, ... is actually an assoc item. maybe we should make // sure that you can only turn actual assoc items into AssocItemIds. This would // require not implementing From, and instead having some checked way of 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 cc53d2e34aac..9cfa3b4b32b0 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -51,12 +51,24 @@ impl PartialEq for Name { } } +impl PartialEq<&Symbol> for Name { + fn eq(&self, &sym: &&Symbol) -> bool { + self.symbol == *sym + } +} + impl PartialEq for Symbol { fn eq(&self, name: &Name) -> bool { *self == name.symbol } } +impl PartialEq for &Symbol { + fn eq(&self, name: &Name) -> bool { + **self == name.symbol + } +} + /// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct UnescapedName<'a>(&'a Name); diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index db3121d3cd35..220baeb15209 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -2756,6 +2756,15 @@ impl Trait { traits.iter().map(|tr| Trait::from(*tr)).collect() } + pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { + db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then( + |&(_, it)| match it { + AssocItemId::FunctionId(id) => Some(Function { id }), + _ => None, + }, + ) + } + pub fn items(self, db: &dyn HirDatabase) -> Vec { db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 523bc6f10aab..0b76f2e1121b 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } - pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call) + /// Env is used to derive the trait environment + // FIXME: better api for the trait environment + pub fn resolve_impl_method( + &self, + env: Type, + trait_: Trait, + func: Function, + subst: impl IntoIterator, + ) -> Option { + let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); + for s in subst { + substs = substs.push(s.ty); + } + Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { 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 6b78d7a3631f..b699ccde4128 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -322,68 +322,6 @@ impl SourceAnalyzer { } } - // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str. - pub(crate) fn resolve_known_blanket_dual_impls( - &self, - db: &dyn HirDatabase, - call: &ast::MethodCallExpr, - ) -> Option { - // e.g. if the method call is let b = a.into(), - // - receiver_type is A (type of a) - // - return_type is B (type of b) - // We will find the definition of B::from(a: A). - let callable = self.resolve_method_call_as_callable(db, call)?; - let (_, receiver_type) = callable.receiver_param(db)?; - let return_type = callable.return_type(); - let (search_method, substs) = match call.name_ref()?.text().as_str() { - "into" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?; - ( - self.trait_fn(db, trait_, "from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.ty) - .push(receiver_type.ty) - .build(), - ) - } - "try_into" => { - let trait_ = self - .resolver - .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?; - ( - self.trait_fn(db, trait_, "try_from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - // If the method is try_into() or parse(), return_type is Result. - // Get T from type arguments of Result. - .push(return_type.type_arguments().next()?.ty) - .push(receiver_type.ty) - .build(), - ) - } - "parse" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?; - ( - self.trait_fn(db, trait_, "from_str")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.type_arguments().next()?.ty) - .build(), - ) - } - _ => return None, - }; - - let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs); - // If found_method == search_method, the method in trait itself is resolved. - // It means the blanket dual impl is not found. - if found_method == search_method { - None - } else { - Some(found_method.into()) - } - } - pub(crate) fn resolve_expr_as_callable( &self, db: &dyn HirDatabase, @@ -1309,18 +1247,6 @@ impl SourceAnalyzer { Some((trait_id, fn_id)) } - fn trait_fn( - &self, - db: &dyn HirDatabase, - trait_id: TraitId, - method_name: &str, - ) -> Option { - db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item { - AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t), - _ => None, - }) - } - fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) } 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 9e3506d6f53b..1dc61e3f0a8d 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 @@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:cmp:Ord") } + pub fn core_convert_FromStr(&self) -> Option { + self.find_trait("core:str:FromStr") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } @@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_TryFrom(&self) -> Option { + self.find_trait("core:convert:TryFrom") + } + + pub fn core_convert_TryInto(&self) -> Option { + self.find_trait("core:convert:TryInto") + } + pub fn core_convert_Index(&self) -> Option { self.find_trait("core:ops:Index") } 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 f804cc367727..2c6c6e6e6c14 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -5,10 +5,14 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{ + sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, + ModuleDef, Semantics, +}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, + famous_defs::FamousDefs, helpers::pick_best_token, RootDatabase, SymbolKind, }; @@ -129,15 +133,45 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } -// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr. +// If the token is into(), try_into(), search the definition of From, TryFrom. fn find_definition_for_known_blanket_dual_impls( sema: &Semantics<'_, RootDatabase>, original_token: &SyntaxToken, ) -> Option> { let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; - let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?; + let callable = sema.resolve_method_call_as_callable(&method_call)?; + let CallableKind::Function(f) = callable.kind() else { return None }; + let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?; - let def = Definition::from(target_method); + let return_type = callable.return_type(); + let fd = FamousDefs(sema, return_type.krate(sema.db)); + let fn_name = f.name(sema.db); + let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { + let dual = fd.core_convert_From()?; + let dual_f = dual.function(sema.db, &sym::from)?; + sema.resolve_impl_method( + return_type.clone(), + dual, + dual_f, + [return_type, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { + let dual = fd.core_convert_TryFrom()?; + let dual_f = dual.function(sema.db, &sym::try_from)?; + sema.resolve_impl_method( + return_type.clone(), + dual, + dual_f, + // Extract the `T` from `Result` + [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], + )? + } else { + return None; + }; + // Assert that we got a trait impl function, if we are back in a trait definition we didn't + // succeed + let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; + let def = Definition::from(f); Some(def_to_nav(sema.db, def)) } @@ -3157,29 +3191,6 @@ impl TryInto for A { fn f() { let a = A; let b: Result = a.try_into$0(); -} - "#, - ); - } - - #[test] - fn parse_call_to_from_str_definition() { - check( - r#" -//- minicore: from, str -struct A; - -impl FromStr for A { - type Error = String; - - fn from_str(value: &str) -> Result { - //^^^^^^^^ - Ok(A) - } -} - -fn f() { - let a: Result = "aaaaaa".parse$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b3b46421b50f..e7c3668c0fc9 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -240,6 +240,7 @@ define_symbols! { format_unsafe_arg, format, freeze, + from, From, FromStr, from_output, @@ -273,6 +274,8 @@ define_symbols! { index_mut, index, Index, + into, + Into, into_future, into_iter, IntoFuture, @@ -361,6 +364,7 @@ define_symbols! { panic_nounwind, panic, Param, + parse, partial_ord, PartialEq, PartialOrd, @@ -459,8 +463,10 @@ define_symbols! { transmute_opts, transmute_trait, transparent, + try_into, Try, TryFrom, + try_from, tuple_trait, u128, u16, 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 fd06736a2524..4a44eaf5d111 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1574,15 +1574,6 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } - pub trait FromStr: Sized { - type Err; - fn from_str(s: &str) -> Result; - } - impl str { - pub fn parse(&self) -> Result { - FromStr::from_str(self) - } - } } // endregion:str @@ -1857,7 +1848,6 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result - str::FromStr, // :str }; } From 4bc683dfd2934ed7e9ae055e393f06ecf9115b19 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:32:17 +0100 Subject: [PATCH 04/77] Bring back goto def redirect for parse -> FromStr --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 4 ++ .../rust-analyzer/crates/hir/src/semantics.rs | 2 +- .../crates/ide/src/goto_definition.rs | 46 +++++++++++++++++-- .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 10 ++++ 5 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 220baeb15209..4938478bb121 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -4682,6 +4682,10 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool)) } + pub fn is_str(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Str) + } + pub fn is_never(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::Never) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 0b76f2e1121b..708db2c08dda 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1441,7 +1441,7 @@ impl<'db> SemanticsImpl<'db> { /// Env is used to derive the trait environment // FIXME: better api for the trait environment - pub fn resolve_impl_method( + pub fn resolve_trait_impl_method( &self, env: Type, trait_: Trait, 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 2c6c6e6e6c14..99d0d6af716d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -141,15 +141,35 @@ fn find_definition_for_known_blanket_dual_impls( let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; let callable = sema.resolve_method_call_as_callable(&method_call)?; let CallableKind::Function(f) = callable.kind() else { return None }; - let t = f.as_assoc_item(sema.db)?.container_trait(sema.db)?; + let assoc = f.as_assoc_item(sema.db)?; let return_type = callable.return_type(); let fd = FamousDefs(sema, return_type.krate(sema.db)); + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(impl_) + if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse => + { + let t = fd.core_convert_FromStr()?; + let t_f = t.function(sema.db, &sym::from_str)?; + return sema + .resolve_trait_impl_method( + return_type.clone(), + t, + t_f, + [return_type.type_arguments().next()?], + ) + .map(|f| def_to_nav(sema.db, f.into())); + } + hir::AssocItemContainer::Impl(_) => return None, + }; + let fn_name = f.name(sema.db); let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { let dual = fd.core_convert_From()?; let dual_f = dual.function(sema.db, &sym::from)?; - sema.resolve_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -158,7 +178,7 @@ fn find_definition_for_known_blanket_dual_impls( } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { let dual = fd.core_convert_TryFrom()?; let dual_f = dual.function(sema.db, &sym::try_from)?; - sema.resolve_impl_method( + sema.resolve_trait_impl_method( return_type.clone(), dual, dual_f, @@ -3191,6 +3211,26 @@ impl TryInto for A { fn f() { let a = A; let b: Result = a.try_into$0(); +} + "#, + ); + } + + #[test] + fn parse_call_to_from_str_definition() { + check( + r#" +//- minicore: from, str +struct A; +impl FromStr for A { + type Error = String; + fn from_str(value: &str) -> Result { + //^^^^^^^^ + Ok(A) + } +} +fn f() { + let a: Result = "aaaaaa".parse$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index e7c3668c0fc9..59c0c9dca1c4 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -243,6 +243,7 @@ define_symbols! { from, From, FromStr, + from_str, from_output, from_residual, from_usize, 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 4a44eaf5d111..fd06736a2524 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -1574,6 +1574,15 @@ pub mod str { pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { "" } + pub trait FromStr: Sized { + type Err; + fn from_str(s: &str) -> Result; + } + impl str { + pub fn parse(&self) -> Result { + FromStr::from_str(self) + } + } } // endregion:str @@ -1848,6 +1857,7 @@ pub mod prelude { option::Option::{self, None, Some}, // :option panic, // :panic result::Result::{self, Err, Ok}, // :result + str::FromStr, // :str }; } From e9b410d29ce996abc31bd85f8168936f6eeda2aa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Jan 2025 16:42:34 +0100 Subject: [PATCH 05/77] Goto `Display::fmt` when invoked on `to_string` --- .../rust-analyzer/crates/hir/src/semantics.rs | 2 + .../crates/ide-db/src/famous_defs.rs | 7 ++++ .../crates/ide/src/goto_definition.rs | 39 +++++++++++++++++++ .../crates/intern/src/symbol/symbols.rs | 1 + 4 files changed, 49 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 708db2c08dda..09470bed9cfb 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1483,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + // This does not resolve the method call to the correct trait impl! + // We should probably fix that. pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } 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 1dc61e3f0a8d..7039d83a5d18 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 @@ -142,6 +142,13 @@ impl FamousDefs<'_, '_> { self.find_macro("core:unimplemented") } + pub fn core_fmt_Display(&self) -> Option { + self.find_trait("core:fmt:Display") + } + + pub fn alloc_string_ToString(&self) -> Option { + self.find_trait("alloc:string:ToString") + } pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), 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 99d0d6af716d..d18732a6b846 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -185,6 +185,15 @@ fn find_definition_for_known_blanket_dual_impls( // Extract the `T` from `Result` [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], )? + } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) { + let dual = fd.core_fmt_Display()?; + let dual_f = dual.function(sema.db, &sym::fmt)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [callable.receiver_param(sema.db)?.1.strip_reference()], + )? } else { return None; }; @@ -3231,6 +3240,36 @@ impl FromStr for A { } fn f() { let a: Result = "aaaaaa".parse$0(); +} + "#, + ); + } + + #[test] + fn to_string_call_to_display_definition() { + check( + r#" +//- minicore:fmt +//- /alloc.rs crate:alloc +pub mod string { + pub struct String; + pub trait ToString { + fn to_string(&self) -> String; + } + + impl ToString for T { + fn to_string(&self) -> String { String } + } +} +//- /lib.rs crate:lib deps:alloc +use alloc::string::ToString; +struct A; +impl core::fmt::Display for A { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {} + // ^^^ +} +fn f() { + A.to_string$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 59c0c9dca1c4..a29ebad31f03 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -460,6 +460,7 @@ define_symbols! { test_case, test, thiscall, + to_string, trace_macros, transmute_opts, transmute_trait, From 1d5af89654d69024e1bcf3c326b6c53a6878b511 Mon Sep 17 00:00:00 2001 From: Giga Bowser <45986823+Giga-Bowser@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:05:19 -0500 Subject: [PATCH 06/77] fix: Only refresh syntax tree view when the active document changes --- src/tools/rust-analyzer/editors/code/src/ctx.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/editors/code/src/ctx.ts b/src/tools/rust-analyzer/editors/code/src/ctx.ts index 5550bfa6558a..96dc4f19b82c 100644 --- a/src/tools/rust-analyzer/editors/code/src/ctx.ts +++ b/src/tools/rust-analyzer/editors/code/src/ctx.ts @@ -361,7 +361,14 @@ export class Ctx implements RustAnalyzerExtensionApi { } }); - vscode.workspace.onDidChangeTextDocument(async () => { + vscode.workspace.onDidChangeTextDocument(async (e) => { + if ( + vscode.window.activeTextEditor?.document !== e.document || + e.contentChanges.length === 0 + ) { + return; + } + if (this.syntaxTreeView?.visible) { await this.syntaxTreeProvider?.refresh(); } From f98e9715200133d0c7f25997cdd37ab7ea2a7247 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Jan 2025 21:21:42 +0200 Subject: [PATCH 07/77] Fix another bug with completion of trait items inside macros This time, when completing the keyword (e.g. `fn` + whitespace). The bug was actually a double-bug: First, we did not resolve the impl in the macro-expanded file but in the real file, which of course cannot work. Second, in analysis the whitespace was correlated with the `impl` and not the incomplete `fn`, which caused fake (where we insert an identifier after the whitespace) and real expansions to go out of sync, which failed analysis. The fix is to skip whitespaces in analysis. --- .../src/completions/item_list/trait_impl.rs | 32 +++++++++++++++---- .../ide-completion/src/context/analysis.rs | 15 ++++++--- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 246b12526698..18629529b75b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -85,7 +85,7 @@ fn complete_trait_impl_name( name: &Option, kind: ImplCompletionKind, ) -> Option<()> { - let item = match name { + let macro_file_item = match name { Some(name) => name.syntax().parent(), None => { let token = &ctx.token; @@ -96,12 +96,12 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node_rooted(&item)?; + let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?; // item -> ASSOC_ITEM_LIST -> IMPL - let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; + let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?; let replacement_range = { // ctx.sema.original_ast_node(item)?; - let first_child = item + let first_child = real_file_item .children_with_tokens() .find(|child| { !matches!( @@ -109,7 +109,7 @@ fn complete_trait_impl_name( SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR ) }) - .unwrap_or_else(|| SyntaxElement::Node(item.clone())); + .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); TextRange::new(first_child.text_range().start(), ctx.source_range().end()) }; @@ -1658,7 +1658,7 @@ trait Trait { impl Trait for () { f$0 } - "#, + "#, expect![[r#" me fn bar(..) me fn baz(..) @@ -1668,5 +1668,25 @@ impl Trait for () { kw self:: "#]], ); + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + fn $0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + "#]], + ); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3c4d489c0ff8..562f60dbe2bb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use itertools::Either; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, + algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, NameOrNameRef, @@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze( }) } +fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { + let token = file.token_at_offset(offset).left_biased()?; + algo::skip_whitespace_token(token, Direction::Prev) +} + /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. @@ -125,9 +130,7 @@ fn expand( // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) - && original_file - .token_at_offset(original_offset + relative_offset) - .left_biased() + && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. @@ -143,9 +146,11 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( - find_node_at_offset::(&original_file, original_offset), + original_node, find_node_at_offset::( &speculative_file, fake_ident_token.text_range().start(), From 54d1d318d3f89eb66029252139c27e19b5e808a0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 10:05:32 +0100 Subject: [PATCH 08/77] Cleanup `Name` string rendering --- .../crates/hir-def/src/import_map.rs | 15 +-- .../hir-def/src/nameres/mod_resolution.rs | 19 +--- .../crates/hir-expand/src/mod_path.rs | 49 +++------ .../crates/hir-expand/src/name.rs | 100 ++++++------------ .../hir-ty/src/diagnostics/decl_check.rs | 3 +- .../crates/hir-ty/src/infer/closure.rs | 2 +- .../src/handlers/convert_bool_then.rs | 2 +- .../src/handlers/convert_closure_to_fn.rs | 8 +- .../extract_struct_from_enum_variant.rs | 2 +- .../src/handlers/fix_visibility.rs | 2 +- .../src/handlers/move_from_mod_rs.rs | 4 +- .../src/handlers/move_module_to_file.rs | 2 +- .../src/handlers/move_to_mod_rs.rs | 4 +- .../ide-assists/src/handlers/qualify_path.rs | 2 +- .../src/handlers/reorder_fields.rs | 2 +- .../src/handlers/reorder_impl_items.rs | 2 +- .../src/completions/flyimport.rs | 4 +- .../src/completions/item_list/trait_impl.rs | 2 +- .../ide-completion/src/completions/mod_.rs | 6 +- .../ide-completion/src/context/analysis.rs | 4 +- .../crates/ide-completion/src/render.rs | 10 +- .../ide-completion/src/render/const_.rs | 6 +- .../ide-completion/src/render/function.rs | 9 +- .../ide-completion/src/render/literal.rs | 2 +- .../ide-completion/src/render/macro_.rs | 14 ++- .../ide-completion/src/render/pattern.rs | 16 ++- .../ide-completion/src/render/type_alias.rs | 7 +- .../src/render/union_literal.rs | 8 +- .../rust-analyzer/crates/ide-db/src/defs.rs | 2 +- .../crates/ide-db/src/famous_defs.rs | 5 +- .../rust-analyzer/crates/ide-db/src/rename.rs | 9 +- .../rust-analyzer/crates/ide-db/src/search.rs | 10 +- .../crates/ide-db/src/ty_filter.rs | 2 +- .../src/handlers/unlinked_file.rs | 4 +- .../rust-analyzer/crates/ide/src/doc_links.rs | 49 ++++----- .../crates/ide/src/inlay_hints/param_name.rs | 4 +- .../rust-analyzer/crates/ide/src/rename.rs | 2 +- .../crates/intern/src/symbol/symbols.rs | 1 + 38 files changed, 145 insertions(+), 249 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c..a3ffdf770e81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::{format_to, TupleExt}; -use syntax::ToSmolStr; use triomphe::Arc; use crate::{ @@ -88,9 +87,9 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.as_str(), idx as u32)) }) .collect(); importables.sort_by(|(_, l_info, _), (_, r_info, _)| { @@ -441,7 +440,7 @@ pub fn search_dependencies( } fn search_maps( - db: &dyn DefDatabase, + _db: &dyn DefDatabase, import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, @@ -464,11 +463,7 @@ fn search_maps( .then(|| (item, &import_infos[info_idx as usize])) }) .filter(|&(_, info)| { - query.search_mode.check( - &query.query, - query.case_sensitive, - &info.name.unescaped().display(db.upcast()).to_smolstr(), - ) + query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) }); res.extend(iter.map(TupleExt::head)); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index ab4ffbb2c1e4..d7e4ca41cd5d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -4,7 +4,6 @@ use base_db::AnchoredPath; use hir_expand::{name::Name, HirFileIdExt}; use limit::Limit; use span::EditionedFileId; -use syntax::ToSmolStr as _; use crate::{db::DefDatabase, HirFileId}; @@ -35,7 +34,7 @@ impl ModDir { let path = match attr_path { None => { let mut path = self.dir_path.clone(); - path.push(&name.unescaped().display_no_db().to_smolstr()); + path.push(name.as_str()); path } Some(attr_path) => { @@ -66,7 +65,7 @@ impl ModDir { name: &Name, attr_path: Option<&str>, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { - let name = name.unescaped(); + let name = name.as_str(); let mut candidate_files = ArrayVec::<_, 2>::new(); match attr_path { @@ -74,16 +73,8 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None => { - candidate_files.push(format!( - "{}{}.rs", - self.dir_path.0, - name.display(db.upcast()) - )); - candidate_files.push(format!( - "{}{}/mod.rs", - self.dir_path.0, - name.display(db.upcast()) - )); + candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); + candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } }; @@ -97,7 +88,7 @@ impl ModDir { let dir_path = if root_dir_owner { DirPath::empty() } else { - DirPath::new(format!("{}/", name.display(db.upcast()))) + DirPath::new(format!("{}/", name)) }; if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 89eae862bd96..f0cf7ebf479f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -23,15 +23,6 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnescapedModPath<'a>(&'a ModPath); - -impl<'a> UnescapedModPath<'a> { - pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - UnescapedDisplay { db, path: self } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -135,9 +126,11 @@ impl ModPath { _ => None, } } - - pub fn unescaped(&self) -> UnescapedModPath<'_> { - UnescapedModPath(self) + pub fn display_verbatim<'a>( + &'a self, + db: &'a dyn crate::db::ExpandDatabase, + ) -> impl fmt::Display + 'a { + Display { db, path: self, edition: None } } pub fn display<'a>( @@ -145,7 +138,7 @@ impl ModPath { db: &'a dyn crate::db::ExpandDatabase, edition: Edition, ) -> impl fmt::Display + 'a { - Display { db, path: self, edition } + Display { db, path: self, edition: Some(edition) } } } @@ -158,23 +151,12 @@ impl Extend for ModPath { struct Display<'a> { db: &'a dyn ExpandDatabase, path: &'a ModPath, - edition: Edition, + edition: Option, } impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition)) - } -} - -struct UnescapedDisplay<'a> { - db: &'a dyn ExpandDatabase, - path: &'a UnescapedModPath<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path.0, f, Escape::No) + display_fmt_path(self.db, self.path, f, self.edition) } } @@ -184,16 +166,11 @@ impl From for ModPath { } } -enum Escape { - No, - IfNeeded(Edition), -} - fn display_fmt_path( db: &dyn ExpandDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, - escaped: Escape, + edition: Option, ) -> fmt::Result { let mut first_segment = true; let mut add_segment = |s| -> fmt::Result { @@ -221,10 +198,10 @@ fn display_fmt_path( f.write_str("::")?; } first_segment = false; - match escaped { - Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?, - Escape::No => segment.unescaped().display(db).fmt(f)?, - } + match edition { + Some(edition) => segment.display(db, edition).fmt(f)?, + None => fmt::Display::fmt(segment.as_str(), f)?, + }; } Ok(()) } 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 9cfa3b4b32b0..848870c3a384 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -4,8 +4,8 @@ use std::fmt; use intern::{sym, Symbol}; use span::{Edition, SyntaxContextId}; -use syntax::ast; use syntax::utils::is_raw_identifier; +use syntax::{ast, format_smolstr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are @@ -69,27 +69,8 @@ impl PartialEq for &Symbol { } } -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnescapedName<'a>(&'a Name); - -impl<'a> UnescapedName<'a> { - pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - _ = db; - UnescapedDisplay { name: self } - } - #[doc(hidden)] - pub fn display_no_db(self) -> impl fmt::Display + 'a { - UnescapedDisplay { name: self } - } -} - impl Name { - /// Note: this is private to make creating name from random string hard. - /// Hopefully, this should allow us to integrate hygiene cleaner in the - /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { - debug_assert!(!text.starts_with("r#")); Name { symbol: Symbol::intern(text), ctx: () } } @@ -99,12 +80,15 @@ impl Name { // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - Self::new_text(text) + match text.strip_prefix("r#") { + Some(text) => Self::new_text(text), + None => Self::new_text(text), + } } pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. - Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015)) + Self::new(text, SyntaxContextId::root(Edition::Edition2015)) } pub fn new_tuple_field(idx: usize) -> Name { @@ -131,12 +115,22 @@ impl Name { } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Self::new_text(lt.text().as_str().trim_start_matches("r#")) + let text = lt.text(); + match text.strip_prefix("'r#") { + Some(text) => Self::new_text(&format_smolstr!("'{text}")), + None => Self::new_text(text.as_str()), + } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); + _ = ctx; + Self { symbol, ctx: () } + } + + // FIXME: This needs to go once we have hygiene + pub fn new_symbol_root(sym: Symbol) -> Self { + Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015)) } /// A fake name for things missing in the source code. @@ -173,22 +167,19 @@ impl Name { self.symbol.as_str().parse().ok() } + /// Whether this name needs to be escaped in the given edition via `r#`. + pub fn needs_escape(&self, edition: Edition) -> bool { + is_raw_identifier(self.symbol.as_str(), edition) + } + /// Returns the text this name represents if it isn't a tuple field. /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. + // FIXME: This should take a database argument to hide the interning pub fn as_str(&self) -> &str { self.symbol.as_str() } - // FIXME: Remove this - pub fn unescaped(&self) -> UnescapedName<'_> { - UnescapedName(self) - } - - pub fn needs_escape(&self, edition: Edition) -> bool { - is_raw_identifier(self.symbol.as_str(), edition) - } - pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -198,7 +189,7 @@ impl Name { self.display_no_db(edition) } - // FIXME: Remove this + // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) } @@ -207,24 +198,6 @@ impl Name { pub fn symbol(&self) -> &Symbol { &self.symbol } - - pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { - debug_assert!(!symbol.as_str().starts_with("r#")); - _ = ctx; - Self { symbol, ctx: () } - } - - // FIXME: This needs to go once we have hygiene - pub fn new_symbol_root(sym: Symbol) -> Self { - debug_assert!(!sym.as_str().starts_with("r#")); - Self { symbol: sym, ctx: () } - } - - // FIXME: Remove this - #[inline] - pub fn eq_ident(&self, ident: &str) -> bool { - self.as_str() == ident.trim_start_matches("r#") - } } struct Display<'a> { @@ -241,17 +214,6 @@ impl fmt::Display for Display<'_> { } } -struct UnescapedDisplay<'a> { - name: UnescapedName<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let symbol = self.name.0.symbol.as_str(); - fmt::Display::fmt(symbol, f) - } -} - pub trait AsName { fn as_name(&self) -> Name; } @@ -260,14 +222,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::resolve(&self.text()), + None => Name::new_root(&self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::resolve(&self.text()) + Name::new_root(&self.text()) } } @@ -282,7 +244,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(self.sym.as_str()) + Name::new_root(self.sym.as_str()) } } @@ -300,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(&self.name) + Name::new_root(&self.name) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 4991d173b9c4..774991560e9c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> { .filter_map(|(pat_id, pat)| match pat { Pat::Bind { id, .. } => { let bind_name = &body.bindings[*id].name; - let mut suggested_text = - to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?; + let mut suggested_text = to_lower_snake_case(bind_name.as_str())?; if is_raw_identifier(&suggested_text, edition) { suggested_text.insert_str(0, "r#"); } 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 2523aba53833..9283c46d0f61 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 @@ -277,7 +277,7 @@ impl CapturedItem { /// Converts the place to a name that can be inserted into source code. pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string(); + let mut result = body[self.place.local].name.as_str().to_owned(); for proj in &self.place.projections { match proj { ProjectionElem::Deref => {} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a5c5b08d5b0c..eb784cd1226f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if !func.name(ctx.sema.db).eq_ident("then") { + if func.name(ctx.sema.db) != sym::then { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index bb04a43cf961..d34cf895cd90 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -343,11 +343,9 @@ fn compute_closure_type_params( let mut mentioned_names = mentioned_generic_params .iter() .filter_map(|param| match param { - hir::GenericParam::TypeParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) - } + hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()), hir::GenericParam::ConstParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) + Some(param.name(ctx.db()).as_str().to_smolstr()) } hir::GenericParam::LifetimeParam(_) => None, }) @@ -390,7 +388,7 @@ fn compute_closure_type_params( let has_name = syntax .descendants() .filter_map(ast::NameOrNameRef::cast) - .any(|name| mentioned_names.contains(&*name.text())); + .any(|name| mentioned_names.contains(name.text().trim_start_matches("r#"))); let mut has_new_params = false; if has_name { syntax diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 615b5d3f98b5..d4f2ea3bd941 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.eq_ident(variant_name.text().as_str())) + .any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#")) } fn extract_generic_params( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 7a92d8911bf8..47e4a68293f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let (_, def) = module .scope(ctx.db(), None) .into_iter() - .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?; + .find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?; let ScopeDef::ModuleDef(def) = def else { return None; }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 8a7a06b380f5..10915f8aafb8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 9692b7059291..bbf18e21948e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> .string_value_unescape() .is_none() => { - format_to!(buf, "{}/", name.unescaped().display(db)) + format_to!(buf, "{}/", name.as_str()) } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 2925e2334b44..7b38c795dc80 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 849b8a42c694..2a8465f634cf 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -208,7 +208,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.eq_ident(trait_method_name.text().as_str())) + .map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#")) .unwrap_or(false) }) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 972303c2a041..a79a82be4507 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -110,7 +110,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx)) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index eb1d538f8743..c3404173eafe 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -122,7 +122,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, name)| (name.as_str().to_owned(), idx)) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 73313eeaa6b7..435b88de4ae6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxNode}; use crate::{ config::AutoImportExclusionType, @@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key( cov_mark::hit!(certain_fuzzy_order_test); let import_name = match proposed_mod_path.segments().last() { // FIXME: nasty alloc, this is a hot path! - Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(), + Some(name) => name.as_str().to_ascii_lowercase(), None => return usize::MAX, }; match import_name.match_indices(user_input_lowercased).next() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 18629529b75b..5f0288ae9509 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -362,7 +362,7 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr(); + let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); let label = format_smolstr!("type {alias_name} ="); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index bafe32942098..cca6a22f290d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -7,7 +7,7 @@ use ide_db::{ base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; -use syntax::{ast, AstNode, SyntaxKind, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxKind}; use crate::{context::CompletionContext, CompletionItem, Completions}; @@ -140,9 +140,7 @@ fn directory_to_look_for_submodules( module_chain_to_containing_module_file(module, db) .into_iter() .filter_map(|module| module.name(db)) - .try_fold(base_directory, |path, name| { - path.join(&name.unescaped().display_no_db().to_smolstr()) - }) + .try_fold(base_directory, |path, name| path.join(name.as_str())) } fn module_chain_to_containing_module_file( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 562f60dbe2bb..f5a50ae81907 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1595,11 +1595,11 @@ fn pattern_context_for( }).map(|enum_| enum_.variants(sema.db)) }) }).map(|variants| variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string(); + let variant_name = variant.name(sema.db); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { - let pat_already_present = pat.syntax().to_string().contains(&variant_name); + let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str()); pat_already_present.then_some(pat_already_present) }).is_some() }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 61e8114d381a..ec26e311e88f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -130,10 +130,8 @@ pub(crate) fn render_field( let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); let name = field.name(db); - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -142,7 +140,7 @@ pub(crate) fn render_field( ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), - exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), + exact_name_match: compute_exact_name_match(ctx.completion, &name), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -512,7 +510,7 @@ fn render_resolution_simple_( let mut item = CompletionItem::new( kind, ctx.source_range(), - local_name.unescaped().display(db).to_smolstr(), + local_name.as_str().to_smolstr(), ctx.completion.edition, ); item.set_relevance(ctx.completion_relevance()) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 415d87c6239b..e357ab24d22d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display(db, ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr()); let detail = const_.display(db, ctx.completion.edition).to_string(); let mut item = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 3b97d67169ec..317c93b10f82 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -59,13 +59,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())), + format_smolstr!("{}.{}", receiver, name.as_str()), format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)), ), - _ => ( - name.unescaped().display(db).to_smolstr(), - name.display(db, completion.edition).to_smolstr(), - ), + _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()), }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( @@ -151,7 +148,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail) - .lookup_by(name.unescaped().display(db).to_smolstr()); + .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { add_call_parens( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index c71356e5300f..aab54ca5e014 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -75,7 +75,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = ( - qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display_verbatim(ctx.db()).to_string(), qualified_name.display(ctx.db(), completion.edition).to_string(), ); let snippet_cap = ctx.snippet_cap(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 6490171fbb48..e265e92f9794 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -46,21 +46,19 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); - let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; + let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") }; let needs_bang = is_fn_like && !is_use_path && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), source_range, - label(&ctx, needs_bang, bra, ket, &name), + label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); item.set_deprecated(ctx.is_deprecated(macro_)) @@ -71,11 +69,11 @@ fn render( match ctx.snippet_cap() { Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{escaped_name}!{bra}$0{ket}"); - let lookup = banged_name(&name); + let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); } _ if needs_bang => { - item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); + item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name)); } _ => { cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 5675dfb5c6ff..124abb17b6a1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr()); let kind = strukt.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap()); - let lookup = format_literal_lookup(name.as_str(), kind); + let label = format_literal_label(name, kind, ctx.snippet_cap()); + let lookup = format_literal_lookup(name, kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; let db = ctx.db(); @@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat( let (name, escaped_name) = match path { Some(path) => ( - path.unescaped().display(ctx.db()).to_string().into(), - path.display(ctx.db(), ctx.completion.edition).to_string().into(), + path.display_verbatim(ctx.db()).to_smolstr(), + path.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); let it = ( - name.unescaped().display(ctx.db()).to_smolstr(), + name.as_str().to_smolstr(), name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ); it diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 09eb19201c5b..1b952f31360c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -32,14 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( - SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]), + SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]), SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]), ) } else { - ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ) + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()) }; let detail = type_alias.display(db, ctx.completion.edition).to_string(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index e053e299d903..742036265211 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -23,12 +23,12 @@ pub(crate) fn render_union_literal( let (qualified_name, escaped_qualified_name) = match path { Some(p) => ( - p.unescaped().display(ctx.db()).to_string(), - p.display(ctx.db(), ctx.completion.edition).to_string(), + p.display_verbatim(ctx.db()).to_smolstr(), + p.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => ( - name.unescaped().display(ctx.db()).to_string(), - name.display(ctx.db(), ctx.completion.edition).to_string(), + name.as_str().to_smolstr(), + name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), }; let label = format_literal_label( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 49d26dfe25c1..d12bda0816fd 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -794,7 +794,7 @@ impl NameRefClass { hir::AssocItem::TypeAlias(it) => Some(it), _ => None, }) - .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) + .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#")) { // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); 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 1dc61e3f0a8d..9b76baf48729 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 @@ -214,14 +214,15 @@ impl FamousDefs<'_, '_> { for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; - if name.eq_ident(segment) { + if name.as_str() == segment { Some(child) } else { None } })?; } - let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1; + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1; Some(def) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 42efbd68e33d..59914bedde43 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -263,13 +263,12 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => Some(( - format!("../{}", mod_name.unescaped().display(sema.db)), - format!("../{new_name}"), - )), + (true, _, Some(mod_name)) => { + Some((format!("../{}", mod_name.as_str()), format!("../{new_name}"))) + } // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) + Some((mod_name.as_str().to_owned(), new_name.to_owned())) } _ => None, }; 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 a75aba137be6..7fc563a42410 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("collect_possible_aliases").entered(); let db = sema.db; - let container_name = container.name(db).unescaped().display(db).to_smolstr(); + let container_name = container.name(db).as_str().to_smolstr(); let search_scope = Definition::from(container).search_scope(db); let mut seen = FxHashSet::default(); let mut completed = FxHashSet::default(); @@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - // We need to unescape the name in case it is written without "r#" in earlier - // editions of Rust where it isn't a keyword. - self.def - .name(sema.db) - .or_else(self_kw_refs) - .map(|it| it.unescaped().display(sema.db).to_smolstr()) + // We need to search without the `r#`, hence `as_str` access. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr()) } }; let name = match &name { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 515bc418cb46..2fdd8358637d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -26,7 +26,7 @@ impl TryEnum { _ => return None, }; TryEnum::ALL.iter().find_map(|&var| { - if enum_.name(sema.db).eq_ident(var.type_name()) { + if enum_.name(sema.db).as_str() == var.type_name() { return Some(var); } None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 13591dfb2eeb..f3109b9bb73a 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -112,7 +112,7 @@ fn fixes( // shouldn't occur _ => continue 'crates, }; - match current.children.iter().find(|(name, _)| name.eq_ident(seg)) { + match current.children.iter().find(|(name, _)| name.as_str() == seg) { Some((_, &child)) => current = &crate_def_map[child], None => continue 'crates, } @@ -161,7 +161,7 @@ fn fixes( // try finding a parent that has an inline tree from here on let mut current = module; for s in stack.iter().rev() { - match module.children.iter().find(|(name, _)| name.eq_ident(s)) { + match module.children.iter().find(|(name, _)| name.as_str() == s) { Some((_, child)) => { current = &crate_def_map[*child]; } 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 bc9843f3f35a..cfd8919730ad 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db))); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str())); path }) } @@ -590,10 +589,10 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { Adt::Struct(s) => { - format!("struct.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("struct.{}.html", s.name(db).as_str()) } - Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())), - Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()), + Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()), }, Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { @@ -603,48 +602,48 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw) } - None => format!("{}/index.html", name.unescaped().display(db.upcast())), + None => format!("{}/index.html", name.as_str()), } } None => String::from("index.html"), }, Definition::Trait(t) => { - format!("trait.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("trait.{}.html", t.name(db).as_str()) } Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("traitalias.{}.html", t.name(db).as_str()) } Definition::TypeAlias(t) => { - format!("type.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("type.{}.html", t.name(db).as_str()) } Definition::BuiltinType(t) => { - format!("primitive.{}.html", t.name().unescaped().display(db.upcast())) + format!("primitive.{}.html", t.name().as_str()) } Definition::Function(f) => { - format!("fn.{}.html", f.name(db).unescaped().display(db.upcast())) + format!("fn.{}.html", f.name(db).as_str()) } Definition::Variant(ev) => { format!( "enum.{}.html#variant.{}", - ev.parent_enum(db).name(db).unescaped().display(db.upcast()), - ev.name(db).unescaped().display(db.upcast()) + ev.parent_enum(db).name(db).as_str(), + ev.name(db).as_str() ) } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast())) + format!("const.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { - format!("static.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("static.{}.html", s.name(db).as_str()) } Definition::Macro(mac) => match mac.kind(db) { hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("macro.{}.html", mac.name(db).as_str()) } hir::MacroKind::Derive => { - format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("derive.{}.html", mac.name(db).as_str()) } }, Definition::Field(field) => { @@ -654,11 +653,7 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some(( - def, - file, - Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))), - )); + return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str())))); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -667,7 +662,7 @@ fn filename_and_frag_for_def( return Some((adt, file, Some(String::from("impl")))); } Definition::ExternCrateDecl(it) => { - format!("{}/index.html", it.name(db).unescaped().display(db.upcast())) + format!("{}/index.html", it.name(db).as_str()) } Definition::Local(_) | Definition::GenericParam(_) @@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db).unescaped().display(db.upcast())) + format!("tymethod.{}", function.name(db).as_str()) } else { - format!("method.{}", function.name(db).unescaped().display(db.upcast())) + format!("method.{}", function.name(db).as_str()) } } AssocItem::Const(constant) => { - format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast())) + format!("associatedconstant.{}", constant.name(db)?.as_str()) } AssocItem::TypeAlias(ty) => { - format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast())) + format!("associatedtype.{}", ty.name(db).as_str()) } }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index a7b066700c54..a6f7e0c184ac 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -124,9 +124,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => { - Some(it.name(sema.db).unescaped().display_no_db().to_smolstr()) - } + hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index ba739df3092b..07dfd83c4eb7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -263,7 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#")) { Err(format_err!("Renaming aliases is currently unsupported")) } else { diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 59c0c9dca1c4..0f80891404e5 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -459,6 +459,7 @@ define_symbols! { termination, test_case, test, + then, thiscall, trace_macros, transmute_opts, From 398cd2dbf63d67a0f58e1fa730883a7420f8ef70 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 10:33:28 +0100 Subject: [PATCH 09/77] make large niche description more terse, switch to using u128::is_power_of_two --- src/tools/rust-analyzer/crates/ide/src/hover/render.rs | 8 ++++---- src/tools/rust-analyzer/crates/ide/src/hover/tests.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 7fe344cfa426..95be7c5618ef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1090,7 +1090,7 @@ fn render_memory_layout( } else if is_pwr2minus1(niches) { format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); } else { - format_to!(label, "niches = really rather quite large, "); + format_to!(label, "niches = a lot, "); } } else { format_to!(label, "niches = {niches}, "); @@ -1224,15 +1224,15 @@ fn render_dyn_compatibility( } fn is_pwr2(val: u128) -> bool { - val.count_ones() == 1 + val.is_power_of_two() } fn is_pwr2minus1(val: u128) -> bool { - val == u128::MAX || (val + 1).count_ones() == 1 + val == u128::MAX || is_pwr2(val + 1) } fn is_pwr2plus1(val: u128) -> bool { - val != 0 && (val - 1).count_ones() == 1 + val != 0 && is_pwr2(val - 1) } fn pwr2_to_exponent(num: u128) -> String { 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 63eb772fde23..064a845dc576 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -1357,7 +1357,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = really rather quite large + size = 12 (0xC), align = 4, niches = a lot "#]], ); } From feb3fb5f19da41d350b7d5b94ce559dabd0fc5ee Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 20 Jan 2025 20:26:45 +0200 Subject: [PATCH 10/77] Sort completion items that skip `await` and `iter()` behind those that don't I don't think my ranking is perfect, because it places them even behind snippet completions, but this is something. --- .../crates/ide-completion/src/item.rs | 9 +++++++++ .../crates/ide-completion/src/render.rs | 16 ++++++++++++++++ .../crates/ide-completion/src/render/function.rs | 1 + 3 files changed, 26 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index dc2f9a768029..b0a096b64af2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -181,6 +181,8 @@ pub struct CompletionRelevance { pub postfix_match: Option, /// This is set for items that are function (associated or method) pub function: Option, + /// true when there is an `await.method()` or `iter().method()` completion. + pub is_skipping_completion: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -269,6 +271,7 @@ impl CompletionRelevance { postfix_match, trait_, function, + is_skipping_completion, } = self; // only applicable for completions within use items @@ -296,6 +299,12 @@ impl CompletionRelevance { score -= 5; } } + + // Lower rank for completions that skip `await` and `iter()`. + if is_skipping_completion { + score -= 7; + } + // lower rank for items that need an import if requires_import { score -= 1; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ec26e311e88f..1b7adf1adb06 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -141,6 +141,7 @@ pub(crate) fn render_field( item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), exact_name_match: compute_exact_name_match(ctx.completion, &name), + is_skipping_completion: receiver.is_some(), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -213,6 +214,10 @@ pub(crate) fn render_tuple_field( ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) .lookup_by(field.to_string()); + item.set_relevance(CompletionRelevance { + is_skipping_completion: receiver.is_some(), + ..ctx.completion_relevance() + }); item.build(ctx.db()) } @@ -1333,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1362,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1451,6 +1458,7 @@ fn foo() { A { the$0 } } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -1509,6 +1517,7 @@ impl S { return_type: Other, }, ), + is_skipping_completion: false, }, }, CompletionItem { @@ -1651,6 +1660,7 @@ fn foo(s: S) { s.$0 } return_type: Other, }, ), + is_skipping_completion: false, }, }, ] @@ -1862,6 +1872,7 @@ fn f() -> i32 { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2622,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@107", }, @@ -2707,6 +2719,7 @@ fn foo() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2764,6 +2777,7 @@ fn main() { return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@92", }, @@ -3138,6 +3152,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, CompletionItem { @@ -3171,6 +3186,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 317c93b10f82..c3354902c3b7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -123,6 +123,7 @@ fn render( exact_name_match: compute_exact_name_match(completion, &call), function, trait_: trait_info, + is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))), ..ctx.completion_relevance() }); From f0f7204892c9faea1160a2a9ba32c7587c926c7e Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 11:15:36 +0100 Subject: [PATCH 11/77] Fix Param::as_local treating closures wrong --- src/tools/rust-analyzer/crates/hir/src/lib.rs | 41 ++++++++++------- .../crates/ide/src/inlay_hints.rs | 2 +- .../ide/src/inlay_hints/generic_param.rs | 30 ++++++++----- .../crates/ide/src/inlay_hints/param_name.rs | 45 +++++++++---------- 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 4938478bb121..0cbc75726bf3 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -2470,20 +2470,31 @@ impl Param { } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { - let parent = match self.func { - Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), - Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, - _ => return None, - }; - let body = db.body(parent); - if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { - Some(Local { parent, binding_id: self_param }) - } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param.is_some() as usize]] - { - Some(Local { parent, binding_id: *id }) - } else { - None + match self.func { + Callee::Def(CallableDefId::FunctionId(it)) => { + let parent = DefWithBodyId::FunctionId(it); + let body = db.body(parent); + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { + Some(Local { parent, binding_id: *id }) + } else { + None + } + } + Callee::Closure(closure, _) => { + let c = db.lookup_intern_closure(closure.into()); + let body = db.body(c.0); + if let Expr::Closure { args, .. } = &body[c.1] { + if let Pat::Bind { id, .. } = &body[args[self.idx]] { + return Some(Local { parent: c.0, binding_id: *id }); + } + } + None + } + _ => None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6d83a747d766..94dd90c4aac0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -209,7 +209,7 @@ fn hints( ) { closing_brace::hints(hints, sema, config, file_id, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); + generic_param::hints(hints, famous_defs, config, any_has_generic_args); } match_ast! { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 037b328d971f..13055757ba6a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -1,17 +1,19 @@ //! Implementation of inlay hints for generic parameters. -use ide_db::{active_parameter::generic_def_for_node, RootDatabase}; +use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs}; use syntax::{ ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, AstNode, }; -use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, +}; -use super::param_name::{is_argument_similar_to_param_name, render_label}; +use super::param_name::is_argument_similar_to_param_name; pub(crate) fn hints( acc: &mut Vec, - sema: &hir::Semantics<'_, RootDatabase>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, node: AnyHasGenericArgs, ) -> Option<()> { @@ -45,12 +47,11 @@ pub(crate) fn hints( return None; } - let name = param.name(sema.db); - let param_name = name.as_str(); + let param_name = param.name(sema.db); let should_hide = { let argument = get_string_representation(&arg)?; - is_argument_similar_to_param_name(&argument, param_name) + is_argument_similar_to_param_name(&argument, param_name.as_str()) }; if should_hide { @@ -64,7 +65,7 @@ pub(crate) fn hints( if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { return None; } - sema.source(it.merge())?.value.syntax().clone() + sema.source(it.merge()).map(|it| it.value.syntax().clone()) } hir::GenericParam::ConstParam(it) => { if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { @@ -72,17 +73,22 @@ pub(crate) fn hints( } let syntax = sema.source(it.merge())?.value.syntax().clone(); let const_param = ast::ConstParam::cast(syntax)?; - const_param.name()?.syntax().clone() + const_param.name().map(|it| it.syntax().clone()) } hir::GenericParam::LifetimeParam(it) => { if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { return None; } - sema.source(it)?.value.syntax().clone() + sema.source(it).map(|it| it.value.syntax().clone()) } }; - let linked_location = sema.original_range_opt(&source_syntax); - let label = render_label(param_name, config, linked_location); + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + linked_location.map(Into::into), + ); Some(InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index a6f7e0c184ac..ac137d97d8ae 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -3,7 +3,6 @@ //! fn max(x: i32, y: i32) -> i32 { x + y } //! _ = max(/*x*/4, /*y*/4); //! ``` -use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; @@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - FamousDefs(sema, _): &FamousDefs<'_, '_>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, _file_id: EditionedFileId, expr: ast::Expr, @@ -37,23 +36,31 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let source = sema.source(p)?; - let (param_name, name_syntax) = match source.value.as_ref() { - Either::Left(pat) => (pat.name()?, pat.name()), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => (it.name()?, it.name()), - _ => return None, - }, - }; - Some((name_syntax, param_name, arg, range)) + let param_name = p.name(sema.db)?; + Some((p, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) + !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg) }) .map(|(param, param_name, _, hir::FileRange { range, .. })| { - let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); + let linked_location = (|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()) + })(); - let label = render_label(¶m_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + linked_location.map(Into::into), + ); InlayHint { range, kind: InlayKind::Parameter, @@ -70,16 +77,6 @@ pub(super) fn hints( Some(()) } -pub(super) fn render_label( - param_name: impl Display, - config: &InlayHintsConfig, - linked_location: Option, -) -> InlayHintLabel { - let colon = if config.render_colons { ":" } else { "" }; - - InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into)) -} - fn get_callable( sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr, From e08f6d45305617b180856ae75969aac6c0db3a80 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 14:45:30 +0100 Subject: [PATCH 12/77] switch from using leading zeros to trailing zeros --- src/tools/rust-analyzer/crates/ide/src/hover/render.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 95be7c5618ef..fb7c2904afc5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1235,9 +1235,12 @@ fn is_pwr2plus1(val: u128) -> bool { val != 0 && is_pwr2(val - 1) } +/// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power +/// of 2, or this function will panic. fn pwr2_to_exponent(num: u128) -> String { const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; - (127 - num.leading_zeros()) + assert_eq!(num.count_ones(), 1); + num.trailing_zeros() .to_string() .chars() .map(|c| c.to_digit(10).unwrap() as usize) From d8553c9c056d8f8f206dc3910e6c6256302b7e35 Mon Sep 17 00:00:00 2001 From: Luuk Wester Date: Tue, 21 Jan 2025 14:47:07 +0100 Subject: [PATCH 13/77] remove is_pwr2 --- .../crates/ide/src/hover/render.rs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index fb7c2904afc5..40f3406b72d3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1083,7 +1083,7 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { if niches > 1024 { - if is_pwr2(niches) { + if niches.is_power_of_two() { format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); } else if is_pwr2plus1(niches) { format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); @@ -1223,16 +1223,12 @@ fn render_dyn_compatibility( } } -fn is_pwr2(val: u128) -> bool { - val.is_power_of_two() -} - fn is_pwr2minus1(val: u128) -> bool { - val == u128::MAX || is_pwr2(val + 1) + val == u128::MAX || (val + 1).is_power_of_two() } fn is_pwr2plus1(val: u128) -> bool { - val != 0 && is_pwr2(val - 1) + val != 0 && (val - 1).is_power_of_two() } /// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power @@ -1254,16 +1250,6 @@ mod tests { const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; - #[test] - fn test_is_pwr2() { - const OUTCOMES: [bool; 10] = - [false, true, true, false, true, false, true, false, false, false]; - for (test, expected) in TESTERS.iter().zip(OUTCOMES) { - let actual = is_pwr2(*test); - assert_eq!(actual, expected, "is_pwr2({test}) gave {actual}, expected {expected}"); - } - } - #[test] fn test_is_pwr2minus1() { const OUTCOMES: [bool; 10] = From 284e0cd74945d05515bd2d257ae814be9bba91d5 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 12:07:50 +0100 Subject: [PATCH 14/77] Make `InlayHint::linked_location` computation lazy --- .../crates/ide/src/inlay_hints.rs | 22 +- .../crates/ide/src/inlay_hints/bounds.rs | 32 +- .../crates/ide/src/inlay_hints/chaining.rs | 301 ++++++++++-------- .../ide/src/inlay_hints/closing_brace.rs | 6 +- .../ide/src/inlay_hints/closure_captures.rs | 17 +- .../ide/src/inlay_hints/generic_param.rs | 53 +-- .../ide/src/inlay_hints/implicit_drop.rs | 29 +- .../crates/ide/src/inlay_hints/param_name.rs | 24 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +- 9 files changed, 285 insertions(+), 204 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 94dd90c4aac0..405cbf607840 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -300,6 +300,7 @@ pub struct InlayHintsConfig { pub closing_brace_hints_min_lines: Option, pub fields_to_resolve: InlayFieldsToResolve, } + impl InlayHintsConfig { fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { if self.fields_to_resolve.resolve_text_edits { @@ -329,6 +330,19 @@ impl InlayHintsConfig { Lazy::Computed(tooltip) } } + + /// This always reports a resolvable location, so only use this when it is very likely for a + /// location link to actually resolve but where computing `finish` would be costly. + fn lazy_location_opt( + &self, + finish: impl FnOnce() -> Option, + ) -> Option> { + if self.fields_to_resolve.resolve_label_location { + Some(Lazy::Lazy) + } else { + finish().map(Lazy::Computed) + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -509,7 +523,7 @@ impl InlayHintLabel { pub fn simple( s: impl Into, tooltip: Option>, - linked_location: Option, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -593,7 +607,7 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. pub tooltip: Option>, @@ -602,7 +616,7 @@ pub struct InlayHintLabelPart { impl std::hash::Hash for InlayHintLabelPart { fn hash(&self, state: &mut H) { self.text.hash(state); - self.linked_location.hash(state); + self.linked_location.is_some().hash(state); self.tooltip.is_some().hash(state); } } @@ -663,7 +677,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take(), + linked_location: self.location.take().map(Lazy::Computed), tooltip: None, }); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index 429ddd31cbd0..e9b728bcaa75 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -22,11 +22,7 @@ pub(super) fn hints( return None; } - let linked_location = - famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| { - let n = it.call_site(); - FileRange { file_id: n.file_id, range: n.focus_or_full_range() } - }); + let sized_trait = famous_defs.core_marker_Sized(); for param in params.type_or_const_params() { match param { @@ -48,7 +44,17 @@ pub(super) fn hints( } hint.parts.push(InlayHintLabelPart { text: "Sized".to_owned(), - linked_location, + linked_location: sized_trait.and_then(|it| { + config.lazy_location_opt(|| { + it.try_to_nav(sema.db).map(|it| { + let n = it.call_site(); + FileRange { + file_id: n.file_id, + range: n.focus_or_full_range(), + } + }) + }) + }), tooltip: None, }); if has_bounds { @@ -134,12 +140,14 @@ fn foo() {} InlayHintLabelPart { text: "Sized", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 135..140, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 135..140, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 7fa7ab1a94d6..477fb5156be6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -81,7 +81,10 @@ mod tests { use crate::{ fixture, - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::{ + tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + Lazy, + }, InlayHintsConfig, }; @@ -99,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(loc) = &mut hint.linked_location { + if let Some(Lazy::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); @@ -134,12 +137,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 63..64, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), ), tooltip: "", }, @@ -151,12 +156,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -213,12 +220,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -230,12 +239,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -276,12 +287,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -293,12 +306,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -340,12 +355,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 23..24, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), ), tooltip: "", }, @@ -353,12 +370,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -371,12 +390,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -384,12 +405,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -435,12 +458,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -448,12 +473,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -467,12 +494,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -480,12 +509,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -499,12 +530,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -512,12 +545,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -531,12 +566,14 @@ fn main() { InlayHintLabelPart { text: "MyIter", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -577,12 +614,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -594,12 +633,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -611,12 +652,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -628,12 +671,14 @@ fn main() { InlayHintLabelPart { text: "self", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 42..46, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 42..46, + }, + ), ), 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 90b8be64a46d..64f1f83d3f5c 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 @@ -11,7 +11,9 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::Lazy, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, +}; pub(super) fn hints( acc: &mut Vec, @@ -141,7 +143,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location), + label: InlayHintLabel::simple(label, None, linked_location.map(Lazy::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 906f2acf0c44..3e91618d08e6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -53,10 +53,6 @@ pub(super) fn hints( let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { let local = capture.local(); - let source = local.primary_source(sema.db); - - // force cache the source file, otherwise sema lookup will potentially panic - _ = sema.parse_or_expand(source.file()); let label = format!( "{}{}", @@ -73,8 +69,17 @@ pub(super) fn hints( } hint.label.append_part(InlayHintLabelPart { text: label, - linked_location: source.name().and_then(|name| { - name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) + linked_location: config.lazy_location_opt(|| { + let source = local.primary_source(sema.db); + + // force cache the source file, otherwise sema lookup will potentially panic + _ = sema.parse_or_expand(source.file()); + source.name().and_then(|name| { + name.syntax() + .original_file_range_opt(sema.db) + .map(TupleExt::head) + .map(Into::into) + }) }), tooltip: None, }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 13055757ba6a..762a4c265518 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -47,6 +47,18 @@ pub(crate) fn hints( return None; } + let allowed = match (param, &arg) { + (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints, + (hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints, + (hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => { + lifetime_hints + } + _ => false, + }; + if !allowed { + return None; + } + let param_name = param.name(sema.db); let should_hide = { @@ -60,34 +72,27 @@ pub(crate) fn hints( let range = sema.original_range_opt(arg.syntax())?.range; - let source_syntax = match param { - hir::GenericParam::TypeParam(it) => { - if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { - return None; - } - sema.source(it.merge()).map(|it| it.value.syntax().clone()) - } - hir::GenericParam::ConstParam(it) => { - if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { - return None; - } - let syntax = sema.source(it.merge())?.value.syntax().clone(); - let const_param = ast::ConstParam::cast(syntax)?; - const_param.name().map(|it| it.syntax().clone()) - } - hir::GenericParam::LifetimeParam(it) => { - if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { - return None; - } - sema.source(it).map(|it| it.value.syntax().clone()) - } - }; - let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); let colon = if config.render_colons { ":" } else { "" }; let label = InlayHintLabel::simple( format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), None, - linked_location.map(Into::into), + config.lazy_location_opt(|| { + let source_syntax = match param { + hir::GenericParam::TypeParam(it) => { + sema.source(it.merge()).map(|it| it.value.syntax().clone()) + } + hir::GenericParam::ConstParam(it) => { + let syntax = sema.source(it.merge())?.value.syntax().clone(); + let const_param = ast::ConstParam::cast(syntax)?; + const_param.name().map(|it| it.syntax().clone()) + } + hir::GenericParam::LifetimeParam(it) => { + sema.source(it).map(|it| it.value.syntax().clone()) + } + }; + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + linked_location.map(Into::into) + }), ); Some(InlayHint { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 1358d3722f89..27c7c3d49818 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -49,7 +49,7 @@ pub(super) fn hints( if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { continue; // Arguably only ADTs have significant drop impls } - let Some(binding) = local_to_binding.get(place.local) else { + let Some(&binding_idx) = local_to_binding.get(place.local) else { continue; // Ignore temporary values }; let range = match terminator.span { @@ -91,25 +91,26 @@ pub(super) fn hints( }, MirSpan::Unknown => continue, }; - let binding_source = source_map - .patterns_for_binding(*binding) - .first() - .and_then(|d| source_map.pat_syntax(*d).ok()) - .and_then(|d| { - Some(FileRange { - file_id: d.file_id.file_id()?.into(), - range: d.value.text_range(), - }) - }); - let binding = &hir.bindings[*binding]; + let binding = &hir.bindings[binding_idx]; let name = binding.name.display_no_db(file_id.edition()).to_smolstr(); if name.starts_with(" pat.name(), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => it.name(), - _ => None, - }, - }?; - sema.original_range_opt(name_syntax.syntax()) - })(); - let colon = if config.render_colons { ":" } else { "" }; let label = InlayHintLabel::simple( format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), None, - linked_location.map(Into::into), + config.lazy_location_opt(|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()).map(Into::into) + }), ); InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index a5516e7f9d4f..4947576f1977 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -684,7 +684,10 @@ fn inlay_hint_label( *something_to_resolve |= part.linked_location.is_some(); None } else { - part.linked_location.map(|range| location(snap, range)).transpose()? + part.linked_location + .and_then(|it| it.computed()) + .map(|range| location(snap, range)) + .transpose()? }; Ok(lsp_types::InlayHintLabelPart { value: part.text, From 802d9d53869031e1c63dca15684381a50ec6370c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 21 Jan 2025 12:32:11 +0100 Subject: [PATCH 15/77] Keep already computed inlay hint properties instead of late resolving them --- .../crates/ide/src/inlay_hints.rs | 48 ++++--- .../crates/ide/src/inlay_hints/chaining.rs | 4 +- .../ide/src/inlay_hints/closing_brace.rs | 4 +- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../crates/rust-analyzer/src/lsp/to_proto.rs | 133 +++++++++--------- 5 files changed, 99 insertions(+), 92 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 405cbf607840..088a11bcb4cc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -302,21 +302,21 @@ pub struct InlayHintsConfig { } impl InlayHintsConfig { - fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { + fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { - Lazy::Lazy + LazyProperty::Lazy } else { let edit = finish(); never!(edit.is_empty(), "inlay hint produced an empty text edit"); - Lazy::Computed(edit) + LazyProperty::Computed(edit) } } - fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy { + fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty { if self.fields_to_resolve.resolve_hint_tooltip && self.fields_to_resolve.resolve_label_tooltip { - Lazy::Lazy + LazyProperty::Lazy } else { let tooltip = finish(); never!( @@ -327,7 +327,7 @@ impl InlayHintsConfig { .is_empty(), "inlay hint produced an empty tooltip" ); - Lazy::Computed(tooltip) + LazyProperty::Computed(tooltip) } } @@ -336,11 +336,11 @@ impl InlayHintsConfig { fn lazy_location_opt( &self, finish: impl FnOnce() -> Option, - ) -> Option> { + ) -> Option> { if self.fields_to_resolve.resolve_label_location { - Some(Lazy::Lazy) + Some(LazyProperty::Lazy) } else { - finish().map(Lazy::Computed) + finish().map(LazyProperty::Computed) } } } @@ -455,7 +455,7 @@ pub struct InlayHint { /// The actual label to show in the inlay hint. pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. - pub text_edit: Option>, + pub text_edit: Option>, /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the /// hint does not support resolving. pub resolve_parent: Option, @@ -463,15 +463,15 @@ pub struct InlayHint { /// A type signaling that a value is either computed, or is available for computation. #[derive(Clone, Debug)] -pub enum Lazy { +pub enum LazyProperty { Computed(T), Lazy, } -impl Lazy { +impl LazyProperty { pub fn computed(self) -> Option { match self { - Lazy::Computed(it) => Some(it), + LazyProperty::Computed(it) => Some(it), _ => None, } } @@ -522,8 +522,8 @@ pub struct InlayHintLabel { impl InlayHintLabel { pub fn simple( s: impl Into, - tooltip: Option>, - linked_location: Option>, + tooltip: Option>, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -607,10 +607,10 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option>, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. - pub tooltip: Option>, + pub tooltip: Option>, } impl std::hash::Hash for InlayHintLabelPart { @@ -624,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart { impl fmt::Debug for InlayHintLabelPart { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f), + Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => { + text.fmt(f) + } Self { text, linked_location, tooltip } => f .debug_struct("InlayHintLabelPart") .field("text", text) @@ -632,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart { .field( "tooltip", &tooltip.as_ref().map_or("", |it| match it { - Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it, - Lazy::Lazy => "", + LazyProperty::Computed( + InlayTooltip::String(it) | InlayTooltip::Markdown(it), + ) => it, + LazyProperty::Lazy => "", }), ) .finish(), @@ -677,7 +681,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take().map(Lazy::Computed), + linked_location: self.location.take().map(LazyProperty::Computed), tooltip: None, }); } @@ -797,7 +801,7 @@ fn ty_to_text_edit( ty: &hir::Type, offset_to_insert: TextSize, prefix: impl Into, -) -> Option> { +) -> Option> { // FIXME: Limit the length and bail out on excess somehow? let rendered = sema .scope(node_for_hint) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 477fb5156be6..8471547727fe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -83,7 +83,7 @@ mod tests { fixture, inlay_hints::{ tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, - Lazy, + LazyProperty, }, InlayHintsConfig, }; @@ -102,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(Lazy::Computed(loc)) = &mut hint.linked_location { + if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); 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 64f1f83d3f5c..bd36e2c3be6f 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 @@ -12,7 +12,7 @@ use syntax::{ }; use crate::{ - inlay_hints::Lazy, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, }; pub(super) fn hints( @@ -143,7 +143,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location.map(Lazy::Computed)), + label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 346e2862b0fd..6ec95ccb62a5 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,7 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, LazyProperty }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 4947576f1977..2a7d95d560d2 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -11,8 +11,8 @@ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, - InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, - NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty, + Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; use ide_db::{assists, rust_doc::format_docs, FxHasher}; @@ -549,12 +549,11 @@ pub(crate) fn inlay_hint( ) -> Cancellable { let hint_needs_resolve = |hint: &InlayHint| -> Option { hint.resolve_parent.filter(|_| { - hint.text_edit.is_some() - || hint - .label - .parts - .iter() - .any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy) + || hint.label.parts.iter().any(|part| { + part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy) + || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) + }) }) }; @@ -569,22 +568,21 @@ pub(crate) fn inlay_hint( }); let mut something_to_resolve = false; - let text_edits = if snap - .config - .visual_studio_code_version() - .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_range_and_hash.is_some() - && fields_to_resolve.resolve_text_edits - { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint - .text_edit - .take() - .and_then(|it| it.computed()) - .map(|it| text_edit_vec(line_index, it)) - }; + let text_edits = inlay_hint + .text_edit + .take() + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + something_to_resolve |= + snap.config.visual_studio_code_version().is_none_or(|version| { + VersionReq::parse(">=1.86.0").unwrap().matches(version) + }) && resolve_range_and_hash.is_some() + && fields_to_resolve.resolve_text_edits; + None + } + }) + .map(|it| text_edit_vec(line_index, it)); let (label, tooltip) = inlay_hint_label( snap, fields_to_resolve, @@ -637,22 +635,23 @@ fn inlay_hint_label( let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - *something_to_resolve |= tooltip.is_some(); - None - } else { - match tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - })) - } - None => None, + let tooltip = tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= + needs_resolve && fields_to_resolve.resolve_hint_tooltip; + None } + }); + let hint_tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)), + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + })) + } + None => None, }; (lsp_types::InlayHintLabel::String(text), hint_tooltip) } @@ -661,34 +660,38 @@ fn inlay_hint_label( .parts .into_iter() .map(|part| { - let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - *something_to_resolve |= part.tooltip.is_some(); - None - } else { - match part.tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, + let tooltip = part.tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_tooltip; + None } + }); + let tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, }; - let location = if needs_resolve && fields_to_resolve.resolve_label_location { - *something_to_resolve |= part.linked_location.is_some(); - None - } else { - part.linked_location - .and_then(|it| it.computed()) - .map(|range| location(snap, range)) - .transpose()? - }; + let location = part + .linked_location + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_location; + None + } + }) + .map(|range| location(snap, range)) + .transpose()?; Ok(lsp_types::InlayHintLabelPart { value: part.text, tooltip, From 5059c1a870bef3b366f6863510b2bff31ba07a0a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 21 Jan 2025 18:51:09 +0200 Subject: [PATCH 16/77] Provide a config to control auto-insertion of `await` and `iter()` --- .../ide-completion/src/completions/dot.rs | 121 ++++++++++-------- .../crates/ide-completion/src/config.rs | 2 + .../crates/ide-completion/src/tests.rs | 2 + .../crates/rust-analyzer/src/config.rs | 6 + .../src/integrated_benchmarks.rs | 6 + .../docs/user/generated_config.adoc | 10 ++ .../rust-analyzer/editors/code/package.json | 20 +++ 7 files changed, 111 insertions(+), 56 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded..0d1765a7b76f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -42,31 +42,38 @@ pub(crate) fn complete_dot( item.detail("expr.await"); item.add_to(acc, ctx.db); - // Completions that skip `.await`, e.g. `.await.foo()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, - }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_fields( - acc, - ctx, - &future_output, - |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty), - |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), - is_field_access, - is_method_access_with_parens, - ); - complete_methods(ctx, &future_output, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) - }); + if ctx.config.enable_auto_await { + // Completions that skip `.await`, e.g. `.await.foo()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { + original: future_output.clone(), + adjusted: None, + }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_fields( + acc, + ctx, + &future_output, + |acc, field, ty| { + acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty) + }, + |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), + is_field_access, + is_method_access_with_parens, + ); + complete_methods(ctx, &future_output, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) + }); + } } complete_fields( @@ -82,39 +89,41 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - // FIXME: - // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute - // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. - // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid - let iter = receiver_ty - .strip_references() - .add_reference(hir::Mutability::Shared) - .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("iter()"))); - // Does ::IntoIter` exist? - let into_iter = || { - receiver_ty - .clone() + if ctx.config.enable_auto_iter { + // FIXME: + // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute + // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. + // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid + let iter = receiver_ty + .strip_references() + .add_reference(hir::Mutability::Shared) .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("into_iter()"))) - }; - if let Some((iter, iter_sym)) = iter.or_else(into_iter) { - // Skip iterators, e.g. complete `.iter().filter_map()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, + .map(|ty| (ty, SmolStr::new_static("iter()"))); + // Does ::IntoIter` exist? + let into_iter = || { + receiver_ty + .clone() + .into_iterator_iter(ctx.db) + .map(|ty| (ty, SmolStr::new_static("into_iter()"))) }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_methods(ctx, &iter, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) - }); + if let Some((iter, iter_sym)) = iter.or_else(into_iter) { + // Skip iterators, e.g. complete `.iter().filter_map()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_methods(ctx, &iter, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) + }); + } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 8b1ce11e8a45..c641df38ff23 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -14,6 +14,8 @@ pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, + pub enable_auto_iter: bool, + pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, pub term_search_fuel: u64, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b7dbf0a6306c..9d91f95eb65b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -87,6 +87,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { 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 3dc4379258fa..44325fa1a29e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -453,6 +453,10 @@ config_data! { /// /// In `match` arms it completes a comma instead. completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + completion_autoAwait_enable: bool = true, + /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + completion_autoIter_enable: bool = true, /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, @@ -1484,6 +1488,8 @@ impl Config { enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), 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), enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), full_function_signatures: self .completion_fullFunctionSignatures_enable(source_root) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index fcfd06679bf2..5cdc51a1c199 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -176,6 +176,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -226,6 +228,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -274,6 +278,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc index bd091db58d3f..b33de1956b8d 100644 --- a/src/tools/rust-analyzer/docs/user/generated_config.adoc +++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc @@ -274,6 +274,16 @@ Whether to automatically add a semicolon when completing unit-returning function In `match` arms it completes a comma instead. -- +[[rust-analyzer.completion.autoAwait.enable]]rust-analyzer.completion.autoAwait.enable (default: `true`):: ++ +-- +Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. +-- +[[rust-analyzer.completion.autoIter.enable]]rust-analyzer.completion.autoIter.enable (default: `true`):: ++ +-- +Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. +-- [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: + -- diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 8b066377f2b2..f148041ac3eb 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1143,6 +1143,26 @@ } } }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.autoAwait.enable": { + "markdownDescription": "Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future.", + "default": true, + "type": "boolean" + } + } + }, + { + "title": "completion", + "properties": { + "rust-analyzer.completion.autoIter.enable": { + "markdownDescription": "Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them.", + "default": true, + "type": "boolean" + } + } + }, { "title": "completion", "properties": { From c5ccf86e90428ce6fb601c27c3f2938099cbd120 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 21 Jan 2025 11:06:05 -0800 Subject: [PATCH 17/77] internal: Treat cfg fetching failures as a warning If the user doesn't have rustc on $PATH, rust-analyzer won't be able to run `rustc --print cfg`. This isn't really an error, as rust-analyzer can still proceed without it. This is particularly noticeable when loading crates defined in a rust-project.json. Until the configuration is loaded, the opened files are briefly treated as detached files and users see this error. Environments with rust-project.json generally have a sysroot and rustc elsewhere, so the error confuses users. --- .../crates/project-model/src/toolchain_info/rustc_cfg.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs index 4bf9b59e7d03..e472da0c89b0 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -19,7 +19,7 @@ pub fn get( let rustc_cfgs = match rustc_cfgs { Ok(cfgs) => cfgs, Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); + tracing::warn!(?e, "failed to get rustc cfgs"); return vec![]; } }; From 98582b23ab4812ac57c5b989055f87e25c751ce9 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Tue, 7 Jan 2025 16:37:49 -0800 Subject: [PATCH 18/77] manual: Document all rust-project.json fields Ensure that all the fields that rust-analyzer understands are in the manual, they all have doc comments, and they use consistent punctuation (`;` rather than mixing `,` and `;`). Whilst we're here, fix the `sysroot_src` example and add 2024 as a legal value for Rust edition. --- .../crates/project-model/src/project_json.rs | 2 +- src/tools/rust-analyzer/docs/user/manual.adoc | 90 +++++++++++++++++-- 2 files changed, 84 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 6a88cf022dfb..a39639676104 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind}; pub struct ProjectJson { /// e.g. `path/to/sysroot` pub(crate) sysroot: Option, - /// e.g. `path/to/sysroot/lib/rustlib/src/rust` + /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, /// The path to the rust-project.json file. May be None if this diff --git a/src/tools/rust-analyzer/docs/user/manual.adoc b/src/tools/rust-analyzer/docs/user/manual.adoc index ffc820e9b7f7..4a2a6f2e3686 100644 --- a/src/tools/rust-analyzer/docs/user/manual.adoc +++ b/src/tools/rust-analyzer/docs/user/manual.adoc @@ -716,6 +716,32 @@ interface JsonProject { /// dependencies as well as sysroot crate (libstd, /// libcore and such). crates: Crate[]; + /// Configuration for CLI commands. + /// + /// These are used for running and debugging binaries + /// and tests without encoding build system-specific + /// knowledge into rust-analyzer. + /// + /// # Example + /// + /// Below is an example of a test runnable. `{label}` and `{test_id}` + /// are explained in `Runnable::args`'s documentation below. + /// + /// ```json + /// { + /// "program": "buck", + /// "args": [ + /// "test", + /// "{label}", + /// "--", + /// "{test_id}", + /// "--print-passing-details" + /// ], + /// "cwd": "/home/user/repo-root/", + /// "kind": "testOne" + /// } + /// ``` + runnables?: Runnable[]; } interface Crate { @@ -726,7 +752,10 @@ interface Crate { /// Path to the root module of the crate. root_module: string; /// Edition of the crate. - edition: "2015" | "2018" | "2021"; + edition: '2015' | '2018' | '2021' | '2024'; + /// The version of the crate. Used for calculating + /// the correct docs.rs URL. + version?: string; /// Dependencies deps: Dep[]; /// Should this crate be treated as a member of @@ -757,9 +786,9 @@ interface Crate { /// rust-analyzer assumes that files from one /// source can't refer to files in another source. source?: { - include_dirs: string[], - exclude_dirs: string[], - }, + include_dirs: string[]; + exclude_dirs: string[]; + }; /// List of cfg groups this crate inherits. /// /// All cfg in these groups will be concatenated to @@ -776,21 +805,68 @@ interface Crate { target?: string; /// Environment variables, used for /// the `env!` macro - env: { [key: string]: string; }, + env: { [key: string]: string; }; /// Whether the crate is a proc-macro crate. is_proc_macro: boolean; /// For proc-macro crates, path to compiled /// proc-macro (.so file). proc_macro_dylib_path?: string; + + /// Repository, matching the URL that would be used + /// in Cargo.toml. + repository?: string; + + /// Build-specific data about this crate. + build?: BuildInfo; } interface Dep { /// Index of a crate in the `crates` array. - crate: number, + crate: number; /// Name as should appear in the (implicit) /// `extern crate name` declaration. - name: string, + name: string; +} + +interface BuildInfo { + /// The name associated with this crate. + /// + /// This is determined by the build system that produced + /// the `rust-project.json` in question. For instance, if buck were used, + /// the label might be something like `//ide/rust/rust-analyzer:rust-analyzer`. + /// + /// Do not attempt to parse the contents of this string; it is a build system-specific + /// identifier similar to `Crate::display_name`. + label: string; + /// Path corresponding to the build system-specific file defining the crate. + build_file: string; + /// The kind of target. + /// + /// This information is used to determine what sort + /// of runnable codelens to provide, if any. + target_kind: 'bin' | 'lib' | 'test'; +} + +interface Runnable { + /// The program invoked by the runnable. + /// + /// For example, this might be `cargo`, `buck`, or `bazel`. + program: string; + /// The arguments passed to `program`. + args: string[]; + /// The current working directory of the runnable. + cwd: string; + /// Used to decide what code lens to offer. + /// + /// `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// + /// The args for testOne can contain two template strings: + /// `{label}` and `{test_id}`. `{label}` will be replaced + /// with the `Build::label` and `{test_id}` will be replaced + /// with the test name. + kind: 'testOne' | string; } ---- From 7d9fe91ccbf350613dd20a39133ad9984999a20b Mon Sep 17 00:00:00 2001 From: Caio Date: Tue, 21 Jan 2025 17:54:16 -0300 Subject: [PATCH 19/77] [cfg_match] Document the use of expressions --- library/core/src/macros/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5c04e5a40df0..01a3c9d2ada7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -313,6 +313,17 @@ pub macro cfg_match { /// } /// } /// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` #[cfg(not(bootstrap))] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] From 2eef052f04de27abf16e8acdd4f2fa9b144b8b00 Mon Sep 17 00:00:00 2001 From: Teodor Tanasoaia <28601907+teoxoy@users.noreply.github.com> Date: Wed, 22 Jan 2025 22:21:02 +0100 Subject: [PATCH 20/77] increase `AUTODEREF_RECURSION_LIMIT` to 20 The limit was introduced in https://github.com/rust-lang/rust-analyzer/pull/1408#discussion_r294059044 to avoid infinite cycles but it effectively caps the number of derefs to 10. Types like `ID3D12Device14` from the `windows` crate run into this because it derefs to `ID3D12Device13`, 13 to 12 and so on. Increasing it to 20 is a quick fix; a better cycle detection method would be nicer long term. --- src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2b5342314a65..2c7076da11ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -17,7 +17,7 @@ use crate::{ TraitEnvironment, Ty, TyBuilder, TyKind, }; -static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); +static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20); #[derive(Debug)] pub(crate) enum AutoderefKind { @@ -49,7 +49,7 @@ pub fn autoderef( // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. // - // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't + // XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more // performant. if v.contains(&resolved) { From a32f64dc307192d7880212f691ff64007dee711e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 23 Jan 2025 13:41:22 +0200 Subject: [PATCH 21/77] Rephrase comment --- src/tools/rust-analyzer/lib/lsp-server/src/msg.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs index 074bc43388a9..2749557b91a0 100644 --- a/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs +++ b/src/tools/rust-analyzer/lib/lsp-server/src/msg.rs @@ -80,9 +80,9 @@ pub struct Request { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Response { - // JSON RPC allows this to be null if it was impossible - // to decode the request's id. Ignore this special case - // and just die horribly. + // JSON-RPC allows this to be null if we can't find or parse the + // request id. We fail deserialization in that case, so we just + // make this field mandatory. pub id: RequestId, #[serde(skip_serializing_if = "Option::is_none")] pub result: Option, From 27084a25a4830b27bef9933291df4e752a6ab27c Mon Sep 17 00:00:00 2001 From: David Richey Date: Thu, 23 Jan 2025 17:36:45 -0600 Subject: [PATCH 22/77] Check cfg when collecting macro defs --- src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 1327bb3ab59c..16c7b5ca00a0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1381,6 +1381,9 @@ impl ExprCollector<'_> { } } ast::Stmt::Item(ast::Item::MacroDef(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; @@ -1390,6 +1393,9 @@ impl ExprCollector<'_> { self.collect_macro_def(statements, macro_id); } ast::Stmt::Item(ast::Item::MacroRules(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; From 99b0ab5f52be400a770bac0e9b58e0d262d2583a Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 12:56:39 +0100 Subject: [PATCH 23/77] Fix `ItemScope` not recording glob imports This caused us other code to incorrectly assume in dealing with a declaration when in fact it was dealing with a glob imported definition --- .../crates/hir-def/src/body/tests/block.rs | 2 +- .../crates/hir-def/src/import_map.rs | 1 + .../crates/hir-def/src/item_scope.rs | 206 ++++++++++-------- .../crates/hir-def/src/nameres/collector.rs | 7 +- .../crates/hir-def/src/nameres/tests.rs | 18 +- .../crates/hir-def/src/nameres/tests/globs.rs | 74 +++---- .../hir-def/src/nameres/tests/macros.rs | 26 +-- .../crates/hir-def/src/per_ns.rs | 24 +- .../crates/hir-def/src/resolver.rs | 8 +- .../rust-analyzer/crates/hir/src/symbols.rs | 64 +++--- .../crates/ide-db/src/symbol_index.rs | 22 +- 11 files changed, 239 insertions(+), 213 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index f483efa85179..e136dd18a55e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -475,7 +475,7 @@ fn outer() { block scope::tests name: _ - outer: v + outer: vg crate outer: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c..04c088b5a5c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -169,6 +169,7 @@ impl ImportMap { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), ImportOrExternCrate::Import(id) => Some(id.import.into()), + ImportOrExternCrate::Glob(id) => Some(id.into()), } } else { match item { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0fec7674109b..0ece878b836a 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,10 +31,54 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { + Glob(UseId), Import(ImportId), ExternCrate(ExternCrateId), } +impl From for ImportOrExternCrate { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it), + ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it), + } + } +} + +impl From for ImportOrExternCrate { + fn from(value: ImportType) -> Self { + match value { + ImportType::Glob(it) => ImportOrExternCrate::Glob(it), + ImportType::Import(it) => ImportOrExternCrate::Import(it), + ImportType::ExternCrate(it) => ImportOrExternCrate::ExternCrate(it), + } + } +} + +impl ImportOrExternCrate { + pub fn into_import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), + ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), + _ => None, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrGlob { + Glob(UseId), + Import(ImportId), +} + +impl ImportOrGlob { + pub fn into_import(self) -> Option { + match self { + ImportOrGlob::Import(it) => Some(it), + _ => None, + } + } +} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum ImportType { Import(ImportId), @@ -42,21 +86,33 @@ pub(crate) enum ImportType { ExternCrate(ExternCrateId), } -impl ImportOrExternCrate { - pub fn into_import(self) -> Option { - match self { - ImportOrExternCrate::Import(it) => Some(it), - _ => None, +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum ImportOrDef { + Import(ImportId), + Glob(UseId), + ExternCrate(ExternCrateId), + Def(ModuleDefId), +} + +impl From for ImportOrDef { + fn from(value: ImportOrExternCrate) -> Self { + match value { + ImportOrExternCrate::Import(it) => ImportOrDef::Import(it), + ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it), + ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it), } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum ImportOrDef { - Import(ImportId), - ExternCrate(ExternCrateId), - Def(ModuleDefId), +impl From for ImportOrDef { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Import(it) => ImportOrDef::Import(it), + ImportOrGlob::Glob(it) => ImportOrDef::Glob(it), + } + } } + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { pub import: UseId, @@ -96,8 +152,8 @@ pub struct ItemScope { // the resolutions of the imports of this scope use_imports_types: FxHashMap, - use_imports_values: FxHashMap, - use_imports_macros: FxHashMap, + use_imports_values: FxHashMap, + use_imports_macros: FxHashMap, use_decls: Vec, extern_crate_decls: Vec, @@ -162,7 +218,7 @@ impl ItemScope { .map(move |name| (name, self.get(name))) } - pub fn values(&self) -> impl Iterator)> + '_ { + pub fn values(&self) -> impl Iterator)> + '_ { self.values.iter().map(|(n, &i)| (n, i)) } @@ -172,7 +228,7 @@ impl ItemScope { self.types.iter().map(|(n, &i)| (n, i)) } - pub fn macros(&self) -> impl Iterator)> + '_ { + pub fn macros(&self) -> impl Iterator)> + '_ { self.macros.iter().map(|(n, &i)| (n, i)) } @@ -183,6 +239,7 @@ impl ItemScope { .filter_map(ImportOrExternCrate::into_import) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) + .filter_map(ImportOrGlob::into_import) .sorted() .dedup() } @@ -192,7 +249,7 @@ impl ItemScope { let mut def_map; let mut scope = self; - while let Some(&m) = scope.use_imports_macros.get(&import) { + while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { let module_id = i.import.lookup(db).container; @@ -224,7 +281,7 @@ impl ItemScope { } } let mut scope = self; - while let Some(&m) = scope.use_imports_values.get(&import) { + while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { let module_id = i.import.lookup(db).container; @@ -514,29 +571,11 @@ impl ItemScope { } _ => _ = glob_imports.types.remove(&lookup), } - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; + let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_types.insert( - import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_types + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -552,28 +591,12 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; + let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, + prev.map_or(ImportOrDef::Def(fld.def), Into::into), ); } cov_mark::hit!(import_shadowed); @@ -597,18 +620,14 @@ impl ItemScope { _ => _ = glob_imports.values.remove(&lookup), } let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -616,19 +635,16 @@ impl ItemScope { Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); + let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -649,17 +665,15 @@ impl ItemScope { _ => _ = glob_imports.macros.remove(&lookup), } let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -669,17 +683,15 @@ impl ItemScope { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); let import = match import { - Some(ImportType::Import(import)) => Some(import), + Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), + Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), _ => None, }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -704,16 +716,27 @@ impl ItemScope { .map(|def| &mut def.vis) .chain(self.values.values_mut().map(|def| &mut def.vis)) .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis)) - .for_each(|vis| { - *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + .for_each(|vis| match vis { + &mut Visibility::Module(_, visibility_explicitness) => { + *vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } }); for mac in self.macros.values_mut() { if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) { continue; } - - mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit); + match mac.vis { + Visibility::Module(_, visibility_explicitness) => { + mac.vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } + } } } @@ -732,20 +755,25 @@ impl ItemScope { buf.push_str(" t"); match import { Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::Glob(_)) => buf.push('g'), Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), None => (), } } if let Some(Item { import, .. }) = def.values { buf.push_str(" v"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if let Some(Item { import, .. }) = def.macros { buf.push_str(" m"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if def.is_none() { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 1e4b42dff5fb..5058d15f3482 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ImportType, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -527,7 +527,10 @@ impl DefCollector<'_> { // FIXME: This should specifically look for a glob import somehow and record that here self.def_map.prelude = Some(( m, - import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), + import + .and_then(ImportOrExternCrate::into_import) + .and_then(ImportOrGlob::into_import) + .map(|it| it.import), )); } types => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 318aee04f7b7..73fc6787bfe8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -103,8 +103,8 @@ mod a { c: t crate::a::b::c - A: v - b: t + A: vg + b: tg "#]], ); } @@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz } "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg "#]], ); } @@ -421,10 +421,10 @@ pub struct NotExported; "#, expect![[r#" crate - Exported: t v - PublicItem: t v - allowed_reexport: t - exported: t + Exported: tg vg + PublicItem: tg vg + allowed_reexport: tg + exported: tg not_allowed_reexport1: _ not_allowed_reexport2: _ "#]], @@ -692,7 +692,7 @@ mod b { b: t crate::a - T: t v + T: t vg crate::b T: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index 8963a5767942..ddb9d4a134d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -18,9 +18,9 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo @@ -53,20 +53,20 @@ pub use super::*; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg Foo: t v bar: t crate::foo::bar Baz: t v - Foo: t v - bar: t + Foo: tg vg + bar: tg "#]], ); } @@ -91,20 +91,20 @@ pub use super::*; ", expect![[r#" crate - Baz: t v - bar: t + Baz: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg PrivateStructFoo: t v bar: t crate::foo::bar Baz: t v PrivateStructBar: t v - PrivateStructFoo: t v - bar: t + PrivateStructFoo: tg vg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct; ", expect![[r#" crate - Foo: t - PubCrateStruct: t v - bar: t + Foo: tg + PubCrateStruct: tg vg + bar: tg foo: t crate::foo @@ -160,7 +160,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -178,7 +178,7 @@ struct Foo; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -193,8 +193,8 @@ use self::Foo::*; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -210,8 +210,8 @@ use self::Foo::{*}; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -359,7 +359,7 @@ use event::Event; event: t crate::event - Event: t v + Event: t vg serenity: t crate::event::serenity @@ -388,10 +388,10 @@ use reexport::*; "#, expect![[r#" crate - Trait: t + Trait: tg defs: t - function: v - makro: m + function: vg + makro: mg reexport: t crate::defs @@ -400,10 +400,10 @@ use reexport::*; makro: m crate::reexport - Trait: t - function: v + Trait: tg + function: vg inner: t - makro: m + makro: mg crate::reexport::inner Trait: ti @@ -442,12 +442,12 @@ mod glob_target { ShouldBePrivate: t v crate::outer - ShouldBePrivate: t v + ShouldBePrivate: tg vg inner_superglob: t crate::outer::inner_superglob - ShouldBePrivate: t v - inner_superglob: t + ShouldBePrivate: tg vg + inner_superglob: tg "#]], ); } @@ -473,20 +473,20 @@ use reexport_2::*; "#, expect![[r#" crate - Placeholder: t v + Placeholder: tg vg libs: t - reexport_1: t + reexport_1: tg reexport_2: t crate::libs Placeholder: t v crate::reexport_2 - Placeholder: t v + Placeholder: tg vg reexport_1: t crate::reexport_2::reexport_1 - Placeholder: t v + Placeholder: tg vg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a05c4dcf9bd7..610886d55f40 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -97,9 +97,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -169,9 +169,9 @@ macro_rules! inner { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -794,7 +794,7 @@ pub trait Clone {} "#, expect![[r#" crate - Clone: t m + Clone: tg mg "#]], ); } @@ -1075,9 +1075,9 @@ macro_rules! mbe { "#, expect![[r#" crate - DummyTrait: m - attribute_macro: m - function_like_macro: m + DummyTrait: mg + attribute_macro: mg + function_like_macro: mg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 899dd4afffef..339786fc0176 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -6,7 +6,7 @@ use bitflags::bitflags; use crate::{ - item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs}, visibility::Visibility, MacroId, ModuleDefId, }; @@ -36,8 +36,8 @@ pub struct Item { } pub type TypesItem = Item; -pub type ValuesItem = Item; -pub type MacrosItem = Item; +pub type ValuesItem = Item; +pub type MacrosItem = Item; #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { @@ -59,7 +59,7 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { + pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: Some(Item { def, vis, import }), macros: None } } @@ -84,7 +84,7 @@ impl PerNs { } } - pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { + pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) } } @@ -108,7 +108,7 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { + pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { self.values.map(|it| (it.def, it.import)) } @@ -116,7 +116,7 @@ impl PerNs { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { + pub fn take_macros_import(self) -> Option<(MacroId, Option)> { self.macros.map(|it| (it.def, it.import)) } @@ -159,14 +159,12 @@ impl PerNs { .map(|it| (ItemInNs::Types(it.def), it.import)) .into_iter() .chain( - self.values.map(|it| { - (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.values + .map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))), ) .chain( - self.macros.map(|it| { - (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.macros + .map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))), ) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 0b9b6da8d513..8c556d8a8c3f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -19,7 +19,7 @@ use crate::{ db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, @@ -107,7 +107,7 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs, Option), + ValueNs(ValueNs, Option), Partial(TypeNs, usize, Option), } @@ -485,7 +485,7 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option<(MacroId, Option)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) @@ -1014,7 +1014,7 @@ impl ModuleItemMap { } } -fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { let (def, import) = per_ns.take_values_import()?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a6b8ed70c363..f379a2d4d65f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -3,7 +3,7 @@ use either::Either; use hir_def::{ db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -55,9 +55,10 @@ impl DeclarationLocation { } /// Represents an outstanding module that the symbol collector must collect symbols from. +#[derive(Debug)] struct SymbolCollectorWork { module_id: ModuleId, - parent: Option, + parent: Option, } pub struct SymbolCollector<'a> { @@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> { } } + pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { + let mut symbol_collector = SymbolCollector::new(db); + symbol_collector.collect(module); + symbol_collector.finish() + } + pub fn collect(&mut self, module: Module) { + let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); + tracing::info!(?module, "SymbolCollector::collect",); self.edition = module.krate().edition(self.db); // The initial work is the root module we're collecting, additional work will @@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> { self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { - let mut symbol_collector = SymbolCollector::new(db); - symbol_collector.collect(module); - symbol_collector.finish() - } - fn do_work(&mut self, work: SymbolCollectorWork) { + let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered(); + tracing::info!(?work, "SymbolCollector::do_work"); self.db.unwind_if_cancelled(); - let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id)); + let parent_name = work.parent.map(|name| name.as_str().to_smolstr()); self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id)); } @@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> { ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::StaticId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::TraitId(id) => { this.push_decl(id, name, false); @@ -235,6 +240,7 @@ impl<'a> SymbolCollector<'a> { if is_explicit_import(vis) { match i { ImportOrExternCrate::Import(i) => push_import(self, i, name, def), + ImportOrExternCrate::Glob(_) => (), ImportOrExternCrate::ExternCrate(i) => { push_extern_crate(self, i, name, def) } @@ -249,7 +255,10 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { if is_explicit_import(vis) { - push_import(self, i, name, def.into()); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into()), + ImportOrGlob::Glob(_) => (), + } } continue; } @@ -260,7 +269,10 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { if is_explicit_import(vis) { - push_import(self, i, name, def); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def), + ImportOrGlob::Glob(_) => (), + } } continue; } @@ -269,7 +281,7 @@ impl<'a> SymbolCollector<'a> { } for const_id in scope.unnamed_consts() { - self.collect_from_body(const_id); + self.collect_from_body(const_id, None); } for (name, id) in scope.legacy_macros() { @@ -285,7 +297,7 @@ impl<'a> SymbolCollector<'a> { } } - fn collect_from_body(&mut self, body_id: impl Into) { + fn collect_from_body(&mut self, body_id: impl Into, name: Option) { let body_id = body_id.into(); let body = self.db.body(body_id); @@ -294,7 +306,7 @@ impl<'a> SymbolCollector<'a> { for (id, _) in def_map.modules() { self.work.push(SymbolCollectorWork { module_id: def_map.module_id(id), - parent: Some(body_id), + parent: name.clone(), }); } } @@ -333,24 +345,6 @@ impl<'a> SymbolCollector<'a> { } } - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { - match body_id { - DefWithBodyId::FunctionId(id) => { - Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::StaticId(id) => { - Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::ConstId(id) => { - Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::VariantId(id) => { - Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::InTypeConstId(_) => Some("in type const".into()), - } - } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { AssocItemId::FunctionId(id) => self.push_decl(id, name, true), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c94644eeb89b..e5ce10a771ef 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -143,7 +143,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module))) + Arc::new(SymbolIndex::new(SymbolCollector::new_module(db.upcast(), module))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { @@ -284,13 +284,15 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - // FIXME: fst::Map should ideally have a way to shrink the backing buffer without the unwrap dance - let map = fst::Map::new({ - let mut buf = builder.into_inner().unwrap(); - buf.shrink_to_fit(); - buf - }) - .unwrap(); + let map = builder + .into_inner() + .and_then(|mut buf| { + fst::Map::new({ + buf.shrink_to_fit(); + buf + }) + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -491,7 +493,7 @@ pub(self) use crate::Trait as IsThisJustATrait; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) @@ -518,7 +520,7 @@ struct Duplicate; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) From dfd94903c84fc267c2d771f87ab7388d00acf808 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 14:23:01 +0100 Subject: [PATCH 24/77] fix: Fix flycheck panicking with "once" invocation strategy We only ever have one flycheck runner no matter the number of workspaces, so just kick off flycheck for it immediately --- .../crates/rust-analyzer/src/flycheck.rs | 11 ++++++++ .../src/handlers/notification.rs | 25 +++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) 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 22f06d68d80d..2309f94a7429 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -88,6 +88,17 @@ pub(crate) enum FlycheckConfig { }, } +impl FlycheckConfig { + pub(crate) fn invocation_strategy_once(&self) -> bool { + match self { + FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CustomCommand { invocation_strategy, .. } => { + *invocation_strategy == InvocationStrategy::Once + } + } + } +} + impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 98efc637c2c8..84ba89d9f31f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -291,9 +291,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { + if invocation_strategy_once { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); + world.flycheck[0].restart_workspace(saved_file.clone()); + } + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -320,16 +326,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // the user opted into package checks then let package_check_allowed = target.is_some() || !may_flycheck_workspace; if package_check_allowed { - let workspace = - world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some((idx, _)) = workspace { + let workspace = world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = workspace { world.flycheck[idx].restart_for_package(package, target); } } From 024da87502fccb6756a9955e57916d6fee11b945 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 24 Jan 2025 01:53:17 +0900 Subject: [PATCH 25/77] feat: Implement `arbitrary-self-types` --- .../crates/hir-def/src/lang_item.rs | 1 + .../crates/hir-ty/src/autoderef.rs | 41 ++++++++++++---- .../crates/hir-ty/src/infer/coerce.rs | 2 +- .../crates/hir-ty/src/infer/expr.rs | 6 +-- .../crates/hir-ty/src/method_resolution.rs | 10 ++-- .../hir-ty/src/tests/method_resolution.rs | 47 +++++++++++++++---- .../ide-completion/src/completions/dot.rs | 30 ++++++++++++ .../crates/intern/src/symbol/symbols.rs | 1 + .../crates/test-utils/src/minicore.rs | 17 +++++++ 9 files changed, 129 insertions(+), 26 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index afdc49a2dc59..e83ce6dc42ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -372,6 +372,7 @@ language_item_table! { DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None; Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2c7076da11ab..62feca5f8cbb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -39,7 +39,7 @@ pub fn autoderef( ) -> impl Iterator { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { at_start: bool, steps: T, explicit: bool, + use_receiver_trait: bool, } impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { + pub(crate) fn new( + table: &'table mut InferenceTable<'db>, + ty: Ty, + explicit: bool, + use_receiver_trait: bool, + ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } + Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { @@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit } + Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -137,7 +144,8 @@ impl Iterator for Autoderef<'_, '_, T> { return None; } - let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; + let (kind, new_ty) = + autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; self.steps.push(kind, &self.ty); self.ty = new_ty; @@ -150,11 +158,12 @@ pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } } @@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>( pub(crate) fn deref_by_trait( table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, ty: Ty, + use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { @@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait( return None; } - let deref_trait = - db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; + let trait_id = || { + if use_receiver_trait { + if let Some(receiver) = + db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) + { + return Some(receiver); + } + } + // Old rustc versions might not have `Receiver` trait. + // Fallback to `Deref` if they don't + db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait()) + }; + let trait_id = trait_id()?; let target = db - .trait_data(deref_trait) + .trait_data(trait_id) .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?; let projection = { - let b = TyBuilder::subst_for_def(db, deref_trait, None); + let b = TyBuilder::subst_for_def(db, trait_id, None); if b.remaining() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 2fe90a8a9243..d40816ba8ced 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -420,7 +420,7 @@ impl InferenceTable<'_> { let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(self, from_ty.clone(), false); + let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; let mut found = None; 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 6b6c0348dcb4..b951443897cb 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 @@ -487,7 +487,7 @@ impl InferenceContext<'_> { } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); @@ -854,7 +854,7 @@ impl InferenceContext<'_> { if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { - deref_by_trait(&mut self.table, inner_ty) + deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) } } @@ -1718,7 +1718,7 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 182032f04812..1cea67ee9641 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -528,7 +528,7 @@ impl ReceiverAdjustments { let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true) { + match autoderef::autoderef_step(table, ty.clone(), true, false) { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); @@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver( // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, @@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver( ControlFlow::Continue(()) })?; table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { // don't try to resolve methods on unknown types @@ -1709,7 +1711,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 74acf23b75ab..8866de22dfb9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1343,7 +1343,7 @@ fn foo(a: &T) { fn autoderef_visibility_field() { check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); pub struct Bar(i32); @@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() { cov_mark::check!(autoderef_candidate_not_visible); check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); impl Foo { @@ -1741,7 +1741,7 @@ fn main() { fn deref_fun_1() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1782,7 +1782,7 @@ fn test() { fn deref_fun_2() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into) { fn bad_inferred_reference_2() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver trait ExactSizeIterator { fn len(&self) -> usize; } @@ -2054,7 +2054,7 @@ fn foo() { fn box_deref_is_builtin() { check( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; #[lang = "owned_box"] @@ -2087,7 +2087,7 @@ fn test() { fn manually_drop_deref_is_not_builtin() { check( r#" -//- minicore: manually_drop, deref +//- minicore: manually_drop, receiver struct Foo; impl Foo { fn foo(&self) {} @@ -2105,7 +2105,7 @@ fn test() { fn mismatched_args_due_to_supertraits_with_deref() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; trait Trait1 { @@ -2139,3 +2139,34 @@ fn problem_method() { "#, ); } + +#[test] +fn receiver_without_deref_impl() { + check( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo1(self: &Bar) -> i32 { 42 } + fn foo2(self: Bar) -> bool { true } +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + let _v1 = bar.foo1(); + //^^^ type: i32 + let _v2 = bar.foo2(); + //^^^ type: bool +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded..fefea1ab13a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -1466,4 +1466,34 @@ async fn bar() { "#, ); } + + #[test] + fn receiver_without_deref_impl_completion() { + check_no_kw( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo(self: Bar) {} +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + bar.$0 +} +"#, + expect![[r#" + me foo() fn(self: Bar) +"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index 7a090a6b6bb7..9bc78ff87b8a 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -394,6 +394,7 @@ define_symbols! { RangeToInclusive, Ready, receiver, + receiver_target, recursion_limit, register_attr, register_tool, 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 fd06736a2524..4ed68d18e807 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -53,6 +53,7 @@ //! pin: //! pointee: copy, send, sync, ord, hash, unpin //! range: +//! receiver: deref //! result: //! send: sized //! size_of: sized @@ -513,10 +514,26 @@ pub mod ops { fn deref_mut(&mut self) -> &mut Self::Target; } // endregion:deref_mut + + // region:receiver + #[lang = "receiver"] + pub trait Receiver { + #[lang = "receiver_target"] + type Target: ?Sized; + } + + impl Receiver for P + where + P: Deref, + { + type Target = T; + } + // endregion:receiver } pub use self::deref::{ Deref, DerefMut, // :deref_mut + Receiver, // :receiver }; // endregion:deref From 1dc34eeb99c6c59de51ebe37c7680cfdc764c30d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 24 Jan 2025 23:06:02 +0900 Subject: [PATCH 26/77] Add a new failing test that overflows stack --- .../crates/ide/src/hover/tests.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) 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 014b751f95b0..8fe932e2df1a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -10349,3 +10349,40 @@ macro_rules! str { "#]], ); } + +#[test] +fn regression_19007() { + check( + r#" +trait Foo { + type Assoc; + + fn foo(&self) -> Self::Assoc; +} + +trait Bar { + type Target; +} + +trait Baz {} + +struct Struct { + field: T, +} + +impl Struct +where + T: Foo, + T::Assoc: Baz<::Target> + Bar, +{ + fn f(&self) { + let x$0 = self.field.foo(); + } +} + "#, + expect![ + r#" + "# + ], + ); +} From 1e5f47bf0970ae125de02a8acb5cd246da0961a8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 15:12:48 +0100 Subject: [PATCH 27/77] Record the use tree index in glob imports --- .../crates/hir-def/src/import_map.rs | 4 +- .../crates/hir-def/src/item_scope.rs | 122 +++++++++--------- .../crates/hir-def/src/nameres/collector.rs | 75 ++++++----- .../crates/hir-def/src/per_ns.rs | 2 +- .../rust-analyzer/crates/hir/src/symbols.rs | 4 +- 5 files changed, 112 insertions(+), 95 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index bb780277efca..34635997bdff 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -167,8 +167,8 @@ impl ImportMap { let attr_id = if let Some(import) = import { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), - ImportOrExternCrate::Import(id) => Some(id.import.into()), - ImportOrExternCrate::Glob(id) => Some(id.into()), + ImportOrExternCrate::Import(id) => Some(id.use_.into()), + ImportOrExternCrate::Glob(id) => Some(id.use_.into()), } } else { match item { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0ece878b836a..65a39c565611 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,7 +31,7 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { - Glob(UseId), + Glob(GlobId), Import(ImportId), ExternCrate(ExternCrateId), } @@ -45,29 +45,41 @@ impl From for ImportOrExternCrate { } } -impl From for ImportOrExternCrate { - fn from(value: ImportType) -> Self { - match value { - ImportType::Glob(it) => ImportOrExternCrate::Glob(it), - ImportType::Import(it) => ImportOrExternCrate::Import(it), - ImportType::ExternCrate(it) => ImportOrExternCrate::ExternCrate(it), - } - } -} - impl ImportOrExternCrate { - pub fn into_import(self) -> Option { + pub fn import_or_glob(self) -> Option { match self { ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), _ => None, } } + + pub fn import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } + + pub fn glob(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id), + _ => None, + } + } + + pub fn use_(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id.use_), + ImportOrExternCrate::Import(id) => Some(id.use_), + _ => None, + } + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrGlob { - Glob(UseId), + Glob(GlobId), Import(ImportId), } @@ -79,17 +91,11 @@ impl ImportOrGlob { } } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum ImportType { - Import(ImportId), - Glob(UseId), - ExternCrate(ExternCrateId), -} #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrDef { Import(ImportId), - Glob(UseId), + Glob(GlobId), ExternCrate(ExternCrateId), Def(ModuleDefId), } @@ -115,7 +121,13 @@ impl From for ImportOrDef { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { - pub import: UseId, + pub use_: UseId, + pub idx: Idx, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct GlobId { + pub use_: UseId, pub idx: Idx, } @@ -236,7 +248,7 @@ impl ItemScope { self.use_imports_types .keys() .copied() - .filter_map(ImportOrExternCrate::into_import) + .filter_map(ImportOrExternCrate::import_or_glob) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) .filter_map(ImportOrGlob::into_import) @@ -252,7 +264,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -268,7 +280,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -284,7 +296,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -545,9 +557,13 @@ impl ItemScope { self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis) } - pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - // FIXME: import - self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None }); + pub(crate) fn push_unnamed_trait( + &mut self, + tr: TraitId, + vis: Visibility, + import: Option, + ) { + self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import }); } pub(crate) fn push_res_with_import( @@ -555,7 +571,7 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - import: Option, + import: Option, ) -> bool { let mut changed = false; @@ -566,12 +582,11 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.types.insert(lookup.clone()); } _ => _ = glob_imports.types.remove(&lookup), } - let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types @@ -582,7 +597,7 @@ impl ItemScope { } Entry::Occupied(mut entry) => { match import { - Some(ImportType::Glob(..)) => { + Some(ImportOrExternCrate::Glob(..)) => { // Multiple globs may import the same item and they may // override visibility from previously resolved globs. This is // currently handled by `DefCollector`, because we need to @@ -591,7 +606,6 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = import.map(Into::into); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( @@ -614,16 +628,12 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.values.insert(lookup.clone()); } _ => _ = glob_imports.values.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_values @@ -632,15 +642,13 @@ impl ItemScope { entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_values @@ -659,16 +667,12 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.macros.insert(lookup.clone()); } _ => _ = glob_imports.macros.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( @@ -679,14 +683,12 @@ impl ItemScope { entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(ImportOrGlob::Import(import)), - Some(ImportType::Glob(u)) => Some(ImportOrGlob::Glob(u)), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( @@ -856,7 +858,7 @@ impl PerNs { match def { ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), ModuleDefId::FunctionId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::AdtId(adt) => match adt { AdtId::UnionId(_) => PerNs::types(def, v, import), @@ -871,14 +873,14 @@ impl PerNs { }, ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => { - PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 5058d15f3482..06276335b718 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ImportType, PerNsGlobImports}, + item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -208,7 +208,7 @@ struct DefCollector<'a> { def_map: DefMap, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, - glob_imports: FxHashMap>, + glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, @@ -524,14 +524,7 @@ impl DefCollector<'_> { match per_ns.types { Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => { - // FIXME: This should specifically look for a glob import somehow and record that here - self.def_map.prelude = Some(( - m, - import - .and_then(ImportOrExternCrate::into_import) - .and_then(ImportOrGlob::into_import) - .map(|it| it.import), - )); + self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_))); } types => { tracing::debug!( @@ -848,13 +841,14 @@ impl DefCollector<'_> { def.values = None; def.macros = None; } - let imp = ImportType::Import(ImportId { import: id, idx: use_tree }); + let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree }); tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => { + ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => { tracing::debug!("glob import: {:?}", import); + let glob = GlobId { use_: id, idx: use_tree }; match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if is_prelude { @@ -878,7 +872,12 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -910,11 +909,16 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); // record the glob import in case we add further items - let glob = self.glob_imports.entry(m.local_id).or_default(); - match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) { - None => glob.push((module_id, vis, id)), + let glob_imports = self.glob_imports.entry(m.local_id).or_default(); + match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) { + None => glob_imports.push((module_id, vis, glob)), Some((_, old_vis, _)) => { if let Some(new_vis) = old_vis.max(vis, &self.def_map) { *old_vis = new_vis; @@ -947,7 +951,12 @@ impl DefCollector<'_> { (Some(name), res) }) .collect::>(); - self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -967,7 +976,7 @@ impl DefCollector<'_> { resolutions: &[(Option, PerNs)], // Visibility this import will have vis: Visibility, - import: Option, + import: Option, ) { self.db.unwind_if_cancelled(); self.update_recursive(module_id, resolutions, vis, import, 0) @@ -981,7 +990,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import: Option, + import: Option, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -997,8 +1006,10 @@ impl DefCollector<'_> { self.push_res_and_update_glob_vis(module_id, name, *res, vis, import); } None => { - let tr = match res.take_types() { - Some(ModuleDefId::TraitId(tr)) => tr, + let (tr, import) = match res.take_types_full() { + Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => { + (tr, import) + } Some(other) => { tracing::debug!("non-trait `_` import of {:?}", other); continue; @@ -1024,7 +1035,11 @@ impl DefCollector<'_> { if should_update { changed = true; - self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis); + self.def_map.modules[module_id].scope.push_unnamed_trait( + tr, + vis, + import.and_then(ImportOrExternCrate::import), + ); } } } @@ -1046,13 +1061,13 @@ impl DefCollector<'_> { .cloned() .collect::>(); - for (glob_importing_module, glob_import_vis, use_) in glob_imports { + for (glob_importing_module, glob_import_vis, glob) in glob_imports { let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis); self.update_recursive( glob_importing_module, resolutions, vis, - Some(ImportType::Glob(use_)), + Some(ImportOrExternCrate::Glob(glob)), depth + 1, ); } @@ -1064,7 +1079,7 @@ impl DefCollector<'_> { name: &Name, mut defs: PerNs, vis: Visibility, - def_import_type: Option, + def_import_type: Option, ) -> bool { // `extern crate crate_name` things can be re-exported as `pub use crate_name`. // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name` @@ -1077,10 +1092,10 @@ impl DefCollector<'_> { let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else { return false; }; - let Some(ImportType::Import(id)) = def_import_type else { + let Some(ImportOrExternCrate::Import(id)) = def_import_type else { return false; }; - let use_id = id.import.lookup(self.db).id; + let use_id = id.use_.lookup(self.db).id; let item_tree = use_id.item_tree(self.db); let use_kind = item_tree[use_id.value].use_tree.kind(); let UseTreeKind::Single { path, .. } = use_kind else { @@ -1103,7 +1118,7 @@ impl DefCollector<'_> { let mut changed = false; - if let Some(ImportType::Glob(_)) = def_import_type { + if let Some(ImportOrExternCrate::Glob(_)) = def_import_type { let prev_defs = self.def_map[module_id].scope.get(name); // Multiple globs may import the same item and they may override visibility from @@ -1730,7 +1745,7 @@ impl ModCollector<'_, '_> { ), )], vis, - Some(ImportType::ExternCrate(id)), + Some(ImportOrExternCrate::ExternCrate(id)), ); } else { if let Some(name) = name { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 339786fc0176..c2d3f67f17e7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -78,7 +78,7 @@ impl PerNs { values: Some(Item { def: values, vis, - import: import.and_then(ImportOrExternCrate::into_import), + import: import.and_then(ImportOrExternCrate::import_or_glob), }), macros: None, } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index f379a2d4d65f..c1c45d817531 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -160,8 +160,8 @@ impl<'a> SymbolCollector<'a> { let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { let source = import_child_source_cache - .entry(i.import) - .or_insert_with(|| i.import.child_source(this.db.upcast())); + .entry(i.use_) + .or_insert_with(|| i.use_.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; let Some(name_ptr) = use_tree_src .rename() From b44570fd5e8271b9126a84675f918779d5491afc Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 24 Jan 2025 14:45:38 +0100 Subject: [PATCH 28/77] Lazily compute location links in type hints again --- .../crates/ide/src/inlay_hints.rs | 21 ++++++++++++------- .../ide/src/inlay_hints/closing_brace.rs | 3 ++- src/tools/rust-analyzer/crates/ide/src/lib.rs | 3 ++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 088a11bcb4cc..1f723c85df7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -650,7 +650,8 @@ struct InlayHintLabelBuilder<'a> { db: &'a RootDatabase, result: InlayHintLabel, last_part: String, - location: Option, + resolve: bool, + location: Option>, } impl fmt::Write for InlayHintLabelBuilder<'_> { @@ -663,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; - let location = location.call_site(); - let location = - FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; - self.location = Some(location); + + self.location = Some(if self.resolve { + LazyProperty::Lazy + } else { + LazyProperty::Computed({ + let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); + FileRange { file_id: location.file_id, range: location.focus_or_full_range() } + }) + }); } fn end_location_link(&mut self) { @@ -681,7 +687,7 @@ impl InlayHintLabelBuilder<'_> { if !text.is_empty() { self.result.parts.push(InlayHintLabelPart { text, - linked_location: self.location.take().map(LazyProperty::Computed), + linked_location: self.location.take(), tooltip: None, }); } @@ -753,6 +759,7 @@ fn label_of_ty( last_part: String::new(), location: None, result: InlayHintLabel::default(), + resolve: config.fields_to_resolve.resolve_label_location, }; let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition); let r = label_builder.finish(); 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 bd36e2c3be6f..3767d34e2c7a 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 @@ -12,7 +12,8 @@ use syntax::{ }; use crate::{ - inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind, + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, + InlayKind, }; pub(super) fn hints( diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 6ec95ccb62a5..41a3302c0953 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,8 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, LazyProperty + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, + LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, From 0a14e17523adc4b53d372a8aef274bf1bf304562 Mon Sep 17 00:00:00 2001 From: David Richey Date: Fri, 24 Jan 2025 09:59:06 -0600 Subject: [PATCH 29/77] Explicitly add buildfiles when constructing ProjectFolders --- .../crates/load-cargo/src/lib.rs | 18 ++++++++ .../crates/project-model/src/workspace.rs | 41 +++++++++---------- 2 files changed, 38 insertions(+), 21 deletions(-) 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 00446b27cf2f..5654c04a5928 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -256,6 +256,24 @@ impl ProjectFolders { fsc.add_file_set(file_set_roots) } + for ws in workspaces.iter() { + let mut file_set_roots: Vec = vec![]; + let mut entries = vec![]; + + for buildfile in ws.buildfiles() { + file_set_roots.push(VfsPath::from(buildfile.to_owned())); + entries.push(buildfile.to_owned()); + } + + if !file_set_roots.is_empty() { + let entry = vfs::loader::Entry::Files(entries); + res.watch.push(res.load.len()); + res.load.push(entry); + local_filesets.push(fsc.len() as u64); + fsc.add_file_set(file_set_roots) + } + } + if let Some(user_config_path) = user_config_dir_path { let ratoml_path = { let mut p = user_config_path.to_path_buf(); 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 f98d983ac060..dcd62753cb2f 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -524,6 +524,17 @@ impl ProjectWorkspace { } } + pub fn buildfiles(&self) -> Vec { + match &self.kind { + ProjectWorkspaceKind::Json(project) => project + .crates() + .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone())) + .map(|build_file| self.workspace_root().join(build_file)) + .collect(), + _ => vec![], + } + } + pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { self.sysroot.discover_proc_macro_srv() } @@ -568,27 +579,15 @@ impl ProjectWorkspace { match &self.kind { ProjectWorkspaceKind::Json(project) => project .crates() - .map(|(_, krate)| { - // FIXME: PackageRoots dont allow specifying files, only directories - let build_file = krate - .build - .as_ref() - .map(|build| self.workspace_root().join(&build.build_file)) - .as_deref() - .and_then(AbsPath::parent) - .map(ToOwned::to_owned); - - PackageRoot { - is_local: krate.is_workspace_member, - include: krate - .include - .iter() - .cloned() - .chain(build_file) - .chain(self.extra_includes.iter().cloned()) - .collect(), - exclude: krate.exclude.clone(), - } + .map(|(_, krate)| PackageRoot { + is_local: krate.is_workspace_member, + include: krate + .include + .iter() + .cloned() + .chain(self.extra_includes.iter().cloned()) + .collect(), + exclude: krate.exclude.clone(), }) .collect::>() .into_iter() From fff24d52ee84ad49b780443cdbfe9ca38bb85611 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 24 Jan 2025 11:25:29 -0800 Subject: [PATCH 30/77] minor: Suggest better names when a type is a sequence Previously, we'd suggest a type of `vec` for a value of type `Vec`, which is rarely what the user wants. We also had no suggestions for values of type `&[T]`. Instead, try to pluralise the inner type name, and fall back to `items`. --- .../src/handlers/extract_variable.rs | 20 ++--- .../ide-db/src/syntax_helpers/suggest_name.rs | 83 +++++++++++++++++++ 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 97321f4ec1ef..7b6f76d00452 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1672,8 +1672,8 @@ macro_rules! vec { () => {Vec} } fn main() { - let $0vec = vec![]; - let _ = vec; + let $0items = vec![]; + let _ = items; } "#, "Extract into variable", @@ -1696,8 +1696,8 @@ macro_rules! vec { () => {Vec} } fn main() { - const $0VEC: Vec = vec![]; - let _ = VEC; + const $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into constant", @@ -1720,8 +1720,8 @@ macro_rules! vec { () => {Vec} } fn main() { - static $0VEC: Vec = vec![]; - let _ = VEC; + static $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into static", @@ -2019,8 +2019,8 @@ impl Vec { } fn foo(s: &mut S) { - let $0vec = &mut s.vec; - vec.push(0); + let $0items = &mut s.vec; + items.push(0); }"#, "Extract into variable", ); @@ -2106,8 +2106,8 @@ impl Vec { } fn foo(f: &mut Y) { - let $0vec = &mut f.field.field.vec; - vec.push(0); + let $0items = &mut f.field.field.vec; + items.push(0); }"#, "Extract into variable", ); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 557c95f704b9..0a7141c19b6b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// `Result` -> `User` const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; +/// Generic types replaced by a plural of their first argument. +/// +/// # Examples +/// `Vec` -> "names" +const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; + /// Prefixes to strip from methods names /// /// # Examples @@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option Option, db: &RootDatabase, edition: Edition) -> SmolStr { + let items_str = SmolStr::new_static("items"); + let Some(inner_ty) = inner_ty else { + return items_str; + }; + let Some(name) = name_of_type(inner_ty, db, edition) else { + return items_str; + }; + + if name.ends_with(['s', 'x', 'y']) { + // Given a type called e.g. "Boss", "Fox" or "Story", don't try to + // create a plural. + items_str + } else { + SmolStr::new(format!("{name}s")) + } +} + fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option { let name = trait_.name(db).display(db, edition).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { @@ -897,6 +928,58 @@ fn foo() { $0(bar())$0; } ); } + #[test] + fn vec_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn vec_value_ends_with_s() { + check( + r#" +struct Vec {}; +struct Boss; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "items", + ); + } + + #[test] + fn vecdeque_value() { + check( + r#" +struct VecDeque {}; +struct Seed; +fn bar() -> VecDeque {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn slice_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> &[Seed] {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + #[test] fn ref_call() { check( From 592eceedf50c5e11b6099a413e47dfb906feb269 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 11:12:06 +0100 Subject: [PATCH 31/77] Only collect implicit visibile use symbols if they have renames Otherwise this will pollute the index too much with unnecessary symbols --- .../rust-analyzer/crates/hir/src/symbols.rs | 87 +++++++++---------- .../test_symbol_index_collection.txt | 33 +++++++ 2 files changed, 72 insertions(+), 48 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index f379a2d4d65f..db6ed8b3e7f2 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -158,24 +158,32 @@ impl<'a> SymbolCollector<'a> { // Nested trees are very common, so a cache here will hit a lot. let import_child_source_cache = &mut FxHashMap::default(); - let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let is_explicit_import = |vis| match vis { + Visibility::Public => true, + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => false, + }; + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| { let source = import_child_source_cache .entry(i.import) .or_insert_with(|| i.import.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; - let Some(name_ptr) = use_tree_src - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = use_tree_src.rename().and_then(|rename| rename.name()); + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => { + (|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))() + } + None => None, + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -188,23 +196,23 @@ impl<'a> SymbolCollector<'a> { }; let push_extern_crate = - |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| { let loc = i.lookup(this.db.upcast()); let source = loc.source(this.db.upcast()); - let Some(name_ptr) = source - .value - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| source.value.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = source.value.rename().and_then(|rename| rename.name()); + + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => None, + None => source.value.name_ref().map(Either::Right), + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -216,18 +224,6 @@ impl<'a> SymbolCollector<'a> { }); }; - let is_explicit_import = |vis| { - match vis { - Visibility::Module(_, VisibilityExplicitness::Explicit) => true, - Visibility::Module(_, VisibilityExplicitness::Implicit) => { - // consider imports in the crate root explicit, as these are visibly - // crate-wide anyways - module_id.is_crate_root() - } - Visibility::Public => true, - } - }; - let def_map = module_id.def_map(self.db.upcast()); let scope = &def_map[module_id.local_id].scope; @@ -237,15 +233,14 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.types() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrExternCrate::Import(i) => push_import(self, i, name, def), - ImportOrExternCrate::Glob(_) => (), - ImportOrExternCrate::ExternCrate(i) => { - push_extern_crate(self, i, name, def) - } + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis), + ImportOrExternCrate::Glob(_) => (), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def, vis) } } + continue; } // self is a declaration @@ -254,11 +249,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrGlob::Import(i) => push_import(self, i, name, def.into()), - ImportOrGlob::Glob(_) => (), - } + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -268,11 +261,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrGlob::Import(i) => push_import(self, i, name, def), - ImportOrGlob::Glob(_) => (), - } + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def, vis), + ImportOrGlob::Glob(_) => (), } continue; } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 535777dfcbea..7dce95592b81 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -1007,6 +1007,39 @@ is_alias: false, is_assoc: false, }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: EditionedFileId( + FileId( + 1, + ), + Edition2021, + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 85..125, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), + }, + container_name: None, + is_alias: false, + is_assoc: false, + }, ], ), ] From 0db8d05b5258f634c1c33551e104818338abd63b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 12:18:54 +0100 Subject: [PATCH 32/77] Fix flyimport not filtering via stability of import path --- .../crates/hir-def/src/find_path.rs | 25 +++++++++---- .../rust-analyzer/crates/hir-def/src/lib.rs | 3 ++ .../crates/hir-ty/src/display.rs | 1 + .../crates/ide-assists/src/assist_config.rs | 1 + .../crates/ide-completion/src/completions.rs | 2 +- .../ide-completion/src/completions/expr.rs | 4 +-- .../src/completions/flyimport.rs | 6 ++-- .../ide-completion/src/completions/postfix.rs | 2 +- .../crates/ide-completion/src/config.rs | 3 +- .../crates/ide-completion/src/context.rs | 4 ++- .../crates/ide-completion/src/lib.rs | 2 +- .../crates/ide-completion/src/render.rs | 2 +- .../crates/ide-completion/src/snippet.rs | 2 +- .../ide-completion/src/tests/flyimport.rs | 35 +++++++++++++++++++ .../crates/ide-db/src/path_transform.rs | 3 ++ .../src/handlers/json_is_not_rust.rs | 1 + .../src/handlers/missing_fields.rs | 1 + .../src/handlers/typed_hole.rs | 1 + .../crates/ide-diagnostics/src/lib.rs | 9 +++-- .../crates/ide-ssr/src/matching.rs | 1 + .../rust-analyzer/src/cli/analysis_stats.rs | 1 + 21 files changed, 89 insertions(+), 20 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 5d67902c8ac1..c30ad0163b9d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -445,6 +445,10 @@ fn find_in_dep( }; cov_mark::hit!(partially_imported); if info.is_unstable { + if !ctx.cfg.allow_unstable { + // the item is unstable and we are not allowed to use unstable items + continue; + } choice.stability = Unstable; } @@ -670,6 +674,7 @@ mod tests { prefer_prelude: bool, prefer_absolute: bool, prefer_no_std: bool, + allow_unstable: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -711,7 +716,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, @@ -732,7 +737,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, false, expect); + check_found_path_(ra_fixture, path, false, false, false, false, expect); } fn check_found_path_prelude( @@ -740,7 +745,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, true, false, false, expect); + check_found_path_(ra_fixture, path, true, false, false, false, expect); } fn check_found_path_absolute( @@ -748,7 +753,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, true, false, expect); + check_found_path_(ra_fixture, path, false, true, false, false, expect); } fn check_found_path_prefer_no_std( @@ -756,7 +761,15 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, true, expect); + check_found_path_(ra_fixture, path, false, false, true, false, expect); + } + + fn check_found_path_prefer_no_std_allow_unstable( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + path: &str, + expect: Expect, + ) { + check_found_path_(ra_fixture, path, false, false, true, true, expect); } #[test] @@ -1951,7 +1964,7 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path_prefer_no_std( + check_found_path_prefer_no_std_allow_unstable( r#" //- /main.rs crate:main deps:std,core extern crate std; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index da9ffae8aab0..c78818c642ce 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -114,6 +114,9 @@ pub struct ImportPathConfig { pub prefer_prelude: bool, /// If true, prefer abs path (starting with `::`) where it is available. pub prefer_absolute: bool, + /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no + /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. + pub allow_unstable: bool, } #[derive(Debug)] 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 3545bf767767..d960aaf99f3d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -1159,6 +1159,7 @@ impl HirDisplay for Ty { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, ) { write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 82d8db425892..fb533077d962 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -28,6 +28,7 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable: true, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 40669c65c576..88f893e42a6a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -660,7 +660,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index db18b531d7c3..e71017517019 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 435b88de4ae6..24243f57b46a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -316,7 +316,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -358,7 +358,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 67ea05e002b7..2c39a8fdfed7 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -60,7 +60,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index c641df38ff23..45aab38e8ea0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -59,11 +59,12 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self) -> ImportPathConfig { + pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { ImportPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3a2a4a23a198..366e79cddfa5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> { /// The module of the `scope`. pub(crate) module: hir::Module, /// Whether nightly toolchain is used. Cached since this is looked up a lot. - is_nightly: bool, + pub(crate) is_nightly: bool, + /// The edition of the current crate + // FIXME: This should probably be the crate of the current token? pub(crate) edition: Edition, /// The expected name of what we are completing. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 56d7eeaf8ea0..ac6b1207f2e0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -289,7 +289,7 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(); + let cfg = config.import_path_config(true); imports.into_iter().for_each(|(full_import_path, imported_name)| { let items_with_name = items_locator::items_with_name( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 1b7adf1adb06..dc7eacbfbafd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -301,7 +301,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 04bb178c658f..866b83a61460 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d491e438feff..2e7c53def7fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1390,6 +1390,41 @@ pub struct FooStruct {} ); } +#[test] +fn flyimport_pattern_unstable_path() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![""], + ); + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![[r#" + st FooStruct (use std::unstable::FooStruct) + "#]], + ); +} + #[test] fn flyimport_pattern_unstable_item_on_nightly() { check( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a045c22c2dff..f045e44dd318 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -319,6 +319,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), @@ -378,6 +379,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; @@ -417,6 +419,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index dca889d1a8ef..f22041ebe233 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -147,6 +147,7 @@ pub(crate) fn json_in_items( prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, + allow_unstable: true, }; if !scope_has("Serialize") { 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 fd1044e51bc2..938b7182bc94 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 @@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole) -> Option prefer_no_std: ctx.config.prefer_no_std, prefer_prelude: ctx.config.prefer_prelude, prefer_absolute: ctx.config.prefer_absolute, + allow_unstable: ctx.is_nightly, }, ctx.edition, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 1e99d7ad6e68..50c91a69602c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -83,7 +83,7 @@ use either::Either; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::SourceDatabase, + base_db::{ReleaseChannel, SourceDatabase}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, @@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> { sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + is_nightly: bool, } impl DiagnosticsContext<'_> { @@ -368,7 +369,11 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); - let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() }; + let is_nightly = matches!( + module.and_then(|m| db.toolchain_channel(m.krate().into())), + Some(ReleaseChannel::Nightly) | None + ); + let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly }; let mut diags = Vec::new(); match module { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 4edc3633fbe6..4bead14e31d4 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -673,6 +673,7 @@ impl Match { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index bcaec5201959..18c27c844964 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -465,6 +465,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, Edition::LATEST, ) From 5d94c97ddbcbe53f5a143691d3ebc0f5dd2c0e27 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Jan 2025 12:30:20 +0100 Subject: [PATCH 33/77] Skip redundant path search in `resolve_completion_edits` --- .../crates/ide-completion/src/item.rs | 10 +---- .../crates/ide-completion/src/lib.rs | 37 +++++-------------- src/tools/rust-analyzer/crates/ide/src/lib.rs | 2 +- .../rust-analyzer/src/handlers/request.rs | 5 +-- .../crates/rust-analyzer/src/lib.rs | 3 +- .../crates/rust-analyzer/src/lsp/ext.rs | 1 - .../crates/rust-analyzer/src/lsp/to_proto.rs | 5 +-- .../crates/syntax/src/ast/make.rs | 12 +++++- .../rust-analyzer/docs/dev/lsp-extensions.md | 2 +- 9 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index b0a096b64af2..41a82409597b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -82,8 +82,7 @@ pub struct CompletionItem { pub ref_match: Option<(CompletionItemRefMode, TextSize)>, /// The import data to add to completion's edits. - /// (ImportPath, LastSegment) - pub import_to_add: SmallVec<[(String, String); 1]>, + pub import_to_add: SmallVec<[String; 1]>, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -570,12 +569,7 @@ impl Builder { let import_to_add = self .imports_to_add .into_iter() - .filter_map(|import| { - Some(( - import.import_path.display(db, self.edition).to_string(), - import.import_path.segments().last()?.display(db, self.edition).to_string(), - )) - }) + .map(|import| import.import_path.display(db, self.edition).to_string()) .collect(); CompletionItem { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index ac6b1207f2e0..8051d48ca5fe 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,17 +10,13 @@ mod snippet; #[cfg(test)] mod tests; -use ide_db::text_edit::TextEdit; use ide_db::{ - helpers::mod_path_to_ast, - imports::{ - import_assets::NameToImport, - insert_use::{self, ImportScope}, - }, - items_locator, + imports::insert_use::{self, ImportScope}, syntax_helpers::tree_diff::diff, + text_edit::TextEdit, FilePosition, FxHashSet, RootDatabase, }; +use syntax::ast::make; use crate::{ completions::Completions, @@ -272,7 +268,7 @@ pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, - imports: impl IntoIterator, + imports: impl IntoIterator, ) -> Option> { let _p = tracing::info_span!("resolve_completion_edits").entered(); let sema = hir::Semantics::new(db); @@ -289,27 +285,12 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(true); - - imports.into_iter().for_each(|(full_import_path, imported_name)| { - let items_with_name = items_locator::items_with_name( - &sema, - current_crate, - NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocSearchMode::Include, + imports.into_iter().for_each(|full_import_path| { + insert_use::insert_use( + &new_ast, + make::path_from_text_with_edition(&full_import_path, current_edition), + &config.insert_use, ); - let import = items_with_name - .filter_map(|candidate| { - current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg) - }) - .find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path); - if let Some(import_path) = import { - insert_use::insert_use( - &new_ast, - mod_path_to_ast(&import_path, current_edition), - &config.insert_use, - ); - } }); diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 41a3302c0953..e942f5a6aac7 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -672,7 +672,7 @@ impl Analysis { &self, config: &CompletionConfig<'_>, position: FilePosition, - imports: impl IntoIterator + std::panic::UnwindSafe, + imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { Ok(self .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))? diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 190015d7faad..39cbf53eaa21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1154,10 +1154,7 @@ pub(crate) fn handle_completion_resolve( .resolve_completion_edits( &forced_resolve_completions_config, position, - resolve_data - .imports - .into_iter() - .map(|import| (import.full_import_path, import.imported_name)), + resolve_data.imports.into_iter().map(|import| import.full_import_path), )? .into_iter() .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 61ec576dd4f9..ccffa7a671e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -142,9 +142,8 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; hasher.update(prefix); hasher.update(u32::from(*text_size).to_le_bytes()); } - for (import_path, import_name) in &item.import_to_add { + for import_path in &item.import_to_add { hasher.update(import_path); - hasher.update(import_name); } hasher.finalize() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 134de92feab3..ca4372aa83f8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -850,7 +850,6 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, - pub imported_name: String, } #[derive(Debug, Deserialize, Default)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index 2a7d95d560d2..bff53cf98b7b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -394,10 +394,7 @@ fn completion_item( item.import_to_add .clone() .into_iter() - .map(|(import_path, import_name)| lsp_ext::CompletionImport { - full_import_path: import_path, - imported_name: import_name, - }) + .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) .collect() } else { Vec::new() 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 dca231604fa1..ff027ac5848b 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -411,6 +411,11 @@ pub fn path_from_text(text: &str) -> ast::Path { ast_from_text(&format!("fn main() {{ let test: {text}; }}")) } +// FIXME: should not be pub +pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path { + ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition) +} + pub fn use_tree_glob() -> ast::UseTree { ast_from_text("use *;") } @@ -1230,7 +1235,12 @@ pub fn token_tree( #[track_caller] fn ast_from_text(text: &str) -> N { - let parse = SourceFile::parse(text, Edition::CURRENT); + ast_from_text_with_edition(text, Edition::CURRENT) +} + +#[track_caller] +fn ast_from_text_with_edition(text: &str, edition: Edition) -> N { + let parse = SourceFile::parse(text, edition); let node = match parse.tree().syntax().descendants().find_map(N::cast) { Some(it) => it, None => { diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index a632fc6f5fb8..c7ee4e402363 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ {}", outlives.sub.as_usize())?; + } + Ok(()) +} From 052e9b430651f5cb89b511d131d1d7a47a28d215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:13:32 +0000 Subject: [PATCH 42/77] add NLL SCCs to polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 11b00fdd44aa..944b5b1d4a7d 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,6 +1,7 @@ use std::io; use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; @@ -54,6 +55,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the list of polonius localized constraints /// - a mermaid graph of the CFG /// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -101,6 +103,14 @@ fn emit_polonius_dump<'tcx>( writeln!(out, "")?; writeln!(out, "")?; + // Section 4: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + // Finalize the dump with the HTML epilogue. writeln!( out, @@ -343,3 +353,42 @@ fn emit_mermaid_nll_regions<'tcx>( } Ok(()) } + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} From 6bdc2dc3cf7534a8066a8f937698f4a7520b85aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Sun, 26 Jan 2025 21:22:46 +0000 Subject: [PATCH 43/77] tidy up html structure - invert pre/code which was an invalid combination, that works fine in practice - remove unneeded code wrapper for graphs --- compiler/rustc_borrowck/src/polonius/dump.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 944b5b1d4a7d..f71e6f3e6f3a 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -74,7 +74,7 @@ fn emit_polonius_dump<'tcx>( // Section 1: the NLL + Polonius MIR. writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_html_mir(
         tcx,
         body,
@@ -84,15 +84,15 @@ fn emit_polonius_dump<'tcx>(
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 2: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 3: mermaid visualization of the NLL region graph. From a9213c27ad09a97ac769c451bbed78e91c84cab8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:17:03 +0000 Subject: [PATCH 44/77] Deduplicate operand creation between scalars, non-scalars and string patterns --- .../src/builder/matches/test.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 8cca84d7fcc6..ec2a93001360 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -145,6 +145,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); + + let expect_ty = value.ty(); + let expect = self.literal_operand(test.span, value); if let ty::Adt(def, _) = ty.kind() && tcx.is_lang_item(def.did(), LangItem::String) { @@ -173,7 +176,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, + expect, + expect_ty, ref_str, ref_str_ty, ); @@ -185,13 +189,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, + expect, + expect_ty, place, ty, ); } else { - assert_eq!(value.ty(), ty); - let expect = self.literal_operand(test.span, value); + assert_eq!(expect_ty, ty); let val = Operand::Copy(place); self.compare( block, @@ -371,12 +375,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block: BasicBlock, fail_block: BasicBlock, source_info: SourceInfo, - value: Const<'tcx>, + mut expect: Operand<'tcx>, + expect_ty: Ty<'tcx>, mut val: Place<'tcx>, mut ty: Ty<'tcx>, ) { - let mut expect = self.literal_operand(source_info.span, value); - // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. // @@ -391,7 +394,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => None, }; let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty()); + let opt_ref_test_ty = unsize(expect_ty); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {} From e1e2e17d2059a0c5e4d1770d1db30d9cf7bb4b26 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:35:03 +0000 Subject: [PATCH 45/77] Use an operand instead of a place that is always turned into an operand --- .../src/builder/matches/test.rs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index ec2a93001360..f7b0f734b2de 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -178,7 +178,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, expect, expect_ty, - ref_str, + Operand::Copy(ref_str), ref_str_ty, ); } else if !ty.is_scalar() { @@ -191,12 +191,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, expect, expect_ty, - place, + Operand::Copy(place), ty, ); } else { assert_eq!(expect_ty, ty); - let val = Operand::Copy(place); self.compare( block, success_block, @@ -204,7 +203,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, BinOp::Eq, expect, - val, + Operand::Copy(place), ); } } @@ -377,7 +376,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info: SourceInfo, mut expect: Operand<'tcx>, expect_ty: Ty<'tcx>, - mut val: Place<'tcx>, + mut val: Operand<'tcx>, mut ty: Ty<'tcx>, ) { // If we're using `b"..."` as a pattern, we need to insert an @@ -413,11 +412,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PointerCoercion::Unsize, CoercionSource::Implicit, ), - Operand::Copy(val), + val, ty, ), ); - val = temp; + val = Operand::Copy(temp); } if opt_ref_test_ty.is_some() { let slice = self.temp(ty, source_info.span); @@ -473,11 +472,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: method, })), - args: [Spanned { node: Operand::Copy(val), span: DUMMY_SP }, Spanned { - node: expect, - span: DUMMY_SP, - }] - .into(), + args: [Spanned { node: val, span: DUMMY_SP }, Spanned { node: expect, span: DUMMY_SP }] + .into(), destination: eq_result, target: Some(eq_block), unwind: UnwindAction::Continue, From f895e31d590d5c10e8cdb19e042f631e435c683f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Mon, 27 Jan 2025 15:10:42 +0100 Subject: [PATCH 46/77] Fix SIMD codegen tests on LLVM 20 The splat contents are printed differently on LLVM 20. --- .../simd-intrinsic/simd-intrinsic-generic-gather.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-masked-load.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-masked-store.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-scatter.rs | 4 ++-- .../simd-intrinsic/simd-intrinsic-generic-select.rs | 4 ++-- .../codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs | 8 ++++---- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs index 605a0d520a77..7f99f695bf4f 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-gather.rs @@ -23,7 +23,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn gather_f32x2(pointers: Vec2<*const f32>, mask: Vec2, values: Vec2) -> Vec2 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x float> @llvm.masked.gather.v2f32.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x float> {{.*}}) simd_gather(values, pointers, mask) @@ -33,7 +33,7 @@ pub unsafe fn gather_f32x2(pointers: Vec2<*const f32>, mask: Vec2, #[no_mangle] pub unsafe fn gather_pf32x2(pointers: Vec2<*const *const f32>, mask: Vec2, values: Vec2<*const f32>) -> Vec2<*const f32> { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x ptr> @llvm.masked.gather.v2p0.v2p0(<2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]], <2 x ptr> {{.*}}) simd_gather(values, pointers, mask) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs index 015f6fd9cef4..7f46630e920d 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-load.rs @@ -21,7 +21,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn load_f32x2(mask: Vec2, pointer: *const f32, values: Vec2) -> Vec2 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call <2 x float> @llvm.masked.load.v2f32.p0(ptr {{.*}}, i32 4, <2 x i1> [[B]], <2 x float> {{.*}}) simd_masked_load(mask, pointer, values) @@ -31,7 +31,7 @@ pub unsafe fn load_f32x2(mask: Vec2, pointer: *const f32, #[no_mangle] pub unsafe fn load_pf32x4(mask: Vec4, pointer: *const *const f32, values: Vec4<*const f32>) -> Vec4<*const f32> { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: call <4 x ptr> @llvm.masked.load.v4p0.p0(ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]], <4 x ptr> {{.*}}) simd_masked_load(mask, pointer, values) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs index 471a4bea181b..0d43234f1e29 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-masked-store.rs @@ -20,7 +20,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @store_f32x2 #[no_mangle] pub unsafe fn store_f32x2(mask: Vec2, pointer: *mut f32, values: Vec2) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.store.v2f32.p0(<2 x float> {{.*}}, ptr {{.*}}, i32 4, <2 x i1> [[B]]) simd_masked_store(mask, pointer, values) @@ -29,7 +29,7 @@ pub unsafe fn store_f32x2(mask: Vec2, pointer: *mut f32, values: Vec2) // CHECK-LABEL: @store_pf32x4 #[no_mangle] pub unsafe fn store_pf32x4(mask: Vec4, pointer: *mut *const f32, values: Vec4<*const f32>) { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: call void @llvm.masked.store.v4p0.p0(<4 x ptr> {{.*}}, ptr {{.*}}, i32 {{.*}}, <4 x i1> [[B]]) simd_masked_store(mask, pointer, values) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs index 1c42b2534d87..ef7827bd96f0 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-scatter.rs @@ -23,7 +23,7 @@ extern "rust-intrinsic" { #[no_mangle] pub unsafe fn scatter_f32x2(pointers: Vec2<*mut f32>, mask: Vec2, values: Vec2) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.scatter.v2f32.v2p0(<2 x float> {{.*}}, <2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]] simd_scatter(values, pointers, mask) @@ -34,7 +34,7 @@ pub unsafe fn scatter_f32x2(pointers: Vec2<*mut f32>, mask: Vec2, #[no_mangle] pub unsafe fn scatter_pf32x2(pointers: Vec2<*mut *const f32>, mask: Vec2, values: Vec2<*const f32>) { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> {{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: call void @llvm.masked.scatter.v2p0.v2p0(<2 x ptr> {{.*}}, <2 x ptr> {{.*}}, i32 {{.*}}, <2 x i1> [[B]] simd_scatter(values, pointers, mask) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs index a73593160f2e..33ed2b437f9b 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-generic-select.rs @@ -29,7 +29,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @select_m8 #[no_mangle] pub unsafe fn select_m8(m: b8x4, a: f32x4, b: f32x4) -> f32x4 { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i8> %{{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i8> %{{.*}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i8> [[A]] to <4 x i1> // CHECK: select <4 x i1> [[B]] simd_select(m, a, b) @@ -38,7 +38,7 @@ pub unsafe fn select_m8(m: b8x4, a: f32x4, b: f32x4) -> f32x4 { // CHECK-LABEL: @select_m32 #[no_mangle] pub unsafe fn select_m32(m: i32x4, a: f32x4, b: f32x4) -> f32x4 { - // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> %{{.*}}, + // CHECK: [[A:%[0-9]+]] = lshr <4 x i32> %{{.*}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <4 x i32> [[A]] to <4 x i1> // CHECK: select <4 x i1> [[B]] simd_select(m, a, b) diff --git a/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs b/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs index 4df246c2f5c7..92067db9b153 100644 --- a/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs +++ b/tests/codegen/simd-intrinsic/simd-intrinsic-mask-reduce.rs @@ -27,7 +27,7 @@ extern "rust-intrinsic" { // CHECK-LABEL: @reduce_any_32x2 #[no_mangle] pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -37,7 +37,7 @@ pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { // CHECK-LABEL: @reduce_all_32x2 #[no_mangle] pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -47,7 +47,7 @@ pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { // CHECK-LABEL: @reduce_any_8x16 #[no_mangle] pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 @@ -57,7 +57,7 @@ pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { // CHECK-LABEL: @reduce_all_8x16 #[no_mangle] pub unsafe fn reduce_all_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 From cedd3e22a64ad7a1097414f314cf444dbd115949 Mon Sep 17 00:00:00 2001 From: rustbot <47979223+rustbot@users.noreply.github.com> Date: Mon, 27 Jan 2025 12:00:59 -0500 Subject: [PATCH 47/77] Update books --- src/doc/book | 2 +- src/doc/edition-guide | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index 82a4a49789bc..fa312a343fbf 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 82a4a49789bc96db1a1b2a210b4c5ed7c9ef0c0d +Subproject commit fa312a343fbff01bc6cef393e326817f70719813 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656..4ed5a1a4a2a7 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda diff --git a/src/doc/nomicon b/src/doc/nomicon index 625b200e5b33..bc2298865544 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 625b200e5b33a5af35589db0bc454203a3d46d20 +Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948 diff --git a/src/doc/reference b/src/doc/reference index 293af9910037..93b921c7d321 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 293af991003772bdccf2d6b980182d84dd055942 +Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d From fa4589bcebfd995ec421f2ddd2351bc374a8273c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 23 Jan 2025 00:00:00 +0000 Subject: [PATCH 48/77] Locate asan-odr-win with other sanitizer tests --- tests/ui/{asan-odr-win => sanitizer}/asan_odr_windows.rs | 0 tests/ui/{asan-odr-win => sanitizer}/auxiliary/asan_odr_win-2.rs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{asan-odr-win => sanitizer}/asan_odr_windows.rs (100%) rename tests/ui/{asan-odr-win => sanitizer}/auxiliary/asan_odr_win-2.rs (100%) diff --git a/tests/ui/asan-odr-win/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs similarity index 100% rename from tests/ui/asan-odr-win/asan_odr_windows.rs rename to tests/ui/sanitizer/asan_odr_windows.rs diff --git a/tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs b/tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs similarity index 100% rename from tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs rename to tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs From bc135aaa9815557704978ccf9ee15aed49f2f976 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 28 Jan 2025 02:56:32 +0100 Subject: [PATCH 49/77] interpret: is_alloc_live: check global allocs last --- compiler/rustc_const_eval/src/interpret/memory.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0..d736f14f5a38 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -830,9 +830,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is /// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics. pub fn is_alloc_live(&self, id: AllocId) -> bool { - self.tcx.try_get_global_alloc(id).is_some() - || self.memory.alloc_map.contains_key_ref(&id) + self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. + // This also matches the order in `get_alloc_info`. + || self.tcx.try_get_global_alloc(id).is_some() } /// Obtain the size and alignment of an allocation, even if that allocation has From d94b64dcefcdf4ad29e8d5f8240389d9506968e5 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 13 Jan 2025 15:20:36 -0700 Subject: [PATCH 50/77] rustdoc: add nobuild typescript checking to our JS By nobuild, I mean that the type annotations are all in comments, not in the "native" typescript syntax. This is a bit uglier, but it lets you rapid-prototype without tsc, works with all the native browser debugging tools, and keeps Node out of Rust's bootstrap chain. This pull request mostly just adds ts-ignore annotations and type declarations. To actually take good advantage of typescript, we'll want to "burn down" this pile of unsafe code until we eventually have a version with almost none of these. This PR also adds tsc to the mingw-check Dockerfile, so that it can't fall out of date like the Closure annotations did. https://rust-lang.zulipchat.com/#narrow/channel/266220-t-rustdoc/topic/typescript --- .../docker/host-x86_64/mingw-check/Dockerfile | 5 +- src/librustdoc/html/static/js/README.md | 10 +- src/librustdoc/html/static/js/externs.js | 270 ---- src/librustdoc/html/static/js/main.js | 345 ++++- src/librustdoc/html/static/js/rustdoc.d.ts | 387 ++++++ .../html/static/js/scrape-examples.js | 3 + src/librustdoc/html/static/js/search.js | 1201 ++++++++++++----- src/librustdoc/html/static/js/settings.js | 3 + src/librustdoc/html/static/js/src-script.js | 3 + src/librustdoc/html/static/js/storage.js | 98 +- src/librustdoc/html/static/js/tsconfig.json | 15 + 11 files changed, 1658 insertions(+), 682 deletions(-) delete mode 100644 src/librustdoc/html/static/js/externs.js create mode 100644 src/librustdoc/html/static/js/rustdoc.d.ts create mode 100644 src/librustdoc/html/static/js/tsconfig.json diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d408cd518a00..9234c6dc921e 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -29,7 +29,7 @@ ENV PATH="/node/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@6.1.1 eslint@8.6.0 -g +RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -68,4 +68,5 @@ ENV SCRIPT \ es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ - eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \ + tsc --project ../src/librustdoc/html/static/js/tsconfig.json diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md index 1fd859ad7cf4..e99d7330f0ed 100644 --- a/src/librustdoc/html/static/js/README.md +++ b/src/librustdoc/html/static/js/README.md @@ -3,13 +3,9 @@ These JavaScript files are incorporated into the rustdoc binary at build time, and are minified and written to the filesystem as part of the doc build process. -We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler) +We use the [TypeScript Compiler](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) dialect of JSDoc to comment our code and annotate params and return types. To run a check: - ./x.py doc library/std - npm i -g google-closure-compiler - google-closure-compiler -W VERBOSE \ - build//doc/{search-index*.js,crates*.js} \ - src/librustdoc/html/static/js/{search.js,main.js,storage.js} \ - --externs src/librustdoc/html/static/js/externs.js >/dev/null + npm i -g typescript + tsc --project tsconfig.json diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js deleted file mode 100644 index c4faca1c0c3b..000000000000 --- a/src/librustdoc/html/static/js/externs.js +++ /dev/null @@ -1,270 +0,0 @@ -// This file contains type definitions that are processed by the Closure Compiler but are -// not put into the JavaScript we include as part of the documentation. It is used for -// type checking. See README.md in this directory for more info. - -/* eslint-disable */ -let searchState; -function initSearch(searchIndex){} - -/** - * @typedef {{ - * name: string, - * id: number|null, - * fullPath: Array, - * pathWithoutLast: Array, - * pathLast: string, - * generics: Array, - * bindings: Map>, - * }} - */ -let QueryElement; - -/** - * @typedef {{ - * pos: number, - * totalElems: number, - * typeFilter: (null|string), - * userQuery: string, - * isInBinding: (null|string), - * }} - */ -let ParserState; - -/** - * @typedef {{ - * original: string, - * userQuery: string, - * typeFilter: number, - * elems: Array, - * args: Array, - * returned: Array, - * foundElems: number, - * totalElems: number, - * literalSearch: boolean, - * hasReturnArrow: boolean, - * corrections: Array<{from: string, to: integer}> | null, - * typeFingerprint: Uint32Array, - * error: Array | null, - * }} - */ -let ParsedQuery; - -/** - * @typedef {{ - * crate: string, - * desc: string, - * id: number, - * name: string, - * normalizedName: string, - * parent: (Object|null|undefined), - * path: string, - * ty: (Number|null|number), - * type: FunctionSearchType? - * }} - */ -let Row; - -/** - * @typedef {{ - * in_args: Array, - * returned: Array, - * others: Array, - * query: ParsedQuery, - * }} - */ -let ResultsTable; - -/** - * @typedef {Map} - */ -let Results; - -/** - * @typedef {{ - * desc: string, - * displayPath: string, - * fullPath: string, - * href: string, - * id: number, - * lev: number, - * name: string, - * normalizedName: string, - * parent: (Object|undefined), - * path: string, - * ty: number, - * type: FunctionSearchType?, - * displayType: Promise>>|null, - * displayTypeMappedNames: Promise]>>|null, - * }} - */ -let ResultObject; - -/** - * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. - * The JavaScript deserializes this into FunctionSearchType. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * An input or output can be encoded as just a number if there is only one of them, AND - * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had - * a function with a single output, and that output had a single generic: - * - * fn something() -> Result - * - * If output was allowed to be any RawFunctionType, it would look like thi - * - * [[], [50, [3, 3]]] - * - * The problem is that the above output could be interpreted as either a type with ID 50 and two - * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second - * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing - * in favor of the pair of types interpretation. This is why the `(number|Array)` - * is used instead of `(RawFunctionType|Array)`. - * - * The output can be skipped if it's actually unit and there's no type constraints. If thi - * function accepts constrained generics, then the output will be unconditionally emitted, and - * after it will come a list of trait constraints. The position of the item in the list will - * determine which type parameter it is. For example: - * - * [1, 2, 3, 4, 5] - * ^ ^ ^ ^ ^ - * | | | | - generic parameter (-3) of trait 5 - * | | | - generic parameter (-2) of trait 4 - * | | - generic parameter (-1) of trait 3 - * | - this function returns a single value (type 2) - * - this function takes a single input parameter (type 1) - * - * Or, for a less contrived version: - * - * [[[4, -1], 3], [[5, -1]], 11] - * -^^^^^^^---- ^^^^^^^ ^^ - * | | | - generic parameter, roughly `where -1: 11` - * | | | since -1 is the type parameter and 11 the trait - * | | - function output 5<-1> - * | - the overall function signature is something like - * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` - * - function input, corresponds roughly to 4<-1> - * 4 is an index into the `p` array for a type - * -1 is the generic parameter, given by 11 - * - * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like - * function inputs and outputs: - * - * [-1, -1, [4, 3]] - * ^^^^^^ where -1: 4 + 3 - * - * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array - * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in - * favor of `4 + 3`: - * - * [-1, -1, [[4, 3]]] - * ^^^^^^^^ where -1: 4 + 3 - * - * [-1, -1, [5, [4, 3]]] - * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 - * - * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i - * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. - * - * @typedef {( - * 0 | - * [(number|Array)] | - * [(number|Array), (number|Array)] | - * Array<(number|Array)> - * )} - */ -let RawFunctionSearchType; - -/** - * A single function input or output type. This is either a single path ID, or a pair of - * [path ID, generics]. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * @typedef {number | [number, Array]} - */ -let RawFunctionType; - -/** - * @typedef {{ - * inputs: Array, - * output: Array, - * where_clause: Array>, - * }} - */ -let FunctionSearchType; - -/** - * @typedef {{ - * id: (null|number), - * ty: number, - * generics: Array, - * bindings: Map>, - * }} - */ -let FunctionType; - -/** - * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` - * are arrays with the same length. `q`, `a`, and `c` use a sparse - * representation for compactness. - * - * `n[i]` contains the name of an item. - * - * `t[i]` contains the type of that item - * (as a string of characters that represent an offset in `itemTypes`). - * - * `d[i]` contains the description of that item. - * - * `q` contains the full paths of the items. For compactness, it is a set of - * (index, path) pairs used to create a map. If a given index `i` is - * not present, this indicates "same as the last index present". - * - * `i[i]` contains an item's parent, usually a module. For compactness, - * it is a set of indexes into the `p` array. - * - * `f` contains function signatures, or `0` if the item isn't a function. - * More information on how they're encoded can be found in rustc-dev-guide - * - * Functions are themselves encoded as arrays. The first item is a list of - * types representing the function's inputs, and the second list item is a list - * of types representing the function's output. Tuples are flattened. - * Types are also represented as arrays; the first item is an index into the `p` - * array, while the second is a list of types representing any generic parameters. - * - * b[i] contains an item's impl disambiguator. This is only present if an item - * is defined in an impl block and, the impl block's type has more than one associated - * item with the same name. - * - * `a` defines aliases with an Array of pairs: [name, offset], where `offset` - * points into the n/t/d/q/i/f arrays. - * - * `doc` contains the description of the crate. - * - * `p` is a list of path/type pairs. It is used for parents and function parameters. - * The first item is the type, the second is the name, the third is the visible path (if any) and - * the fourth is the canonical path used for deduplication (if any). - * - * `r` is the canonical path used for deduplication of re-exported items. - * It is not used for associated items like methods (that's the fourth element - * of `p`) but is used for modules items like free functions. - * - * `c` is an array of item indices that are deprecated. - * @typedef {{ - * doc: string, - * a: Object, - * n: Array, - * t: string, - * d: Array, - * q: Array<[number, string]>, - * i: Array, - * f: string, - * p: Array<[number, string] | [number, string, number] | [number, string, number, number]>, - * b: Array<[number, String]>, - * c: Array, - * r: Array<[number, number]>, - * }} - */ -let RawSearchIndexCrate; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 984b0877d8de..ccf4002bb300 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -11,8 +11,13 @@ window.RUSTDOC_TOOLTIP_HOVER_MS = 300; window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS = 450; -// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL -// for a resource under the root-path, with the resource-suffix. +/** + * Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL + * for a resource under the root-path, with the resource-suffix. + * + * @param {string} basename + * @param {string} extension + */ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } @@ -27,13 +32,18 @@ function hideMain() { function showMain() { const main = document.getElementById(MAIN_ID); + if (!main) { + return; + } removeClass(main, "hidden"); const mainHeading = main.querySelector(".main-heading"); - if (mainHeading && searchState.rustdocToolbar) { - if (searchState.rustdocToolbar.parentElement) { - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } const toggle = document.getElementById("toggle-all-docs"); if (toggle) { @@ -61,16 +71,20 @@ function setMobileTopbar() { } } -// Gets the human-readable string for the virtual-key code of the -// given KeyboardEvent, ev. -// -// This function is meant as a polyfill for KeyboardEvent#key, -// since it is not supported in IE 11 or Chrome for Android. We also test for -// KeyboardEvent#keyCode because the handleShortcut handler is -// also registered for the keydown event, because Blink doesn't fire -// keypress on hitting the Escape key. -// -// So I guess you could say things are getting pretty interoperable. +/** + * Gets the human-readable string for the virtual-key code of the + * given KeyboardEvent, ev. + * + * This function is meant as a polyfill for KeyboardEvent#key, + * since it is not supported in IE 11 or Chrome for Android. We also test for + * KeyboardEvent#keyCode because the handleShortcut handler is + * also registered for the keydown event, because Blink doesn't fire + * keypress on hitting the Escape key. + * + * So I guess you could say things are getting pretty interoperable. + * + * @param {KeyboardEvent} ev + */ function getVirtualKey(ev) { if ("key" in ev && typeof ev.key !== "undefined") { return ev.key; @@ -110,6 +124,9 @@ function getNakedUrl() { * @param {HTMLElement} referenceNode */ function insertAfter(newNode, referenceNode) { + // You're not allowed to pass an element with no parent. + // I dunno how to make TS's typechecker see that. + // @ts-expect-error referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -129,6 +146,7 @@ function getOrCreateSection(id, classes) { el = document.createElement("section"); el.id = id; el.className = classes; + // @ts-expect-error insertAfter(el, document.getElementById(MAIN_ID)); } return el; @@ -159,12 +177,13 @@ function getNotDisplayedElem() { * contains the displayed element (there can be only one at the same time!). So basically, we switch * elements between the two `
` elements. * - * @param {HTMLElement} elemToDisplay + * @param {HTMLElement|null} elemToDisplay */ function switchDisplayedElement(elemToDisplay) { const el = getAlternativeDisplayElem(); if (el.children.length > 0) { + // @ts-expect-error getNotDisplayedElem().appendChild(el.firstElementChild); } if (elemToDisplay === null) { @@ -177,10 +196,14 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); + // @ts-expect-error if (mainHeading && searchState.rustdocToolbar) { + // @ts-expect-error if (searchState.rustdocToolbar.parentElement) { + // @ts-expect-error searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); } + // @ts-expect-error mainHeading.appendChild(searchState.rustdocToolbar); } } @@ -189,6 +212,7 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } +// @ts-expect-error function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -201,6 +225,7 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); + // @ts-expect-error function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -211,21 +236,25 @@ function preLoadCss(cssUrl) { } if (getSettingsButton()) { + // @ts-expect-error getSettingsButton().onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } + // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); // Sending request for the CSS and the JS files at the same time so it will // hopefully be loaded when the JS will generate the settings content. + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("settings-js")); // Pre-load all theme CSS files, so that switching feels seamless. // // When loading settings.html as a standalone page, the equivalent HTML is // generated in context.rs. setTimeout(() => { + // @ts-expect-error const themes = getVar("themes").split(","); for (const theme of themes) { // if there are no themes, do nothing @@ -241,6 +270,8 @@ function preLoadCss(cssUrl) { window.searchState = { rustdocToolbar: document.querySelector("rustdoc-toolbar"), loadingText: "Loading search results...", + // This will always be an HTMLInputElement, but tsc can't see that + // @ts-expect-error input: document.getElementsByClassName("search-input")[0], outputElement: () => { let el = document.getElementById("search"); @@ -263,31 +294,38 @@ function preLoadCss(cssUrl) { // tab and back preserves the element that was focused. focusedByTab: [null, null, null], clearInputTimeout: () => { - if (searchState.timeout !== null) { - clearTimeout(searchState.timeout); - searchState.timeout = null; + if (window.searchState.timeout !== null) { + clearTimeout(window.searchState.timeout); + window.searchState.timeout = null; } }, - isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, + // @ts-expect-error + isDisplayed: () => { + const outputElement = window.searchState.outputElement(); + return outputElement && + outputElement.parentElement && + outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; + }, // Sets the focus on the search bar at the top of the page focus: () => { - searchState.input.focus(); + window.searchState.input && window.searchState.input.focus(); }, // Removes the focus from the search bar. defocus: () => { - searchState.input.blur(); + window.searchState.input && window.searchState.input.blur(); }, showResults: search => { if (search === null || typeof search === "undefined") { - search = searchState.outputElement(); + search = window.searchState.outputElement(); } switchDisplayedElement(search); - searchState.mouseMovedAfterSearch = false; - document.title = searchState.title; + // @ts-expect-error + window.searchState.mouseMovedAfterSearch = false; + document.title = window.searchState.title; }, removeQueryParameters: () => { // We change the document title. - document.title = searchState.titleBeforeSearch; + document.title = window.searchState.titleBeforeSearch; if (browserSupportsHistoryApi()) { history.replaceState(null, "", getNakedUrl() + window.location.hash); } @@ -295,9 +333,10 @@ function preLoadCss(cssUrl) { hideResults: () => { switchDisplayedElement(null); // We also remove the query parameter from the URL. - searchState.removeQueryParameters(); + window.searchState.removeQueryParameters(); }, getQueryStringParams: () => { + /** @type {Object.} */ const params = {}; window.location.search.substring(1).split("&"). map(s => { @@ -309,26 +348,28 @@ function preLoadCss(cssUrl) { return params; }, setup: () => { - const search_input = searchState.input; - if (!searchState.input) { + const search_input = window.searchState.input; + if (!search_input) { return; } let searchLoaded = false; // If you're browsing the nightly docs, the page might need to be refreshed for the // search to work because the hash of the JS scripts might have changed. function sendSearchForm() { + // @ts-expect-error document.getElementsByClassName("search-form")[0].submit(); } function loadSearch() { if (!searchLoaded) { searchLoaded = true; + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm); loadScript(resourcePath("search-index", ".js"), sendSearchForm); } } search_input.addEventListener("focus", () => { - search_input.origPlaceholder = search_input.placeholder; + window.searchState.origPlaceholder = search_input.placeholder; search_input.placeholder = "Type your search here."; loadSearch(); }); @@ -337,16 +378,21 @@ function preLoadCss(cssUrl) { loadSearch(); } - const params = searchState.getQueryStringParams(); + const params = window.searchState.getQueryStringParams(); if (params.search !== undefined) { - searchState.setLoadingSearch(); + window.searchState.setLoadingSearch(); loadSearch(); } }, setLoadingSearch: () => { - const search = searchState.outputElement(); - search.innerHTML = "

" + searchState.loadingText + "

"; - searchState.showResults(search); + const search = window.searchState.outputElement(); + if (!search) { + return; + } + search.innerHTML = "

" + + window.searchState.loadingText + + "

"; + window.searchState.showResults(search); }, descShards: new Map(), loadDesc: async function({descShard, descIndex}) { @@ -370,6 +416,8 @@ function preLoadCss(cssUrl) { return list[descIndex]; }, loadedDescShard: function(crate, shard, data) { + // If loadedDescShard gets called, then the library must have been declared. + // @ts-expect-error this.descShards.get(crate)[shard].resolve(data.split("\n")); }, }; @@ -377,8 +425,11 @@ function preLoadCss(cssUrl) { const toggleAllDocsId = "toggle-all-docs"; let savedHash = ""; + /** + * @param {HashChangeEvent|null} ev + */ function handleHashes(ev) { - if (ev !== null && searchState.isDisplayed() && ev.newURL) { + if (ev !== null && window.searchState.isDisplayed() && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. switchDisplayedElement(null); @@ -419,6 +470,7 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), + // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -437,12 +489,16 @@ function preLoadCss(cssUrl) { } } + /** + * @param {HashChangeEvent|null} ev + */ function onHashChange(ev) { // If we're in mobile mode, we should hide the sidebar in any case. hideSidebar(); handleHashes(ev); } + // @ts-expect-error function openParentDetails(elem) { while (elem) { if (elem.tagName === "DETAILS") { @@ -452,18 +508,25 @@ function preLoadCss(cssUrl) { } } + // @ts-expect-error function expandSection(id) { openParentDetails(document.getElementById(id)); } + // @ts-expect-error function handleEscape(ev) { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error searchState.hideResults(); ev.preventDefault(); + // @ts-expect-error searchState.defocus(); + // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } + // @ts-expect-error function handleShortcut(ev) { // Don't interfere with browser shortcuts const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; @@ -471,8 +534,11 @@ function preLoadCss(cssUrl) { return; } + // @ts-expect-error if (document.activeElement.tagName === "INPUT" && + // @ts-expect-error document.activeElement.type !== "checkbox" && + // @ts-expect-error document.activeElement.type !== "radio") { switch (getVirtualKey(ev)) { case "Escape": @@ -489,6 +555,7 @@ function preLoadCss(cssUrl) { case "S": case "/": ev.preventDefault(); + // @ts-expect-error searchState.focus(); break; @@ -515,6 +582,7 @@ function preLoadCss(cssUrl) { document.addEventListener("keydown", handleShortcut); function addSidebarItems() { + // @ts-expect-error if (!window.SIDEBAR_ITEMS) { return; } @@ -529,6 +597,7 @@ function preLoadCss(cssUrl) { * "Modules", or "Macros". */ function block(shortty, id, longty) { + // @ts-expect-error const filtered = window.SIDEBAR_ITEMS[shortty]; if (!filtered) { return; @@ -564,7 +633,9 @@ function preLoadCss(cssUrl) { li.appendChild(link); ul.appendChild(li); } + // @ts-expect-error sidebar.appendChild(h3); + // @ts-expect-error sidebar.appendChild(ul); } @@ -600,6 +671,7 @@ function preLoadCss(cssUrl) { } // + // @ts-expect-error window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -615,18 +687,22 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). + // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; } + // @ts-expect-error aliases.split(",").forEach(alias => { inlined_types.add(alias); }); }); } + // @ts-expect-error let currentNbImpls = implementors.getElementsByClassName("impl").length; + // @ts-expect-error const traitName = document.querySelector(".main-heading h1 > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; const libs = Object.getOwnPropertyNames(imp); @@ -636,6 +712,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( + // @ts-expect-error (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { @@ -663,6 +740,7 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); + // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -681,12 +759,15 @@ function preLoadCss(cssUrl) { addClass(display, "impl"); display.appendChild(anchor); display.appendChild(code); + // @ts-expect-error list.appendChild(display); currentNbImpls += 1; } } }; + // @ts-expect-error if (window.pending_implementors) { + // @ts-expect-error window.register_implementors(window.pending_implementors); } @@ -719,12 +800,15 @@ function preLoadCss(cssUrl) { * * - After processing all of the impls, it sorts the sidebar items by name. * - * @param {{[cratename: string]: Array>}} impl + * @param {{[cratename: string]: Array>}} imp */ + // @ts-expect-error window.register_type_impls = imp => { + // @ts-expect-error if (!imp || !imp[window.currentCrate]) { return; } + // @ts-expect-error window.pending_type_impls = null; const idMap = new Map(); @@ -744,6 +828,7 @@ function preLoadCss(cssUrl) { let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); + // @ts-expect-error for (const impList of imp[window.currentCrate]) { const types = impList.slice(2); const text = impList[0]; @@ -772,20 +857,28 @@ function preLoadCss(cssUrl) { h.appendChild(link); trait_implementations = outputList; trait_implementations_header = outputListHeader; + // @ts-expect-error sidebarSection.appendChild(h); sidebarTraitList = document.createElement("ul"); sidebarTraitList.className = "block trait-implementation"; + // @ts-expect-error sidebarSection.appendChild(sidebarTraitList); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } else { implementations = outputList; if (trait_implementations) { + // @ts-expect-error mainContent.insertBefore(outputListHeader, trait_implementations_header); + // @ts-expect-error mainContent.insertBefore(outputList, trait_implementations_header); } else { const mainContent = document.querySelector("#main-content"); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } } @@ -793,6 +886,7 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -800,6 +894,7 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); + // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -817,6 +912,7 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -830,11 +926,14 @@ function preLoadCss(cssUrl) { if (isTrait) { const li = document.createElement("li"); const a = document.createElement("a"); + // @ts-expect-error a.href = `#${template.content.querySelector(".impl").id}`; a.textContent = traitName; li.appendChild(a); + // @ts-expect-error sidebarTraitList.append(li); } else { + // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -856,10 +955,14 @@ function preLoadCss(cssUrl) { const insertionReference = methods || sidebarTraitList; if (insertionReference) { const insertionReferenceH = insertionReference.previousElementSibling; + // @ts-expect-error sidebarSection.insertBefore(blockHeader, insertionReferenceH); + // @ts-expect-error sidebarSection.insertBefore(block, insertionReferenceH); } else { + // @ts-expect-error sidebarSection.appendChild(blockHeader); + // @ts-expect-error sidebarSection.appendChild(block); } if (hasClass(item, "associatedtype")) { @@ -896,11 +999,14 @@ function preLoadCss(cssUrl) { list.replaceChildren(...newChildren); } }; + // @ts-expect-error if (window.pending_type_impls) { + // @ts-expect-error window.register_type_impls(window.pending_type_impls); } function addSidebarCrates() { + // @ts-expect-error if (!window.ALL_CRATES) { return; } @@ -914,6 +1020,7 @@ function preLoadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block crate"; + // @ts-expect-error for (const crate of window.ALL_CRATES) { const link = document.createElement("a"); link.href = window.rootPath + crate + "/index.html"; @@ -933,17 +1040,20 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Summary"; } function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -952,6 +1062,7 @@ function preLoadCss(cssUrl) { e.open = false; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Show all"; } @@ -977,9 +1088,11 @@ function preLoadCss(cssUrl) { const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; + // @ts-expect-error function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { + // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -991,6 +1104,7 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1002,6 +1116,7 @@ function preLoadCss(cssUrl) { }); }()); + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { if (document.querySelector(".rustdoc.src")) { // We are in the source code page, nothing to be done here! @@ -1009,6 +1124,7 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", + // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1027,33 +1143,41 @@ function preLoadCss(cssUrl) { }); }; + // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); }; if (getSettingValue("line-numbers") === "true") { + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } function showSidebar() { + // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error removeClass(sidebar, "shown"); } window.addEventListener("resize", () => { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { // As a workaround to the behavior of `contains: layout` used in doc togglers, // tooltip popovers are positioned using javascript. // // This means when the window is resized, we need to redo the layout. + // @ts-expect-error const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; const force_visible = base.TOOLTIP_FORCE_VISIBLE; hideTooltip(false); @@ -1069,6 +1193,7 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } + // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1079,7 +1204,9 @@ function preLoadCss(cssUrl) { }); }); + // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { + // @ts-expect-error el.addEventListener("click", e => { if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { e.preventDefault(); @@ -1090,15 +1217,17 @@ function preLoadCss(cssUrl) { /** * Show a tooltip immediately. * - * @param {DOMElement} e - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} e - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. */ function showTooltip(e) { const notable_ty = e.getAttribute("data-notable-ty"); + // @ts-expect-error if (!window.NOTABLE_TRAITS && notable_ty) { const data = document.getElementById("notable-traits-data"); if (data) { + // @ts-expect-error window.NOTABLE_TRAITS = JSON.parse(data.innerText); } else { throw new Error("showTooltip() called with notable without any notable traits!"); @@ -1106,36 +1235,44 @@ function preLoadCss(cssUrl) { } // Make this function idempotent. If the tooltip is already shown, avoid doing extra work // and leave it alone. + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } + // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { wrapper.innerHTML = "
" + + // @ts-expect-error window.NOTABLE_TRAITS[notable_ty] + "
"; } else { // Replace any `title` attribute with `data-title` to avoid double tooltips. - if (e.getAttribute("title") !== null) { - e.setAttribute("data-title", e.getAttribute("title")); + const ttl = e.getAttribute("title"); + if (ttl !== null) { + e.setAttribute("data-title", ttl); e.removeAttribute("title"); } - if (e.getAttribute("data-title") !== null) { + const dttl = e.getAttribute("data-title"); + if (dttl !== null) { const titleContent = document.createElement("div"); titleContent.className = "content"; - titleContent.appendChild(document.createTextNode(e.getAttribute("data-title"))); + titleContent.appendChild(document.createTextNode(dttl)); wrapper.appendChild(titleContent); } } wrapper.className = "tooltip popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); + // @ts-expect-error focusCatcher.onfocus = hideTooltip; wrapper.appendChild(focusCatcher); const pos = e.getBoundingClientRect(); // 5px overlap so that the mouse can easily travel from place to place wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px"; + // @ts-expect-error wrapper.style.left = 0; wrapper.style.right = "auto"; wrapper.style.visibility = "hidden"; @@ -1152,8 +1289,11 @@ function preLoadCss(cssUrl) { ); } wrapper.style.visibility = ""; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = wrapper; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); wrapper.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. @@ -1164,7 +1304,7 @@ function preLoadCss(cssUrl) { }; wrapper.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. - if (ev.pointerType !== "mouse") { + if (ev.pointerType !== "mouse" || !(ev.relatedTarget instanceof HTMLElement)) { return; } if (!e.TOOLTIP_FORCE_VISIBLE && !e.contains(ev.relatedTarget)) { @@ -1180,23 +1320,27 @@ function preLoadCss(cssUrl) { * was called, that timeout gets cleared. If the tooltip is already in the requested state, * this function will still clear any pending timeout, but otherwise do nothing. * - * @param {DOMElement} element - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} element - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. * @param {boolean} show - If true, the tooltip will be made visible. If false, it will * be hidden. */ function setTooltipHoverTimeout(element, show) { clearTooltipHoverTimeout(element); + // @ts-expect-error if (!show && !window.CURRENT_TOOLTIP_ELEMENT) { // To "hide" an already hidden element, just cancel its timeout. return; } + // @ts-expect-error if (show && window.CURRENT_TOOLTIP_ELEMENT) { // To "show" an already visible element, just cancel its timeout. return; } + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE !== element) { // Don't do anything if another tooltip is already visible. return; @@ -1214,22 +1358,29 @@ function preLoadCss(cssUrl) { * If a show/hide timeout was set by `setTooltipHoverTimeout`, cancel it. If none exists, * do nothing. * - * @param {DOMElement} element - The tooltip's anchor point, - * as passed to `setTooltipHoverTimeout`. + * @param {HTMLElement} element - The tooltip's anchor point, + * as passed to `setTooltipHoverTimeout`. */ function clearTooltipHoverTimeout(element) { if (element.TOOLTIP_HOVER_TIMEOUT !== undefined) { + // @ts-expect-error removeClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); clearTimeout(element.TOOLTIP_HOVER_TIMEOUT); delete element.TOOLTIP_HOVER_TIMEOUT; } } + // @ts-expect-error function tooltipBlurHandler(event) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget) ) { // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. @@ -1251,32 +1402,45 @@ function preLoadCss(cssUrl) { * If set to `false`, leave keyboard focus alone. */ function hideTooltip(focus) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { if (focus) { + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); } + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } + // @ts-expect-error document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = null; } } + // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) { hideTooltip(true); } else { showTooltip(e); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.focus(); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; + // @ts-expect-error e.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1284,6 +1448,7 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointermove = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1291,12 +1456,15 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } + // @ts-expect-error if (!e.TOOLTIP_FORCE_VISIBLE && window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)) { // Tooltip pointer leave gesture: // @@ -1329,6 +1497,7 @@ function preLoadCss(cssUrl) { // * https://www.nngroup.com/articles/tooltip-guidelines/ // * https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown setTooltipHoverTimeout(e, false); + // @ts-expect-error addClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); } }; @@ -1338,6 +1507,7 @@ function preLoadCss(cssUrl) { if (sidebar_menu_toggle) { sidebar_menu_toggle.addEventListener("click", () => { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error if (!hasClass(sidebar, "shown")) { showSidebar(); } else { @@ -1346,12 +1516,18 @@ function preLoadCss(cssUrl) { }); } + // @ts-expect-error function helpBlurHandler(event) { + // @ts-expect-error if (!getHelpButton().contains(document.activeElement) && + // @ts-expect-error !getHelpButton().contains(event.relatedTarget) && + // @ts-expect-error !getSettingsButton().contains(document.activeElement) && + // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { + // @ts-expect-error window.hidePopoverMenus(); } } @@ -1427,14 +1603,18 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (isHelpPage) { const help_section = document.createElement("section"); help_section.appendChild(container); + // @ts-expect-error document.getElementById("main-content").appendChild(help_section); container.style.display = "block"; } else { const help_button = getHelpButton(); + // @ts-expect-error help_button.appendChild(container); container.onblur = helpBlurHandler; + // @ts-expect-error help_button.onblur = helpBlurHandler; + // @ts-expect-error help_button.children[0].onblur = helpBlurHandler; } @@ -1446,8 +1626,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * Pass "true" to reset focus for tooltip popovers. */ + // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); + // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1455,7 +1637,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm /** * Hide all the popover menus. */ + // @ts-expect-error window.hidePopoverMenus = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1474,10 +1658,12 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * @return {HTMLElement} */ function getHelpMenu(buildNeeded) { + // @ts-expect-error let menu = getHelpButton().querySelector(".popover"); if (!menu && buildNeeded) { menu = buildHelpMenu(); } + // @ts-expect-error return menu; } @@ -1489,9 +1675,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // other modals. const button = getHelpButton(); addClass(button, "help-open"); + // @ts-expect-error button.querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { + // @ts-expect-error window.hideAllModals(); menu.style.display = ""; } @@ -1506,8 +1694,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // If user clicks with a moderator, though, use default browser behavior, // probably opening in a new window or tab. if (!helpLink.contains(helpLink) || + // @ts-expect-error event.ctrlKey || + // @ts-expect-error event.altKey || + // @ts-expect-error event.metaKey) { return; } @@ -1517,6 +1708,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (shouldShowHelp) { showHelp(); } else { + // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1527,6 +1719,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm addSidebarCrates(); onHashChange(null); window.addEventListener("hashchange", onHashChange); + // @ts-expect-error searchState.setup(); }()); @@ -1580,6 +1773,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); if (document.querySelector(".rustdoc.src")) { + // @ts-expect-error window.rustdocToggleSrcSidebar(); } e.preventDefault(); @@ -1589,6 +1783,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Pointer capture. // // Resizing is a single-pointer gesture. Any secondary pointer is ignored + // @ts-expect-error let currentPointerId = null; // "Desired" sidebar size. @@ -1596,6 +1791,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // This is stashed here for window resizing. If the sidebar gets // shrunk to maintain BODY_MIN, and then the user grows the window again, // it gets the sidebar to restore its size. + // @ts-expect-error let desiredSidebarSize = null; // Sidebar resize debouncer. @@ -1626,7 +1822,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // through that size when using the shrink-to-nothing gesture. function hideSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocCloseSourceSidebar(); + // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1639,14 +1837,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } } @@ -1659,6 +1862,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // remains visible all the time on there. function showSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); @@ -1674,6 +1878,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm */ function changeSidebarSize(size) { if (isSrcPage) { + // @ts-expect-error updateLocalStorage("src-sidebar-width", size); // [RUSTDOCIMPL] CSS variable fast path // @@ -1681,11 +1886,16 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. + // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", size); + // @ts-expect-error sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } } @@ -1701,7 +1911,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Respond to the resize handle event. // This function enforces size constraints, and implements the // shrink-to-nothing gesture based on thresholds defined above. + // @ts-expect-error function resize(e) { + // @ts-expect-error if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1719,15 +1931,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { + // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } + // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { + // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", + // @ts-expect-error desiredSidebarSize + "px", ); }, 100); @@ -1739,51 +1955,69 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } stopResize(); + // @ts-expect-error if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); + // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { + // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); + // @ts-expect-error function stopResize(e) { + // @ts-expect-error if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } + // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; + // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); + // @ts-expect-error if (resizer.releasePointerCapture) { + // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } } + // @ts-expect-error function initResize(e) { + // @ts-expect-error if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } + // @ts-expect-error if (resizer.setPointerCapture) { + // @ts-expect-error resizer.setPointerCapture(e.pointerId); + // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead + // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } + // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); + // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); + // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; @@ -1795,6 +2029,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. + // @ts-expect-error function copyContentToClipboard(content) { const el = document.createElement("textarea"); el.value = content; @@ -1809,6 +2044,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.body.removeChild(el); } + // @ts-expect-error function copyButtonAnimation(button) { button.classList.add("clicked"); @@ -1831,6 +2067,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) + // @ts-expect-error const title = document.querySelector("title").textContent.replace(" - Rust", ""); const [item, module] = title.split(" in "); const path = [item]; @@ -1843,6 +2080,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }; // Copy buttons on code examples. + // @ts-expect-error function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -1851,6 +2089,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyContentToClipboard(codeElem.textContent); } + // @ts-expect-error function getExampleWrap(event) { let elem = event.target; while (!hasClass(elem, "example-wrap")) { @@ -1866,6 +2105,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return elem; } + // @ts-expect-error function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1896,9 +2136,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } const scrapedWrapped = elem.parentElement; + // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } + // @ts-expect-error function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1917,6 +2159,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm buttons.classList.toggle("keep-visible"); } + // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts new file mode 100644 index 000000000000..18a3e22113b8 --- /dev/null +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -0,0 +1,387 @@ +// This file contains type definitions that are processed by the TypeScript Compiler but are +// not put into the JavaScript we include as part of the documentation. It is used for +// type checking. See README.md in this directory for more info. + +/* eslint-disable */ +declare global { + interface Window { + /** Make the current theme easy to find */ + currentTheme: HTMLLinkElement|null; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_MS: number; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_EXIT_MS: number; + /** Search engine data used by main.js and search.js */ + searchState: rustdoc.SearchState; + /** Global option, with a long list of "../"'s */ + rootPath: string|null; + /** + * Currently opened crate. + * As a multi-page application, we know this never changes once set. + */ + currentCrate: string|null; + } + interface HTMLElement { + /** Used by the popover tooltip code. */ + TOOLTIP_FORCE_VISIBLE: boolean|undefined, + /** Used by the popover tooltip code */ + TOOLTIP_HOVER_TIMEOUT: Timeout|undefined, + } +} + +export = rustdoc; + +declare namespace rustdoc { + interface SearchState { + rustdocToolbar: HTMLElement|null; + loadingText: string; + input: HTMLInputElement|null; + title: string; + titleBeforeSearch: string; + timeout: number|null; + currentTab: number; + focusedByTab: [number|null, number|null, number|null]; + clearInputTimeout: function; + outputElement: function(): HTMLElement|null; + focus: function(); + defocus: function(); + showResults: function(HTMLElement|null|undefined); + removeQueryParameters: function(); + hideResults: function(); + getQueryStringParams: function(): Object.; + origPlaceholder: string; + setup: function(); + setLoadingSearch: function(); + descShards: Map; + loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise; + loadedDescShard: function(string, number, string); + isDisplayed: function(): boolean, + } + + interface SearchDescShard { + crate: string; + promise: Promise|null; + resolve: function(string[])|null; + shard: number; + } + + /** + * A single parsed "atom" in a search query. For example, + * + * std::fmt::Formatter, Write -> Result<()> + * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ + * ┃ │ ┗ QueryElement { ┊ + * ┃ │ name: Result ┊ + * ┃ │ generics: [ ┊ + * ┃ │ QueryElement ┘ + * ┃ │ name: () + * ┃ │ ] + * ┃ │ } + * ┃ └ QueryElement { + * ┃ name: Write + * ┃ } + * ┗ QueryElement { + * name: Formatter + * pathWithoutLast: std::fmt + * } + */ + interface QueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + typeFilter: number|null, + } + + /** + * Same as QueryElement, but bindings and typeFilter support strings + */ + interface ParserQueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + bindingName: {name: string, generics: ParserQueryElement[]}|null, + typeFilter: string|null, + } + + /** + * Intermediate parser state. Discarded when parsing is done. + */ + interface ParserState { + pos: number; + length: number; + totalElems: number; + genericsElems: number; + typeFilter: (null|string); + userQuery: string; + isInBinding: (null|{name: string, generics: ParserQueryElement[]}); + } + + /** + * A complete parsed query. + */ + interface ParsedQuery { + userQuery: string, + elems: Array, + returned: Array, + foundElems: number, + totalElems: number, + literalSearch: boolean, + hasReturnArrow: boolean, + correction: string|null, + proposeCorrectionFrom: string|null, + proposeCorrectionTo: string|null, + typeFingerprint: Uint32Array, + error: Array | null, + } + + /** + * An entry in the search index database. + */ + interface Row { + crate: string, + descShard: SearchDescShard, + id: number, + name: string, + normalizedName: string, + word: string, + parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), + path: string, + ty: number, + type?: FunctionSearchType + } + + /** + * The viewmodel for the search engine results page. + */ + interface ResultsTable { + in_args: Array, + returned: Array, + others: Array, + query: ParsedQuery, + } + + type Results = Map; + + /** + * An annotated `Row`, used in the viewmodel. + */ + interface ResultObject { + desc: string, + displayPath: string, + fullPath: string, + href: string, + id: number, + dist: number, + path_dist: number, + name: string, + normalizedName: string, + word: string, + index: number, + parent: (Object|undefined), + path: string, + ty: number, + type?: FunctionSearchType, + paramNames?: string[], + displayType: Promise>>|null, + displayTypeMappedNames: Promise]>>|null, + item: Row, + dontValidate?: boolean, + } + + /** + * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * An input or output can be encoded as just a number if there is only one of them, AND + * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had + * a function with a single output, and that output had a single generic: + * + * fn something() -> Result + * + * If output was allowed to be any RawFunctionType, it would look like thi + * + * [[], [50, [3, 3]]] + * + * The problem is that the above output could be interpreted as either a type with ID 50 and two + * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second + * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing + * in favor of the pair of types interpretation. This is why the `(number|Array)` + * is used instead of `(RawFunctionType|Array)`. + * + * The output can be skipped if it's actually unit and there's no type constraints. If thi + * function accepts constrained generics, then the output will be unconditionally emitted, and + * after it will come a list of trait constraints. The position of the item in the list will + * determine which type parameter it is. For example: + * + * [1, 2, 3, 4, 5] + * ^ ^ ^ ^ ^ + * | | | | - generic parameter (-3) of trait 5 + * | | | - generic parameter (-2) of trait 4 + * | | - generic parameter (-1) of trait 3 + * | - this function returns a single value (type 2) + * - this function takes a single input parameter (type 1) + * + * Or, for a less contrived version: + * + * [[[4, -1], 3], [[5, -1]], 11] + * -^^^^^^^---- ^^^^^^^ ^^ + * | | | - generic parameter, roughly `where -1: 11` + * | | | since -1 is the type parameter and 11 the trait + * | | - function output 5<-1> + * | - the overall function signature is something like + * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` + * - function input, corresponds roughly to 4<-1> + * 4 is an index into the `p` array for a type + * -1 is the generic parameter, given by 11 + * + * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like + * function inputs and outputs: + * + * [-1, -1, [4, 3]] + * ^^^^^^ where -1: 4 + 3 + * + * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array + * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in + * favor of `4 + 3`: + * + * [-1, -1, [[4, 3]]] + * ^^^^^^^^ where -1: 4 + 3 + * + * [-1, -1, [5, [4, 3]]] + * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 + * + * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i + * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. + */ + type RawFunctionSearchType = + 0 | + [(number|Array)] | + [(number|Array), (number|Array)] | + Array<(number|Array)> + ; + + /** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + */ + type RawFunctionType = number | [number, Array]; + + /** + * The type signature entry in the decoded search index. + * (The "Raw" objects are encoded differently to save space in the JSON). + */ + interface FunctionSearchType { + inputs: Array, + output: Array, + where_clause: Array>, + } + + /** + * A decoded function type, made from real objects. + * `ty` will be negative for generics, positive for types, and 0 for placeholders. + */ + interface FunctionType { + id: null|number, + ty: number|null, + name?: string, + path: string|null, + exactPath: string|null, + unboxFlag: boolean, + generics: Array, + bindings: Map>, + }; + + interface HighlightedFunctionType extends FunctionType { + generics: HighlightedFunctionType[], + bindings: Map, + highlighted?: boolean; + } + + interface FingerprintableType { + id: number|null; + generics: FingerprintableType[]; + bindings: Map; + }; + + /** + * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` + * are arrays with the same length. `q`, `a`, and `c` use a sparse + * representation for compactness. + * + * `n[i]` contains the name of an item. + * + * `t[i]` contains the type of that item + * (as a string of characters that represent an offset in `itemTypes`). + * + * `d[i]` contains the description of that item. + * + * `q` contains the full paths of the items. For compactness, it is a set of + * (index, path) pairs used to create a map. If a given index `i` is + * not present, this indicates "same as the last index present". + * + * `i[i]` contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * `f` contains function signatures, or `0` if the item isn't a function. + * More information on how they're encoded can be found in rustc-dev-guide + * + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. + * + * b[i] contains an item's impl disambiguator. This is only present if an item + * is defined in an impl block and, the impl block's type has more than one associated + * item with the same name. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a list of path/type pairs. It is used for parents and function parameters. + * The first item is the type, the second is the name, the third is the visible path (if any) and + * the fourth is the canonical path used for deduplication (if any). + * + * `r` is the canonical path used for deduplication of re-exported items. + * It is not used for associated items like methods (that's the fourth element + * of `p`) but is used for modules items like free functions. + * + * `c` is an array of item indices that are deprecated. + */ + type RawSearchIndexCrate = { + doc: string, + a: Object, + n: Array, + t: string, + D: string, + e: string, + q: Array<[number, string]>, + i: string, + f: string, + p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, + b: Array<[number, String]>, + c: string, + r: Array<[number, number]>, + P: Array<[number, string]>, + }; + + type VlqData = VlqData[] | number; +} diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 98c53b8656f5..d08f15a5bfa8 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,5 +1,8 @@ /* global addClass, hasClass, removeClass, onEachLazy */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 660484c133c3..1ad32721e068 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -10,11 +10,19 @@ if (!Array.prototype.toSpliced) { // Can't use arrow functions, because we want `this` Array.prototype.toSpliced = function() { const me = this.slice(); + // @ts-expect-error Array.prototype.splice.apply(me, arguments); return me; }; } +/** + * + * @template T + * @param {Iterable} arr + * @param {function(T): any} func + * @param {function(T): boolean} funcBtwn + */ function onEachBtwn(arr, func, funcBtwn) { let skipped = true; for (const value of arr) { @@ -98,9 +106,24 @@ const NO_TYPE_FILTER = -1; * documentation. */ const editDistanceState = { + /** + * @type {number[]} + */ current: [], + /** + * @type {number[]} + */ prev: [], + /** + * @type {number[]} + */ prevPrev: [], + /** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ calculate: function calculate(a, b, limit) { // Ensure that `b` is the shorter string, minimizing memory use. if (a.length < b.length) { @@ -186,14 +209,28 @@ const editDistanceState = { }, }; +/** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ function editDistance(a, b, limit) { return editDistanceState.calculate(a, b, limit); } +/** + * @param {string} c + * @returns {boolean} + */ function isEndCharacter(c) { return "=,>-])".indexOf(c) !== -1; } +/** + * @param {number} ty + * @returns + */ function isFnLikeTy(ty) { return ty === TY_FN || ty === TY_METHOD || ty === TY_TYMETHOD; } @@ -212,7 +249,7 @@ function isSeparatorCharacter(c) { /** * Returns `true` if the current parser position is starting with "->". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -223,7 +260,7 @@ function isReturnArrow(parserState) { /** * Increase current parser position until it doesn't find a whitespace anymore. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState */ function skipWhitespace(parserState) { while (parserState.pos < parserState.userQuery.length) { @@ -238,7 +275,7 @@ function skipWhitespace(parserState) { /** * Returns `true` if the previous character is `lookingFor`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * @param {String} lookingFor * * @return {boolean} @@ -260,8 +297,8 @@ function prevIs(parserState, lookingFor) { /** * Returns `true` if the last element in the `elems` argument has generics. * - * @param {Array} elems - * @param {ParserState} parserState + * @param {Array} elems + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -270,6 +307,13 @@ function isLastElemGeneric(elems, parserState) { prevIs(parserState, ">"); } +/** + * + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {rustdoc.ParserQueryElement[]} elems + * @param {boolean} isInGenerics + */ function getFilteredNextElem(query, parserState, elems, isInGenerics) { const start = parserState.pos; if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { @@ -294,6 +338,8 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); checkExtraTypeFilterCharacters(start, parserState); + // typeFilterElem is not null. If it was, the elems.length check would have fired. + // @ts-expect-error parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; parserState.totalElems -= 1; @@ -309,12 +355,13 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { * If there is no `endChar`, this function will implicitly stop at the end * without raising an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. - * @param {string} endChar - This function will stop when it'll encounter this - * character. - * @returns {{foundSeparator: bool}} + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. + * @param {string} endChar - This function will stop when it'll encounter this + * character. + * @returns {{foundSeparator: boolean}} */ function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; @@ -385,6 +432,7 @@ function getItemsBefore(query, parserState, elems, endChar) { throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { + /** @type {string[]} */ let extra = []; if (isLastElemGeneric(query.elems, parserState)) { extra = [" after ", ">"]; @@ -463,12 +511,14 @@ function getItemsBefore(query, parserState, elems, endChar) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. * @param {boolean} isInGenerics */ function getNextElem(query, parserState, elems, isInGenerics) { + /** @type {rustdoc.ParserQueryElement[]} */ const generics = []; skipWhitespace(parserState); @@ -588,6 +638,7 @@ function getNextElem(query, parserState, elems, isInGenerics) { getFilteredNextElem(query, parserState, generics, isInGenerics); generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { + // @ts-expect-error generics.push(makePrimitiveElement(null, { bindingName: makePrimitiveElement("output"), typeFilter: null, @@ -640,7 +691,8 @@ function getNextElem(query, parserState, elems, isInGenerics) { * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored * if empty). * - * @param {ParserState} parserState + * @param {number} start + * @param {rustdoc.ParserState} parserState */ function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery.slice(start, parserState.pos).trim(); @@ -658,12 +710,13 @@ function checkExtraTypeFilterCharacters(start, parserState) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {string} name - Name of the query element. - * @param {Array} generics - List of generics of this query element. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {string} name - Name of the query element. + * @param {Array} generics - List of generics of this query element. + * @param {boolean} isInGenerics * - * @return {QueryElement} - The newly created `QueryElement`. + * @return {rustdoc.ParserQueryElement} - The newly created `QueryElement`. */ function createQueryElement(query, parserState, name, generics, isInGenerics) { const path = name.trim(); @@ -756,9 +809,15 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { }; } +/** + * + * @param {string} name + * @param {Object=} extra + * @returns {rustdoc.ParserQueryElement} + */ function makePrimitiveElement(name, extra) { return Object.assign({ - name, + name: name, id: null, fullPath: [name], pathWithoutLast: [], @@ -781,8 +840,8 @@ function makePrimitiveElement(name, extra) { * * There is more than one element. * * There is no closing `"`. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState * @param {boolean} isInGenerics */ function getStringElem(query, parserState, isInGenerics) { @@ -813,9 +872,9 @@ function getStringElem(query, parserState, isInGenerics) { * character or the end of the query. It returns the position of the last * character of the ident. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * - * @return {integer} + * @return {number} */ function getIdentEndPosition(parserState) { let afterIdent = consumeIdent(parserState); @@ -890,6 +949,10 @@ function getIdentEndPosition(parserState) { return end; } +/** + * @param {string} c + * @returns + */ function isSpecialStartCharacter(c) { return "<\"".indexOf(c) !== -1; } @@ -897,7 +960,7 @@ function isSpecialStartCharacter(c) { /** * Returns `true` if the current parser position is starting with "::". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -909,7 +972,7 @@ function isPathStart(parserState) { * If the current parser position is at the beginning of an identifier, * move the position to the end of it and return `true`. Otherwise, return `false`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -935,14 +998,25 @@ function isPathSeparator(c) { return c === ":" || c === " "; } +/** + * @template T + */ class VlqHexDecoder { + /** + * @param {string} string + * @param {function(rustdoc.VlqData): T} cons + */ constructor(string, cons) { this.string = string; this.cons = cons; this.offset = 0; + /** @type {T[]} */ this.backrefQueue = []; } - // call after consuming `{` + /** + * call after consuming `{` + * @returns {rustdoc.VlqData[]} + */ decodeList() { let c = this.string.charCodeAt(this.offset); const ret = []; @@ -953,7 +1027,10 @@ class VlqHexDecoder { this.offset += 1; // eat cb return ret; } - // consumes and returns a list or integer + /** + * consumes and returns a list or integer + * @returns {rustdoc.VlqData} + */ decode() { let n = 0; let c = this.string.charCodeAt(this.offset); @@ -972,6 +1049,9 @@ class VlqHexDecoder { this.offset += 1; return sign ? -value : value; } + /** + * @returns {T} + */ next() { const c = this.string.charCodeAt(this.offset); // sixteen characters after "0" are backref @@ -994,6 +1074,7 @@ class VlqHexDecoder { } } class RoaringBitmap { + /** @param {string} str */ constructor(str) { // https://github.com/RoaringBitmap/RoaringFormatSpec // @@ -1063,6 +1144,7 @@ class RoaringBitmap { } } } + /** @param {number} keyvalue */ contains(keyvalue) { const key = keyvalue >> 16; const value = keyvalue & 0xFFFF; @@ -1091,10 +1173,15 @@ class RoaringBitmap { } class RoaringBitmapRun { + /** + * @param {number} runcount + * @param {Uint8Array} array + */ constructor(runcount, array) { this.runcount = runcount; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1120,10 +1207,15 @@ class RoaringBitmapRun { } } class RoaringBitmapArray { + /** + * @param {number} cardinality + * @param {Uint8Array} array + */ constructor(cardinality, array) { this.cardinality = cardinality; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1148,9 +1240,13 @@ class RoaringBitmapArray { } } class RoaringBitmapBits { + /** + * @param {Uint8Array} array + */ constructor(array) { this.array = array; } + /** @param {number} value */ contains(value) { return !!(this.array[value >> 3] & (1 << (value & 7))); } @@ -1176,9 +1272,9 @@ class RoaringBitmapBits { * matches * : A list of search index IDs for this node. * - * @typedef {{ - * children: [NameTrie], - * matches: [number], + * @type {{ + * children: NameTrie[], + * matches: number[], * }} */ class NameTrie { @@ -1186,9 +1282,20 @@ class NameTrie { this.children = []; this.matches = []; } + /** + * @param {string} name + * @param {number} id + * @param {Map} tailTable + */ insert(name, id, tailTable) { this.insertSubstring(name, 0, id, tailTable); } + /** + * @param {string} name + * @param {number} substart + * @param {number} id + * @param {Map} tailTable + */ insertSubstring(name, substart, id, tailTable) { const l = name.length; if (substart === l) { @@ -1201,10 +1308,13 @@ class NameTrie { } else { child = new NameTrie(); this.children[sb] = child; + /** @type {NameTrie[]} */ let sste; if (substart >= 2) { const tail = name.substring(substart - 2, substart + 1); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error sste = tailTable.get(tail); } else { sste = []; @@ -1216,6 +1326,10 @@ class NameTrie { child.insertSubstring(name, substart + 1, id, tailTable); } } + /** + * @param {string} name + * @param {Map} tailTable + */ search(name, tailTable) { const results = new Set(); this.searchSubstringPrefix(name, 0, results); @@ -1226,6 +1340,8 @@ class NameTrie { this.searchLev(name, 0, levParams, results); const tail = name.substring(0, 3); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error for (const entry of tailTable.get(tail)) { entry.searchSubstringPrefix(name, 3, results); } @@ -1233,6 +1349,11 @@ class NameTrie { } return [...results]; } + /** + * @param {string} name + * @param {number} substart + * @param {Set} results + */ searchSubstringPrefix(name, substart, results) { const l = name.length; if (substart === l) { @@ -1240,14 +1361,18 @@ class NameTrie { results.add(match); } // breadth-first traversal orders prefix matches by length + /** @type {NameTrie[]} */ let unprocessedChildren = []; for (const child of this.children) { if (child) { unprocessedChildren.push(child); } } + /** @type {NameTrie[]} */ let nextSet = []; while (unprocessedChildren.length !== 0) { + /** @type {NameTrie} */ + // @ts-expect-error const next = unprocessedChildren.pop(); for (const child of next.children) { if (child) { @@ -1270,10 +1395,18 @@ class NameTrie { } } } + /** + * @param {string} name + * @param {number} substart + * @param {Lev2TParametricDescription|Lev1TParametricDescription} levParams + * @param {Set} results + */ searchLev(name, substart, levParams, results) { const stack = [[this, 0]]; const n = levParams.n; while (stack.length !== 0) { + // It's not empty + //@ts-expect-error const [trie, levState] = stack.pop(); for (const [charCode, child] of trie.children.entries()) { if (!child) { @@ -1305,6 +1438,11 @@ class NameTrie { } class DocSearch { + /** + * @param {Map} rawSearchIndex + * @param {string} rootPath + * @param {rustdoc.SearchState} searchState + */ constructor(rawSearchIndex, rootPath, searchState) { /** * @type {Map} @@ -1317,19 +1455,19 @@ class DocSearch { /** * @type {Uint32Array} */ - this.functionTypeFingerprint = null; + this.functionTypeFingerprint = new Uint32Array(0); /** * Map from normalized type names to integers. Used to make type search * more efficient. * - * @type {Map} + * @type {Map} */ this.typeNameIdMap = new Map(); /** * Map from type ID to associated type name. Used for display, * not for search. * - * @type {Map} + * @type {Map} */ this.assocTypeIdNameMap = new Map(); this.ALIASES = new Map(); @@ -1338,64 +1476,88 @@ class DocSearch { /** * Special type name IDs for searching by array. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArray = this.buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfHof = this.buildTypeMapIndex("->"); /** * Special type name IDs the output assoc type. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); /** * Special type name IDs for searching by reference. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Map>} + * @type {Map>} */ this.EMPTY_BINDINGS_MAP = new Map(); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Array} + * @type {Array} */ this.EMPTY_GENERICS_ARRAY = []; @@ -1403,7 +1565,7 @@ class DocSearch { * Object pool for function types with no bindings or generics. * This is reset after loading the index. * - * @type {Map} + * @type {Map} */ this.TYPES_POOL = new Map(); @@ -1422,8 +1584,9 @@ class DocSearch { this.tailTable = new Map(); /** - * @type {Array} + * @type {Array} */ + // @ts-expect-error this.searchIndex = this.buildIndex(rawSearchIndex); } @@ -1436,9 +1599,9 @@ class DocSearch { * get the same ID. * * @param {string} name - * @param {boolean} isAssocType - True if this is an assoc type + * @param {boolean=} isAssocType - True if this is an assoc type * - * @returns {integer} + * @returns {number?} */ buildTypeMapIndex(name, isAssocType) { if (name === "" || name === null) { @@ -1446,12 +1609,14 @@ class DocSearch { } if (this.typeNameIdMap.has(name)) { + /** @type {{id: number, assocOnly: boolean}} */ + // @ts-expect-error const obj = this.typeNameIdMap.get(name); - obj.assocOnly = isAssocType && obj.assocOnly; + obj.assocOnly = !!(isAssocType && obj.assocOnly); return obj.id; } else { const id = this.typeNameIdMap.size; - this.typeNameIdMap.set(name, { id, assocOnly: isAssocType }); + this.typeNameIdMap.set(name, { id, assocOnly: !!isAssocType }); return id; } } @@ -1469,13 +1634,26 @@ class DocSearch { * The format for individual function types is encoded in * librustdoc/html/render/mod.rs: impl Serialize for RenderType * - * @param {null|Array} types - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {null|Array} types + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths * - * @return {Array} + * @return {Array} */ buildItemSearchTypeAll(types, paths, lowercasePaths) { - return types.length > 0 ? + return types && types.length > 0 ? types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) : this.EMPTY_GENERICS_ARRAY; } @@ -1483,7 +1661,22 @@ class DocSearch { /** * Converts a single type. * - * @param {RawFunctionType} type + * @param {rustdoc.RawFunctionType} type + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths + * @param {boolean=} isAssocType */ buildItemSearchType(type, paths, lowercasePaths, isAssocType) { const PATH_INDEX_DATA = 0; @@ -1501,7 +1694,9 @@ class DocSearch { paths, lowercasePaths, ); + // @ts-expect-error if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { + // @ts-expect-error bindings = new Map(type[BINDINGS_DATA].map(binding => { const [assocType, constraints] = binding; // Associated type constructors are represented sloppily in rustdoc's @@ -1524,7 +1719,7 @@ class DocSearch { } } /** - * @type {FunctionType} + * @type {rustdoc.FunctionType} */ let result; if (pathIndex < 0) { @@ -1555,7 +1750,7 @@ class DocSearch { } else { const item = lowercasePaths[pathIndex - 1]; const id = this.buildTypeMapIndex(item.name, isAssocType); - if (isAssocType) { + if (isAssocType && id !== null) { this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name); } result = { @@ -1585,6 +1780,7 @@ class DocSearch { if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { let ok = true; for (const [k, v] of cr.bindings.entries()) { + // @ts-expect-error const v2 = result.bindings.get(v); if (!v2) { ok = false; @@ -1629,7 +1825,7 @@ class DocSearch { * [^1]: Distance is the relatively naive metric of counting the number of distinct items in * the function that are not present in the query. * - * @param {FunctionType|QueryElement} type - a single type + * @param {rustdoc.FingerprintableType} type - a single type * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits */ buildFunctionTypeFingerprint(type, output) { @@ -1647,9 +1843,12 @@ class DocSearch { input === this.typeNameIdOfFnOnce) { input = this.typeNameIdOfHof; } - // http://burtleburtle.net/bob/hash/integer.html - // ~~ is toInt32. It's used before adding, so - // the number stays in safe integer range. + /** + * http://burtleburtle.net/bob/hash/integer.html + * ~~ is toInt32. It's used before adding, so + * the number stays in safe integer range. + * @param {number} k + */ const hashint1 = k => { k = (~~k + 0x7ed55d16) + (k << 12); k = (k ^ 0xc761c23c) ^ (k >>> 19); @@ -1658,6 +1857,7 @@ class DocSearch { k = (~~k + 0xfd7046c5) + (k << 3); return (k ^ 0xb55a4f09) ^ (k >>> 16); }; + /** @param {number} k */ const hashint2 = k => { k = ~k + (k << 15); k ^= k >>> 12; @@ -1684,6 +1884,14 @@ class DocSearch { for (const g of type.generics) { this.buildFunctionTypeFingerprint(g, output); } + /** + * @type {{ + * id: number|null, + * ty: number, + * generics: rustdoc.FingerprintableType[], + * bindings: Map + * }} + */ const fb = { id: null, ty: 0, @@ -1700,7 +1908,7 @@ class DocSearch { /** * Convert raw search index into in-memory search index. * - * @param {[string, RawSearchIndexCrate][]} rawSearchIndex + * @param {Map} rawSearchIndex */ buildIndex(rawSearchIndex) { /** @@ -1714,19 +1922,37 @@ class DocSearch { * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {Array<{name: string, ty: number}>} paths - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} lowercasePaths * - * @return {null|FunctionSearchType} + * @return {function(rustdoc.RawFunctionSearchType): null|rustdoc.FunctionSearchType} */ const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => { - return functionSearchType => { + /** + * @param {rustdoc.RawFunctionSearchType} functionSearchType + */ + const cb = functionSearchType => { if (functionSearchType === 0) { return null; } const INPUTS_DATA = 0; const OUTPUT_DATA = 1; - let inputs, output; + /** @type {rustdoc.FunctionType[]} */ + let inputs; + /** @type {rustdoc.FunctionType[]} */ + let output; if (typeof functionSearchType[INPUTS_DATA] === "number") { inputs = [ this.buildItemSearchType( @@ -1753,6 +1979,7 @@ class DocSearch { ]; } else { output = this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[OUTPUT_DATA], paths, lowercasePaths, @@ -1765,8 +1992,10 @@ class DocSearch { const l = functionSearchType.length; for (let i = 2; i < l; ++i) { where_clause.push(typeof functionSearchType[i] === "number" + // @ts-expect-error ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)] : this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[i], paths, lowercasePaths, @@ -1776,6 +2005,7 @@ class DocSearch { inputs, output, where_clause, }; }; + return cb; }; const searchIndex = []; @@ -1798,7 +2028,12 @@ class DocSearch { for (const [crate, crateCorpus] of rawSearchIndex) { // a string representing the lengths of each description shard // a string representing the list of function types - const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => { + /** @type {number} */ + // @ts-expect-error + const n = noop; + return n; + }); let descShard = { crate, shard: 0, @@ -1817,7 +2052,7 @@ class DocSearch { /** * List of generic function type parameter names. * Used for display, not for searching. - * @type {[string]} + * @type {string[]} */ let lastParamNames = []; @@ -1847,6 +2082,8 @@ class DocSearch { id += 1; searchIndex.push(crateRow); currentIndex += 1; + // it's not undefined + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(0)) { descIndex += 1; } @@ -1870,7 +2107,7 @@ class DocSearch { const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] - const paths = crateCorpus.p; + const rawPaths = crateCorpus.p; // an array of [(String) alias name // [Number] index to items] const aliases = crateCorpus.a; @@ -1879,33 +2116,61 @@ class DocSearch { // an item whose index is not present will fall back to the previous present path const itemParamNames = new Map(crateCorpus.P); - // an array of [{name: String, ty: Number}] + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ const lowercasePaths = []; + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ + const paths = []; // a string representing the list of function types const itemFunctionDecoder = new VlqHexDecoder( crateCorpus.f, + // @ts-expect-error buildFunctionSearchTypeCallback(paths, lowercasePaths), ); // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode - let len = paths.length; + let len = rawPaths.length; let lastPath = itemPaths.get(0); for (let i = 0; i < len; ++i) { - const elem = paths[i]; + const elem = rawPaths[i]; const ty = elem[0]; const name = elem[1]; let path = null; if (elem.length > 2 && elem[2] !== null) { + // @ts-expect-error path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; lastPath = path; } - const exactPath = elem.length > 3 && elem[3] !== null ? + let exactPath = elem.length > 3 && elem[3] !== null ? + // @ts-expect-error itemPaths.get(elem[3]) : path; const unboxFlag = elem.length > 4 && !!elem[4]; + if (path === undefined) { + path = null; + } + if (exactPath === undefined) { + exactPath = null; + } + lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); paths[i] = { ty, name, path, exactPath, unboxFlag }; } @@ -1924,6 +2189,7 @@ class DocSearch { for (let i = 0; i < len; ++i) { const bitIndex = i + 1; if (descIndex >= descShard.len && + // @ts-expect-error !this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descShard = { crate, @@ -1938,8 +2204,11 @@ class DocSearch { } const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); + /** @type {string} */ + // @ts-expect-error const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; const paramNames = itemParamNames.has(i) ? + // @ts-expect-error itemParamNames.get(i).split(",") : lastParamNames; const type = itemFunctionDecoder.next(); @@ -1971,7 +2240,9 @@ class DocSearch { descShard, descIndex, exactPath: itemReexports.has(i) ? + // @ts-expect-error itemPaths.get(itemReexports.get(i)) : path, + // @ts-expect-error parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, type, paramNames, @@ -1987,6 +2258,7 @@ class DocSearch { searchIndex.push(row); lastPath = row.path; lastParamNames = row.paramNames; + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descIndex += 1; } @@ -2002,13 +2274,16 @@ class DocSearch { continue; } + // @ts-expect-error let currentNameAliases; if (currentCrateAliases.has(alias_name)) { currentNameAliases = currentCrateAliases.get(alias_name); } else { currentNameAliases = []; + // @ts-expect-error currentCrateAliases.set(alias_name, currentNameAliases); } + // @ts-expect-error for (const local_alias of aliases[alias_name]) { currentNameAliases.push(local_alias + currentIndex); } @@ -2030,11 +2305,15 @@ class DocSearch { * * When adding new things to the parser, add them there, too! * - * @param {string} val - The user query + * @param {string} userQuery - The user query * - * @return {ParsedQuery} - The parsed query + * @return {rustdoc.ParsedQuery} - The parsed query */ static parseQuery(userQuery) { + /** + * @param {string} typename + * @returns {number} + */ function itemTypeFromName(typename) { const index = itemTypes.findIndex(i => i === typename); if (index < 0) { @@ -2043,14 +2322,19 @@ class DocSearch { return index; } + /** + * @param {rustdoc.ParserQueryElement} elem + */ function convertTypeFilterOnElem(elem) { if (elem.typeFilter !== null) { let typeFilter = elem.typeFilter; if (typeFilter === "const") { typeFilter = "constant"; } + // @ts-expect-error elem.typeFilter = itemTypeFromName(typeFilter); } else { + // @ts-expect-error elem.typeFilter = NO_TYPE_FILTER; } for (const elem2 of elem.generics) { @@ -2068,7 +2352,7 @@ class DocSearch { * * @param {string} userQuery * - * @return {ParsedQuery} + * @return {rustdoc.ParsedQuery} */ function newParsedQuery(userQuery) { return { @@ -2094,8 +2378,8 @@ class DocSearch { * Parses the provided `query` input to fill `parserState`. If it encounters an error while * parsing `query`, it'll throw an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState */ function parseInput(query, parserState) { let foundStopChar = true; @@ -2125,6 +2409,7 @@ class DocSearch { if (!foundStopChar) { let extra = ""; if (isLastElemGeneric(query.elems, parserState)) { + // @ts-expect-error extra = [" after ", ">"]; } else if (prevIs(parserState, "\"")) { throw ["Cannot have more than one element if you use quotes"]; @@ -2208,6 +2493,8 @@ class DocSearch { } } catch (err) { query = newParsedQuery(userQuery); + // is string list + // @ts-expect-error query.error = err; return query; } @@ -2224,25 +2511,167 @@ class DocSearch { /** * Executes the parsed query and builds a {ResultsTable}. * - * @param {ParsedQuery} parsedQuery - The parsed user query - * @param {Object} [filterCrates] - Crate to search in if defined - * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * @param {rustdoc.ParsedQuery} origParsedQuery + * - The parsed user query + * @param {Object} [filterCrates] - Crate to search in if defined + * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher * - * @return {ResultsTable} + * @return {Promise} */ - async execQuery(parsedQuery, filterCrates, currentCrate) { + async execQuery(origParsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); + /** @type {rustdoc.ParsedQuery} */ + // @ts-expect-error + const parsedQuery = origParsedQuery; + + const queryLen = + parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + + parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); + const maxEditDistance = Math.floor(queryLen / 3); + + /** + * @type {Map} + */ + const genericSymbols = new Map(); + + /** + * Convert names to ids in parsed query elements. + * This is not used for the "In Names" tab, but is used for the + * "In Params", "In Returns", and "In Function Signature" tabs. + * + * If there is no matching item, but a close-enough match, this + * function also that correction. + * + * See `buildTypeMapIndex` for more information. + * + * @param {rustdoc.QueryElement} elem + * @param {boolean} isAssocType + */ + const convertNameToId = (elem, isAssocType) => { + const loweredName = elem.pathLast.toLowerCase(); + if (this.typeNameIdMap.has(loweredName) && + // @ts-expect-error + (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { + // @ts-expect-error + elem.id = this.typeNameIdMap.get(loweredName).id; + } else if (!parsedQuery.literalSearch) { + let match = null; + let matchDist = maxEditDistance + 1; + let matchName = ""; + for (const [name, { id, assocOnly }] of this.typeNameIdMap) { + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); + if (dist <= matchDist && dist <= maxEditDistance && + (isAssocType || !assocOnly)) { + if (dist === matchDist && matchName > name) { + continue; + } + match = id; + matchDist = dist; + matchName = name; + } + } + if (match !== null) { + parsedQuery.correction = matchName; + } + elem.id = match; + } + if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 + && elem.generics.length === 0 && elem.bindings.size === 0) + || elem.typeFilter === TY_GENERIC) { + if (genericSymbols.has(elem.normalizedPathLast)) { + // @ts-expect-error + elem.id = genericSymbols.get(elem.normalizedPathLast); + } else { + elem.id = -(genericSymbols.size + 1); + genericSymbols.set(elem.normalizedPathLast, elem.id); + } + if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { + // Silly heuristic to catch if the user probably meant + // to not write a generic parameter. We don't use it, + // just bring it up. + const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); + let matchDist = maxPartDistance + 1; + let matchName = ""; + for (const name of this.typeNameIdMap.keys()) { + const dist = editDistance( + name, + elem.normalizedPathLast, + maxPartDistance, + ); + if (dist <= matchDist && dist <= maxPartDistance) { + if (dist === matchDist && matchName > name) { + continue; + } + matchDist = dist; + matchName = name; + } + } + if (matchName !== "") { + parsedQuery.proposeCorrectionFrom = elem.name; + parsedQuery.proposeCorrectionTo = matchName; + } + } + elem.typeFilter = TY_GENERIC; + } + if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { + // Rust does not have HKT + parsedQuery.error = [ + "Generic type parameter ", + elem.name, + " does not accept generic parameters", + ]; + } + for (const elem2 of elem.generics) { + // @ts-expect-error + convertNameToId(elem2); + } + elem.bindings = new Map(Array.from(elem.bindings.entries()) + .map(entry => { + const [name, constraints] = entry; + // @ts-expect-error + if (!this.typeNameIdMap.has(name)) { + parsedQuery.error = [ + "Type parameter ", + // @ts-expect-error + name, + " does not exist", + ]; + return [0, []]; + } + for (const elem2 of constraints) { + convertNameToId(elem2, false); + } + + // @ts-expect-error + return [this.typeNameIdMap.get(name).id, constraints]; + }), + ); + }; + + for (const elem of parsedQuery.elems) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + for (const elem of parsedQuery.returned) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + + /** * Creates the query results. * - * @param {Array} results_in_args - * @param {Array} results_returned - * @param {Array} results_others - * @param {ParsedQuery} parsedQuery + * @param {Array} results_in_args + * @param {Array} results_returned + * @param {Array} results_others + * @param {rustdoc.ParsedQuery} parsedQuery * - * @return {ResultsTable} + * @return {rustdoc.ResultsTable} */ function createQueryResults( results_in_args, @@ -2257,6 +2686,7 @@ class DocSearch { }; } + // @ts-expect-error const buildHrefAndPath = item => { let displayPath; let href; @@ -2320,6 +2750,7 @@ class DocSearch { return [displayPath, href, `${exactPath}::${name}`]; }; + // @ts-expect-error function pathSplitter(path) { const tmp = "" + path.replace(/::/g, "::"); if (tmp.endsWith("")) { @@ -2332,10 +2763,9 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[ResultObject]} results + * @param {[rustdoc.ResultObject]} results * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns {[ResultObject]} + * @returns {[rustdoc.ResultObject]} */ const transformResults = (results, typeInfo) => { const duplicates = new Set(); @@ -2350,7 +2780,9 @@ class DocSearch { }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. + // @ts-expect-error obj.fullPath = res[2] + "|" + obj.ty; + // @ts-expect-error if (duplicates.has(obj.fullPath)) { continue; } @@ -2363,14 +2795,18 @@ class DocSearch { if (duplicates.has(res[2] + "|" + TY_IMPORT)) { continue; } + // @ts-expect-error duplicates.add(obj.fullPath); duplicates.add(res[2]); if (typeInfo !== null) { + // @ts-expect-error obj.displayTypeSignature = + // @ts-expect-error this.formatDisplayTypeSignature(obj, typeInfo); } + // @ts-expect-error obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2378,6 +2814,7 @@ class DocSearch { } } } + // @ts-expect-error return out; }; @@ -2388,30 +2825,34 @@ class DocSearch { * The output is formatted as an array of hunks, where odd numbered * hunks are highlighted and even numbered ones are not. * - * @param {ResultObject} obj + * @param {rustdoc.ResultObject} obj * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns Promise< + * @returns {Promise<{ * "type": Array, * "mappedNames": Map, * "whereClause": Map>, - * > + * }>} */ this.formatDisplayTypeSignature = async(obj, typeInfo) => { + const objType = obj.type; + if (!objType) { + return {type: [], mappedNames: new Map(), whereClause: new Map()}; + } let fnInputs = null; let fnOutput = null; + // @ts-expect-error let mgens = null; if (typeInfo !== "elems" && typeInfo !== "returned") { fnInputs = unifyFunctionTypes( - obj.type.inputs, + objType.inputs, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensScratch => { fnOutput = unifyFunctionTypes( - obj.type.output, + objType.output, parsedQuery.returned, - obj.type.where_clause, + objType.where_clause, mgensScratch, mgensOut => { mgens = mgensOut; @@ -2424,11 +2865,11 @@ class DocSearch { 0, ); } else { - const arr = typeInfo === "elems" ? obj.type.inputs : obj.type.output; + const arr = typeInfo === "elems" ? objType.inputs : objType.output; const highlighted = unifyFunctionTypes( arr, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensOut => { mgens = mgensOut; @@ -2443,15 +2884,16 @@ class DocSearch { } } if (!fnInputs) { - fnInputs = obj.type.inputs; + fnInputs = objType.inputs; } if (!fnOutput) { - fnOutput = obj.type.output; + fnOutput = objType.output; } const mappedNames = new Map(); const whereClause = new Map(); - const fnParamNames = obj.paramNames; + const fnParamNames = obj.paramNames || []; + // @ts-expect-error const queryParamNames = []; /** * Recursively writes a map of IDs to query generic names, @@ -2461,10 +2903,10 @@ class DocSearch { * mapping `(-1, "X")`, and the writeFn function looks up the entry * for -1 to form the final, user-visible mapping of "X is T". * - * @param {QueryElement} queryElem + * @param {rustdoc.QueryElement} queryElem */ const remapQuery = queryElem => { - if (queryElem.id < 0) { + if (queryElem.id !== null && queryElem.id < 0) { queryParamNames[-1 - queryElem.id] = queryElem.name; } if (queryElem.generics.length > 0) { @@ -2483,7 +2925,7 @@ class DocSearch { * Index 0 is not highlighted, index 1 is highlighted, * index 2 is not highlighted, etc. * - * @param {{name: string, highlighted: bool|undefined}} fnType - input + * @param {{name?: string, highlighted?: boolean}} fnType - input * @param {[string]} result */ const pushText = (fnType, result) => { @@ -2496,6 +2938,7 @@ class DocSearch { // needs coerced to a boolean. if (!!(result.length % 2) === !!fnType.highlighted) { result.push(""); + // @ts-expect-error } else if (result.length === 0 && !!fnType.highlighted) { result.push(""); result.push(""); @@ -2508,7 +2951,7 @@ class DocSearch { * Write a higher order function type: either a function pointer * or a trait bound on Fn, FnMut, or FnOnce. * - * @param {FunctionType} fnType - input + * @param {rustdoc.HighlightedFunctionType} fnType - input * @param {[string]} result */ const writeHof = (fnType, result) => { @@ -2548,7 +2991,7 @@ class DocSearch { * Write a primitive type with special syntax, like `!` or `[T]`. * Returns `false` if the supplied type isn't special. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeSpecialPrimitive = (fnType, result) => { @@ -2563,6 +3006,7 @@ class DocSearch { onEachBtwn( fnType.generics, nested => writeFn(nested, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); pushText({ name: sb, highlighted: fnType.highlighted }, result); @@ -2573,9 +3017,10 @@ class DocSearch { onEachBtwn( fnType.generics, value => { - prevHighlighted = value.highlighted; + prevHighlighted = !!value.highlighted; writeFn(value, result); }, + // @ts-expect-error value => pushText({ name: " ", highlighted: prevHighlighted && value.highlighted, @@ -2593,25 +3038,27 @@ class DocSearch { * like slices, with their own formatting. It also handles * updating the where clause and generic type param map. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeFn = (fnType, result) => { - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (fnParamNames[-1 - fnType.id] === "") { // Normally, there's no need to shown an unhighlighted // where clause, but if it's impl Trait, then we do. const generics = fnType.generics.length > 0 ? fnType.generics : - obj.type.where_clause[-1 - fnType.id]; + objType.where_clause[-1 - fnType.id]; for (const nested of generics) { writeFn(nested, result); } return; + // @ts-expect-error } else if (mgens) { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { mappedNames.set( + // @ts-expect-error queryParamNames[-1 - queryId], fnParamNames[-1 - fnType.id], ); @@ -2622,13 +3069,17 @@ class DocSearch { name: fnParamNames[-1 - fnType.id], highlighted: !!fnType.highlighted, }, result); + // @ts-expect-error const where = []; onEachBtwn( fnType.generics, + // @ts-expect-error nested => writeFn(nested, where), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, where), ); if (where.length > 0) { + // @ts-expect-error whereClause.set(fnParamNames[-1 - fnType.id], where); } } else { @@ -2650,12 +3101,15 @@ class DocSearch { fnType.bindings, ([key, values]) => { const name = this.assocTypeIdNameMap.get(key); + // @ts-expect-error if (values.length === 1 && values[0].id < 0 && + // @ts-expect-error `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) { // the internal `Item=Iterator::Item` type variable should be // shown in the where clause and name mapping output, but is // redundant in this spot for (const value of values) { + // @ts-expect-error writeFn(value, []); } return true; @@ -2672,12 +3126,14 @@ class DocSearch { onEachBtwn( values || [], value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, result), ); if (values.length !== 1) { pushText({ name: ")", highlighted: false }, result); } }, + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); } @@ -2687,6 +3143,7 @@ class DocSearch { onEachBtwn( fnType.generics, value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); if (hasBindings || fnType.generics.length > 0) { @@ -2694,19 +3151,26 @@ class DocSearch { } } }; + // @ts-expect-error const type = []; onEachBtwn( fnInputs, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error pushText({ name: " -> ", highlighted: false }, type); onEachBtwn( fnOutput, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error return {type, mappedNames, whereClause}; }; @@ -2714,10 +3178,10 @@ class DocSearch { * This function takes a result map, and sorts it by various criteria, including edit * distance, substring match, and the crate it comes from. * - * @param {Results} results - * @param {boolean} isType + * @param {rustdoc.Results} results + * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {string} preferredCrate - * @returns {Promise<[ResultObject]>} + * @returns {Promise<[rustdoc.ResultObject]>} */ const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; @@ -2733,12 +3197,12 @@ class DocSearch { // we are doing a return-type based search, // deprioritize "clone-like" results, // ie. functions that also take the queried type as an argument. - const hasType = result.item && result.item.type; - if (!hasType) { + const resultItemType = result.item && result.item.type; + if (!resultItemType) { continue; } - const inputs = result.item.type.inputs; - const where_clause = result.item.type.where_clause; + const inputs = resultItemType.inputs; + const where_clause = resultItemType.where_clause; if (containsTypeFromQuery(inputs, where_clause)) { result.path_dist *= 100; result.dist *= 100; @@ -2748,35 +3212,38 @@ class DocSearch { } result_list.sort((aaa, bbb) => { - let a, b; + /** @type {number} */ + let a; + /** @type {number} */ + let b; // sort by exact case-sensitive match if (isMixedCase) { - a = (aaa.item.name !== userQuery); - b = (bbb.item.name !== userQuery); + a = Number(aaa.item.name !== userQuery); + b = Number(bbb.item.name !== userQuery); if (a !== b) { return a - b; } } // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== normalizedUserQuery); - b = (bbb.word !== normalizedUserQuery); + a = Number(aaa.word !== normalizedUserQuery); + b = Number(bbb.word !== normalizedUserQuery); if (a !== b) { return a - b; } // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); + a = Number(aaa.index < 0); + b = Number(bbb.index < 0); if (a !== b) { return a - b; } // in type based search, put functions first if (parsedQuery.hasReturnArrow) { - a = !isFnLikeTy(aaa.item.ty); - b = !isFnLikeTy(bbb.item.ty); + a = Number(!isFnLikeTy(aaa.item.ty)); + b = Number(!isFnLikeTy(bbb.item.ty)); if (a !== b) { return a - b; } @@ -2784,80 +3251,93 @@ class DocSearch { // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) - a = aaa.path_dist; - b = bbb.path_dist; + a = Number(aaa.path_dist); + b = Number(bbb.path_dist); if (a !== b) { return a - b; } // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; + a = Number(aaa.index); + b = Number(bbb.index); if (a !== b) { return a - b; } // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) - a = (aaa.dist); - b = (bbb.dist); + a = Number(aaa.dist); + b = Number(bbb.dist); if (a !== b) { return a - b; } // sort deprecated items later - a = this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by crate (current crate comes first) - a = (aaa.item.crate !== preferredCrate); - b = (bbb.item.crate !== preferredCrate); + a = Number(aaa.item.crate !== preferredCrate); + b = Number(bbb.item.crate !== preferredCrate); if (a !== b) { return a - b; } // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; + a = Number(aaa.word.length); + b = Number(bbb.word.length); if (a !== b) { return a - b; } // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { - return (a > b ? +1 : -1); + let aw = aaa.word; + let bw = bbb.word; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // sort by description (no description goes later) - a = this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; + a = Number(aaa.item.ty); + b = Number(bbb.item.ty); if (a !== b) { return a - b; } // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { - return (a > b ? +1 : -1); + aw = aaa.item.path; + bw = bbb.item.path; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // que sera, sera return 0; }); + // @ts-expect-error return transformResults(result_list, typeInfo); }; @@ -2871,17 +3351,19 @@ class DocSearch { * then this function will try with a different solution, or bail with null if it * runs out of candidates. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} fnTypesIn - The objects to check. + * @param {rustdoc.QueryElement[]} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map query generics to function generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map?): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyFunctionTypes( fnTypesIn, @@ -2895,7 +3377,7 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { @@ -2915,7 +3397,11 @@ class DocSearch { if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (mgens && mgens.has(queryElem.id) && mgens.get(queryElem.id) !== fnType.id) { continue; @@ -2959,8 +3445,10 @@ class DocSearch { )) { continue; } + // @ts-expect-error if (fnType.id < 0) { const highlightedGenerics = unifyFunctionTypes( + // @ts-expect-error whereClause[(-fnType.id) - 1], queryElems, whereClause, @@ -2998,12 +3486,13 @@ class DocSearch { } } } + // @ts-expect-error return false; } // Multiple element recursive case /** - * @type Array + * @type {Array} */ const fnTypes = fnTypesIn.slice(); /** @@ -3033,7 +3522,7 @@ class DocSearch { continue; } let mgensScratch; - if (fnType.id < 0) { + if (fnType.id !== null && queryElem.id !== null && fnType.id < 0) { mgensScratch = new Map(mgens); if (mgensScratch.has(queryElem.id) && mgensScratch.get(queryElem.id) !== fnType.id) { @@ -3052,8 +3541,11 @@ class DocSearch { if (!queryElemsTmp) { queryElemsTmp = queryElems.slice(0, qlast); } + /** @type {rustdoc.HighlightedFunctionType[]|null} */ let unifiedGenerics = []; + // @ts-expect-error let unifiedGenericsMgens = null; + /** @type {rustdoc.HighlightedFunctionType[]|null} */ const passesUnification = unifyFunctionTypes( fnTypes, queryElemsTmp, @@ -3102,11 +3594,14 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, queryElem.bindings.has(k) ? unifyFunctionTypes( v, + // @ts-expect-error queryElem.bindings.get(k), whereClause, + // @ts-expect-error unifiedGenericsMgens, solutionCb, unboxingDepth, + // @ts-expect-error ) : unifiedGenerics.splice(0, v.length)]; })), }); @@ -3128,7 +3623,7 @@ class DocSearch { )) { continue; } - const generics = fnType.id < 0 ? + const generics = fnType.id !== null && fnType.id < 0 ? whereClause[(-fnType.id) - 1] : fnType.generics; const bindings = fnType.bindings ? @@ -3171,17 +3666,19 @@ class DocSearch { * `Vec` of `Allocators` and not the implicit `Allocator` parameter that every * `Vec` has. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Array} fnTypesIn - The objects to check. + * @param {Array} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map functions generics to query generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyGenericTypes( fnTypesIn, @@ -3195,10 +3692,11 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { + // @ts-expect-error return solutionCb(mgens) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { @@ -3207,7 +3705,11 @@ class DocSearch { const fnType = fnTypesIn[0]; const queryElem = queryElems[0]; if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (!mgens || !mgens.has(queryElem.id) || mgens.get(queryElem.id) === fnType.id ) { @@ -3238,6 +3740,7 @@ class DocSearch { queryElems.slice(1), whereClause, mgens, + // @ts-expect-error mgensScratch => { const solution = unifyFunctionTypeCheckBindings( fnType, @@ -3285,7 +3788,7 @@ class DocSearch { unboxingDepth + 1, )) { let highlightedRemaining; - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { // Where clause corresponds to `F: A + B` // ^^^^^ // The order of the constraints doesn't matter, so @@ -3295,6 +3798,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3316,6 +3820,7 @@ class DocSearch { highlighted: true, }, fnType, { generics: highlightedGenerics, + // @ts-expect-error }), ...highlightedRemaining]; } } else { @@ -3327,6 +3832,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3349,6 +3855,7 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, highlightedGenerics.splice(0, v.length)]; })), + // @ts-expect-error }), ...highlightedRemaining]; } } @@ -3364,8 +3871,8 @@ class DocSearch { * or associated type bindings: that's not load-bearing, but it prevents unnecessary * backtracking later. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ @@ -3377,7 +3884,7 @@ class DocSearch { // fnType.id < 0 means generic // queryElem.id < 0 does too // mgensIn[queryElem.id] = fnType.id - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && fnType.id < 0 && queryElem.id !== null && queryElem.id < 0) { if ( mgensIn && mgensIn.has(queryElem.id) && mgensIn.get(queryElem.id) !== fnType.id @@ -3456,13 +3963,15 @@ class DocSearch { * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were * the type instead. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} mgensIn - Map query generics to function generics. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth - * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} + * @returns {false|{ + * mgens: [Map|null], simplifiedGenerics: rustdoc.FunctionType[] + * }} */ function unifyFunctionTypeCheckBindings( fnType, @@ -3486,8 +3995,10 @@ class DocSearch { } const fnTypeBindings = fnType.bindings.get(name); mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { + // @ts-expect-error const newSolutions = []; unifyFunctionTypes( + // @ts-expect-error fnTypeBindings, constraints, whereClause, @@ -3500,6 +4011,7 @@ class DocSearch { }, unboxingDepth, ); + // @ts-expect-error return newSolutions; }); } @@ -3519,14 +4031,15 @@ class DocSearch { } else { simplifiedGenerics = binds; } + // @ts-expect-error return { simplifiedGenerics, mgens: mgensSolutionSet }; } return { simplifiedGenerics, mgens: [mgensIn] }; } /** - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth * @returns {boolean} @@ -3541,7 +4054,7 @@ class DocSearch { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (!whereClause) { return false; } @@ -3577,14 +4090,14 @@ class DocSearch { * This function checks if the given list contains any * (non-generic) types mentioned in the query. * - * @param {Array} list - A list of function types. - * @param {[FunctionType]} where_clause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list - A list of function types. + * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. */ function containsTypeFromQuery(list, where_clause) { if (!list) return false; for (const ty of parsedQuery.returned) { // negative type ids are generics - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3592,7 +4105,7 @@ class DocSearch { } } for (const ty of parsedQuery.elems) { - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3606,9 +4119,9 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Array} list - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. * @param {number} unboxingDepth * @@ -3627,18 +4140,20 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Row} row - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} row + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ + // @ts-expect-error const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + if (row.id !== null && elem.id !== null && + row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && row.generics.length === 0 && elem.generics.length === 0 && row.bindings.size === 0 && elem.bindings.size === 0 && // special case @@ -3648,6 +4163,7 @@ class DocSearch { ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { + // @ts-expect-error return unifyFunctionTypes( [row], [elem], @@ -3662,6 +4178,7 @@ class DocSearch { /** * Check a query solution for conflicting generics. */ + // @ts-expect-error const checkTypeMgensForConflict = mgens => { if (!mgens) { return true; @@ -3679,7 +4196,7 @@ class DocSearch { /** * Compute an "edit distance" that ignores missing path elements. * @param {string[]} contains search query path - * @param {Row} ty indexed item + * @param {rustdoc.Row} ty indexed item * @returns {null|number} edit distance */ function checkPath(contains, ty) { @@ -3720,6 +4237,7 @@ class DocSearch { return ret_dist > maxPathEditDistance ? null : ret_dist; } + // @ts-expect-error function typePassesFilter(filter, type) { // No filter or Exact mach if (filter <= NO_TYPE_FILTER || filter === type) return true; @@ -3741,6 +4259,7 @@ class DocSearch { return false; } + // @ts-expect-error function createAliasFromItem(item) { return { crate: item.crate, @@ -3758,11 +4277,14 @@ class DocSearch { }; } + // @ts-expect-error const handleAliases = async(ret, query, filterCrates, currentCrate) => { const lowerQuery = query.toLowerCase(); // We separate aliases and crate aliases because we want to have current crate // aliases to be before the others in the displayed results. + // @ts-expect-error const aliases = []; + // @ts-expect-error const crateAliases = []; if (filterCrates !== null) { if (this.ALIASES.has(filterCrates) @@ -3775,6 +4297,7 @@ class DocSearch { } else { for (const [crate, crateAliasesIndex] of this.ALIASES) { if (crateAliasesIndex.has(lowerQuery)) { + // @ts-expect-error const pushTo = crate === currentCrate ? crateAliases : aliases; const query_aliases = crateAliasesIndex.get(lowerQuery); for (const alias of query_aliases) { @@ -3784,6 +4307,7 @@ class DocSearch { } } + // @ts-expect-error const sortFunc = (aaa, bbb) => { if (aaa.path < bbb.path) { return 1; @@ -3792,18 +4316,23 @@ class DocSearch { } return -1; }; + // @ts-expect-error crateAliases.sort(sortFunc); aliases.sort(sortFunc); + // @ts-expect-error const fetchDesc = alias => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : this.searchState.loadDesc(alias); }; const [crateDescs, descs] = await Promise.all([ + // @ts-expect-error Promise.all(crateAliases.map(fetchDesc)), Promise.all(aliases.map(fetchDesc)), ]); + // @ts-expect-error const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); @@ -3818,12 +4347,15 @@ class DocSearch { }; aliases.forEach((alias, i) => { + // @ts-expect-error alias.desc = descs[i]; }); aliases.forEach(pushFunc); + // @ts-expect-error crateAliases.forEach((alias, i) => { alias.desc = crateDescs[i]; }); + // @ts-expect-error crateAliases.forEach(pushFunc); }; @@ -3843,21 +4375,24 @@ class DocSearch { * * `path_dist` is zero if a single-component search query is used, otherwise it's the * distance computed for everything other than the last path component. * - * @param {Results} results + * @param {rustdoc.Results} results * @param {string} fullId - * @param {integer} id - * @param {integer} index - * @param {integer} dist - * @param {integer} path_dist + * @param {number} id + * @param {number} index + * @param {number} dist + * @param {number} path_dist */ + // @ts-expect-error function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { if (dist <= maxEditDistance || index !== -1) { if (results.has(fullId)) { const result = results.get(fullId); + // @ts-expect-error if (result.dontValidate || result.dist <= dist) { return; } } + // @ts-expect-error results.set(fullId, { id: id, index: index, @@ -3873,12 +4408,16 @@ class DocSearch { * try to match the items which validates all the elements. For `aa -> bb` will look for * functions which have a parameter `aa` and has `bb` in its returned values. * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {Object} results + * @param {rustdoc.Row} row + * @param {number} pos - Position in the `searchIndex`. + * @param {rustdoc.Results} results */ function handleArgs(row, pos, results) { - if (!row || (filterCrates !== null && row.crate !== filterCrates) || !row.type) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; + } + const rowType = row.type; + if (!rowType) { return; } @@ -3889,21 +4428,23 @@ class DocSearch { if (tfpDist === null) { return; } + // @ts-expect-error if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { return; } // If the result is too "bad", we return false and it ends this search. if (!unifyFunctionTypes( - row.type.inputs, + rowType.inputs, parsedQuery.elems, - row.type.where_clause, + rowType.where_clause, null, + // @ts-expect-error mgens => { return unifyFunctionTypes( - row.type.output, + rowType.output, parsedQuery.returned, - row.type.where_clause, + rowType.where_clause, mgens, checkTypeMgensForConflict, 0, // unboxing depth @@ -3914,15 +4455,16 @@ class DocSearch { return; } + // @ts-expect-error results.max_dist = Math.max(results.max_dist || 0, tfpDist); - addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); + addIntoResults(results, row.id.toString(), pos, 0, tfpDist, 0, Number.MAX_VALUE); } /** * Compare the query fingerprint with the function fingerprint. * - * @param {{number}} fullId - The function - * @param {{Uint32Array}} queryFingerprint - The query + * @param {number} fullId - The function + * @param {Uint32Array} queryFingerprint - The query * @returns {number|null} - Null if non-match, number if distance * This function might return 0! */ @@ -3953,138 +4495,10 @@ class DocSearch { const innerRunQuery = () => { - const queryLen = - parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + - parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); - const maxEditDistance = Math.floor(queryLen / 3); - - /** - * @type {Map} - */ - const genericSymbols = new Map(); - - /** - * Convert names to ids in parsed query elements. - * This is not used for the "In Names" tab, but is used for the - * "In Params", "In Returns", and "In Function Signature" tabs. - * - * If there is no matching item, but a close-enough match, this - * function also that correction. - * - * See `buildTypeMapIndex` for more information. - * - * @param {QueryElement} elem - * @param {boolean} isAssocType - */ - const convertNameToId = (elem, isAssocType) => { - const loweredName = elem.pathLast.toLowerCase(); - if (this.typeNameIdMap.has(loweredName) && - (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { - elem.id = this.typeNameIdMap.get(loweredName).id; - } else if (!parsedQuery.literalSearch) { - let match = null; - let matchDist = maxEditDistance + 1; - let matchName = ""; - for (const [name, { id, assocOnly }] of this.typeNameIdMap) { - const dist = Math.min( - editDistance(name, loweredName, maxEditDistance), - editDistance(name, elem.normalizedPathLast, maxEditDistance), - ); - if (dist <= matchDist && dist <= maxEditDistance && - (isAssocType || !assocOnly)) { - if (dist === matchDist && matchName > name) { - continue; - } - match = id; - matchDist = dist; - matchName = name; - } - } - if (match !== null) { - parsedQuery.correction = matchName; - } - elem.id = match; - } - if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 - && elem.generics.length === 0 && elem.bindings.size === 0) - || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.normalizedPathLast)) { - elem.id = genericSymbols.get(elem.normalizedPathLast); - } else { - elem.id = -(genericSymbols.size + 1); - genericSymbols.set(elem.normalizedPathLast, elem.id); - } - if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { - // Silly heuristic to catch if the user probably meant - // to not write a generic parameter. We don't use it, - // just bring it up. - const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); - let matchDist = maxPartDistance + 1; - let matchName = ""; - for (const name of this.typeNameIdMap.keys()) { - const dist = editDistance( - name, - elem.normalizedPathLast, - maxPartDistance, - ); - if (dist <= matchDist && dist <= maxPartDistance) { - if (dist === matchDist && matchName > name) { - continue; - } - matchDist = dist; - matchName = name; - } - } - if (matchName !== "") { - parsedQuery.proposeCorrectionFrom = elem.name; - parsedQuery.proposeCorrectionTo = matchName; - } - } - elem.typeFilter = TY_GENERIC; - } - if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { - // Rust does not have HKT - parsedQuery.error = [ - "Generic type parameter ", - elem.name, - " does not accept generic parameters", - ]; - } - for (const elem2 of elem.generics) { - convertNameToId(elem2); - } - elem.bindings = new Map(Array.from(elem.bindings.entries()) - .map(entry => { - const [name, constraints] = entry; - if (!this.typeNameIdMap.has(name)) { - parsedQuery.error = [ - "Type parameter ", - name, - " does not exist", - ]; - return [null, []]; - } - for (const elem2 of constraints) { - convertNameToId(elem2); - } - - return [this.typeNameIdMap.get(name).id, constraints]; - }), - ); - }; - - for (const elem of parsedQuery.elems) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - for (const elem of parsedQuery.returned) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { const elem = parsedQuery.elems[0]; // use arrow functions to preserve `this`. + // @ts-expect-error const handleNameSearch = id => { const row = this.searchIndex[id]; if (!typePassesFilter(elem.typeFilter, row.ty) || @@ -4094,6 +4508,7 @@ class DocSearch { let pathDist = 0; if (elem.fullPath.length > 1) { + // @ts-expect-error pathDist = checkPath(elem.pathWithoutLast, row); if (pathDist === null) { return; @@ -4102,11 +4517,13 @@ class DocSearch { if (parsedQuery.literalSearch) { if (row.word === elem.pathLast) { + // @ts-expect-error addIntoResults(results_others, row.id, id, 0, 0, pathDist); } } else { addIntoResults( results_others, + // @ts-expect-error row.id, id, row.normalizedName.indexOf(elem.normalizedPathLast), @@ -4147,23 +4564,31 @@ class DocSearch { const returned = row.type && row.type.output && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); if (in_args) { + // @ts-expect-error results_in_args.max_dist = Math.max( + // @ts-expect-error results_in_args.max_dist || 0, tfpDist, ); const maxDist = results_in_args.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_in_args.max_dist; + // @ts-expect-error addIntoResults(results_in_args, row.id, i, -1, tfpDist, 0, maxDist); } if (returned) { + // @ts-expect-error results_returned.max_dist = Math.max( + // @ts-expect-error results_returned.max_dist || 0, tfpDist, ); const maxDist = results_returned.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_returned.max_dist; + // @ts-expect-error addIntoResults(results_returned, row.id, i, -1, tfpDist, 0, maxDist); } } @@ -4173,14 +4598,17 @@ class DocSearch { // types with generic parameters go last. // That's because of the way unification is structured: it eats off // the end, and hits a fast path if the last item is a simple atom. + // @ts-expect-error const sortQ = (a, b) => { const ag = a.generics.length === 0 && a.bindings.size === 0; const bg = b.generics.length === 0 && b.bindings.size === 0; if (ag !== bg) { + // @ts-expect-error return ag - bg; } const ai = a.id > 0; const bi = b.id > 0; + // @ts-expect-error return ai - bi; }; parsedQuery.elems.sort(sortQ); @@ -4197,8 +4625,11 @@ class DocSearch { const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow; const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ + // @ts-expect-error sortResults(results_in_args, "elems", currentCrate), + // @ts-expect-error sortResults(results_returned, "returned", currentCrate), + // @ts-expect-error sortResults(results_others, (isType ? "query" : null), currentCrate), ]); const ret = createQueryResults( @@ -4210,11 +4641,14 @@ class DocSearch { filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? "" : + // @ts-expect-error this.searchState.loadDesc(result); })); for (const [i, result] of list.entries()) { + // @ts-expect-error result.desc = descs[i]; } })); @@ -4229,7 +4663,9 @@ class DocSearch { // ==================== Core search logic end ==================== +/** @type {Map} */ let rawSearchIndex; +// @ts-expect-error let docSearch; const longItemTypes = [ "keyword", @@ -4259,13 +4695,16 @@ const longItemTypes = [ "derive macro", "trait alias", ]; +// @ts-expect-error let currentResults; // In the search display, allows to switch between tabs. +// @ts-expect-error function printTab(nb) { let iter = 0; let foundCurrentTab = false; let foundCurrentResultSet = false; + // @ts-expect-error onEachLazy(document.getElementById("search-tabs").childNodes, elem => { if (nb === iter) { addClass(elem, "selected"); @@ -4277,6 +4716,7 @@ function printTab(nb) { }); const isTypeSearch = (nb > 0 || iter === 1); iter = 0; + // @ts-expect-error onEachLazy(document.getElementById("results").childNodes, elem => { if (nb === iter) { addClass(elem, "active"); @@ -4287,12 +4727,15 @@ function printTab(nb) { iter += 1; }); if (foundCurrentTab && foundCurrentResultSet) { + // @ts-expect-error searchState.currentTab = nb; // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { + // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { + // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { @@ -4326,16 +4769,22 @@ function getFilterCrates() { const elem = document.getElementById("crate-search"); if (elem && + // @ts-expect-error elem.value !== "all crates" && + // @ts-expect-error window.searchIndex.has(elem.value) ) { + // @ts-expect-error return elem.value; } return null; } +// @ts-expect-error function nextTab(direction) { + // @ts-expect-error const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = document.activeElement; printTab(next); focusSearchResult(); @@ -4344,9 +4793,12 @@ function nextTab(direction) { // Focus the first search result on the active tab, or the result that // was focused last time this tab was active. function focusSearchResult() { + // @ts-expect-error const target = searchState.focusedByTab[searchState.currentTab] || document.querySelectorAll(".search-results.active a").item(0) || + // @ts-expect-error document.querySelectorAll("#search-tabs button").item(searchState.currentTab); + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = null; if (target) { target.focus(); @@ -4356,9 +4808,8 @@ function focusSearchResult() { /** * Render a set of search results for a single tab. * @param {Array} array - The search results for this tab - * @param {ParsedQuery} query + * @param {rustdoc.ParsedQuery} query * @param {boolean} display - True if this is the active tab - * @param {"sig"|"elems"|"returned"|null} typeInfo */ async function addTab(array, query, display) { const extraClass = display ? " active" : ""; @@ -4405,6 +4856,7 @@ ${item.displayPath}${name}\ if (item.displayTypeSignature) { const {type, mappedNames, whereClause} = await item.displayTypeSignature; const displayType = document.createElement("div"); + // @ts-expect-error type.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4445,6 +4897,7 @@ ${item.displayPath}${name}\ const line = document.createElement("div"); line.className = "where"; line.appendChild(document.createTextNode(` ${name}: `)); + // @ts-expect-error innerType.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4488,6 +4941,7 @@ ${item.displayPath}${name}\ return output; } +// @ts-expect-error function makeTabHeader(tabNb, text, nbElems) { // https://blog.horizon-eda.org/misc/2020/02/19/ui.html // @@ -4497,6 +4951,7 @@ function makeTabHeader(tabNb, text, nbElems) { const fmtNbElems = nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : `\u{2007}(${nbElems})`; + // @ts-expect-error if (searchState.currentTab === tabNb) { return ""; @@ -4505,11 +4960,12 @@ function makeTabHeader(tabNb, text, nbElems) { } /** - * @param {ResultsTable} results + * @param {rustdoc.ResultsTable} results * @param {boolean} go_to_first * @param {string} filterCrates */ async function showResults(results, go_to_first, filterCrates) { + // @ts-expect-error const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -4527,6 +4983,7 @@ async function showResults(results, go_to_first, filterCrates) { // will be used, starting search again since the search input is not empty, leading you // back to the previous page again. window.onunload = () => { }; + // @ts-expect-error searchState.removeQueryParameters(); const elem = document.createElement("a"); elem.href = results.others[0].href; @@ -4537,6 +4994,7 @@ async function showResults(results, go_to_first, filterCrates) { return; } if (results.query === undefined) { + // @ts-expect-error results.query = DocSearch.parseQuery(searchState.input.value); } @@ -4545,6 +5003,7 @@ async function showResults(results, go_to_first, filterCrates) { // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on // it again. + // @ts-expect-error let currentTab = searchState.currentTab; if ((currentTab === 0 && results.others.length === 0) || (currentTab === 1 && results.in_args.length === 0) || @@ -4572,6 +5031,7 @@ async function showResults(results, go_to_first, filterCrates) {

Results

${crates}`; if (results.query.error !== null) { const error = results.query.error; + // @ts-expect-error error.forEach((value, index) => { value = value.split("<").join("<").split(">").join(">"); if (index % 2 !== 0) { @@ -4632,7 +5092,9 @@ async function showResults(results, go_to_first, filterCrates) { resultsElem.appendChild(ret_returned); search.innerHTML = output; + // @ts-expect-error if (searchState.rustdocToolbar) { + // @ts-expect-error search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); } const crateSearch = document.getElementById("crate-search"); @@ -4641,23 +5103,30 @@ async function showResults(results, go_to_first, filterCrates) { } search.appendChild(resultsElem); // Reset focused elements. + // @ts-expect-error searchState.showResults(search); + // @ts-expect-error const elems = document.getElementById("search-tabs").childNodes; + // @ts-expect-error searchState.focusedByTab = []; let i = 0; for (const elem of elems) { const j = i; + // @ts-expect-error elem.onclick = () => printTab(j); + // @ts-expect-error searchState.focusedByTab.push(null); i += 1; } printTab(currentTab); } +// @ts-expect-error function updateSearchHistory(url) { if (!browserSupportsHistoryApi()) { return; } + // @ts-expect-error const params = searchState.getQueryStringParams(); if (!history.state && !params.search) { history.pushState(null, "", url); @@ -4672,9 +5141,11 @@ function updateSearchHistory(url) { * @param {boolean} [forced] */ async function search(forced) { + // @ts-expect-error const query = DocSearch.parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); + // @ts-expect-error if (!forced && query.userQuery === currentResults) { if (query.userQuery.length > 0) { putBackSearch(); @@ -4682,8 +5153,10 @@ async function search(forced) { return; } + // @ts-expect-error searchState.setLoadingSearch(); + // @ts-expect-error const params = searchState.getQueryStringParams(); // In case we have no information about the saved crate and there is a URL query parameter, @@ -4693,6 +5166,7 @@ async function search(forced) { } // Update document title to maintain a meaningful browser history + // @ts-expect-error searchState.title = "\"" + query.userQuery + "\" Search - Rust"; // Because searching is incremental by character, only the most @@ -4700,8 +5174,10 @@ async function search(forced) { updateSearchHistory(buildUrl(query.userQuery, filterCrates)); await showResults( + // @ts-expect-error await docSearch.execQuery(query, filterCrates, window.currentCrate), params.go_to_first, + // @ts-expect-error filterCrates); } @@ -4710,62 +5186,83 @@ async function search(forced) { * @param {Event} [e] - The event that triggered this call, if any */ function onSearchSubmit(e) { + // @ts-expect-error e.preventDefault(); + // @ts-expect-error searchState.clearInputTimeout(); search(); } function putBackSearch() { + // @ts-expect-error const search_input = searchState.input; + // @ts-expect-error if (!searchState.input) { return; } + // @ts-expect-error if (search_input.value !== "" && !searchState.isDisplayed()) { + // @ts-expect-error searchState.showResults(); if (browserSupportsHistoryApi()) { history.replaceState(null, "", buildUrl(search_input.value, getFilterCrates())); } + // @ts-expect-error document.title = searchState.title; } } function registerSearchEvents() { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Populate search bar with query string search term when provided, // but only if the input bar is empty. This avoid the obnoxious issue // where you start trying to do a search, and the index loads, and // suddenly your search is gone! + // @ts-expect-error if (searchState.input.value === "") { + // @ts-expect-error searchState.input.value = params.search || ""; } const searchAfter500ms = () => { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error if (searchState.input.value.length === 0) { + // @ts-expect-error searchState.hideResults(); } else { + // @ts-expect-error searchState.timeout = setTimeout(search, 500); } }; + // @ts-expect-error searchState.input.onkeyup = searchAfter500ms; + // @ts-expect-error searchState.input.oninput = searchAfter500ms; + // @ts-expect-error document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; + // @ts-expect-error searchState.input.onchange = e => { if (e.target !== document.activeElement) { // To prevent doing anything when it's from a blur event. return; } // Do NOT e.preventDefault() here. It will prevent pasting. + // @ts-expect-error searchState.clearInputTimeout(); // zero-timeout necessary here because at the time of event handler execution the // pasted content is not in the input field yet. Shouldn’t make any difference for // change, though. setTimeout(search, 0); }; + // @ts-expect-error searchState.input.onpaste = searchState.input.onchange; + // @ts-expect-error searchState.outputElement().addEventListener("keydown", e => { // We only handle unmodified keystrokes here. We don't want to interfere with, // for instance, alt-left and alt-right for history navigation. @@ -4775,18 +5272,24 @@ function registerSearchEvents() { // up and down arrow select next/previous search result, or the // search box if we're already at the top. if (e.which === 38) { // up + // @ts-expect-error const previous = document.activeElement.previousElementSibling; if (previous) { + // @ts-expect-error previous.focus(); } else { + // @ts-expect-error searchState.focus(); } e.preventDefault(); } else if (e.which === 40) { // down + // @ts-expect-error const next = document.activeElement.nextElementSibling; if (next) { + // @ts-expect-error next.focus(); } + // @ts-expect-error const rect = document.activeElement.getBoundingClientRect(); if (window.innerHeight - rect.bottom < rect.height) { window.scrollBy(0, rect.height); @@ -4801,6 +5304,7 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("keydown", e => { if (e.which === 40) { // down focusSearchResult(); @@ -4808,11 +5312,14 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("focus", () => { putBackSearch(); }); + // @ts-expect-error searchState.input.addEventListener("blur", () => { + // @ts-expect-error searchState.input.placeholder = searchState.input.origPlaceholder; }); @@ -4823,6 +5330,7 @@ function registerSearchEvents() { const previousTitle = document.title; window.addEventListener("popstate", e => { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Revert to the previous title manually since the History // API ignores the title parameter. @@ -4836,6 +5344,7 @@ function registerSearchEvents() { // nothing there, which lets you really go back to a // previous state with nothing in the bar. if (params.search && params.search.length > 0) { + // @ts-expect-error searchState.input.value = params.search; // Some browsers fire "onpopstate" for every page load // (Chrome), while others fire the event only when actually @@ -4845,9 +5354,11 @@ function registerSearchEvents() { e.preventDefault(); search(); } else { + // @ts-expect-error searchState.input.value = ""; // When browsing back from search results the main page // visibility must be reset. + // @ts-expect-error searchState.hideResults(); } }); @@ -4860,17 +5371,22 @@ function registerSearchEvents() { // that try to sync state between the URL and the search input. To work around it, // do a small amount of re-init on page show. window.onpageshow = () => { + // @ts-expect-error const qSearch = searchState.getQueryStringParams().search; + // @ts-expect-error if (searchState.input.value === "" && qSearch) { + // @ts-expect-error searchState.input.value = qSearch; } search(); }; } +// @ts-expect-error function updateCrate(ev) { if (ev.target.value === "all crates") { // If we don't remove it from the URL, it'll be picked up again by the search. + // @ts-expect-error const query = searchState.input.value.trim(); updateSearchHistory(buildUrl(query, null)); } @@ -4881,9 +5397,11 @@ function updateCrate(ev) { search(true); } +// @ts-expect-error function initSearch(searchIndx) { rawSearchIndex = searchIndx; if (typeof window !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); registerSearchEvents(); // If there's a search term in the URL, execute the search now. @@ -4891,6 +5409,7 @@ function initSearch(searchIndx) { search(); } } else if (typeof exports !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); exports.docSearch = docSearch; exports.parseQuery = DocSearch.parseQuery; @@ -4902,8 +5421,11 @@ if (typeof exports !== "undefined") { } if (typeof window !== "undefined") { + // @ts-expect-error window.initSearch = initSearch; + // @ts-expect-error if (window.searchIndex !== undefined) { + // @ts-expect-error initSearch(window.searchIndex); } } else { @@ -4918,19 +5440,23 @@ if (typeof window !== "undefined") { // https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ // LevenshteinAutomata.java class ParametricDescription { + // @ts-expect-error constructor(w, n, minErrors) { this.w = w; this.n = n; this.minErrors = minErrors; } + // @ts-expect-error isAccept(absState) { const state = Math.floor(absState / (this.w + 1)); const offset = absState % (this.w + 1); return this.w - offset + this.minErrors[state] <= this.n; } + // @ts-expect-error getPosition(absState) { return absState % (this.w + 1); } + // @ts-expect-error getVector(name, charCode, pos, end) { let vector = 0; for (let i = pos; i < end; i += 1) { @@ -4941,6 +5467,7 @@ class ParametricDescription { } return vector; } + // @ts-expect-error unpack(data, index, bitsPerValue) { const bitLoc = (bitsPerValue * index); const dataLoc = bitLoc >> 5; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 183663b94fc2..d7b0e4b4f541 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -3,6 +3,9 @@ /* global addClass, removeClass, onEach, onEachLazy */ /* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index 3003f4c15033..8f712f4c20c7 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -5,6 +5,9 @@ /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage, getVar */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d77804d045e3..4770ccc12799 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -5,15 +5,28 @@ // the page, so we don't see major layout changes during the load of the page. "use strict"; +/** + * @import * as rustdoc from "./rustdoc.d.ts"; + */ + const builtinThemes = ["light", "dark", "ayu"]; const darkThemes = ["dark", "ayu"]; -window.currentTheme = document.getElementById("themeStyle"); +window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; +})(); const settingsDataset = (function() { const settingsElement = document.getElementById("default-settings"); return settingsElement && settingsElement.dataset ? settingsElement.dataset : null; })(); +/** + * Get a configuration value. If it's not set, get the default. + * + * @param {string} settingName + * @returns + */ function getSettingValue(settingName) { const current = getCurrentValue(settingName); if (current === null && settingsDataset !== null) { @@ -29,17 +42,39 @@ function getSettingValue(settingName) { const localStoredTheme = getSettingValue("theme"); +/** + * Check if a DOM Element has the given class set. + * If `elem` is null, returns false. + * + * @param {HTMLElement|null} elem + * @param {string} className + * @returns {boolean} + */ // eslint-disable-next-line no-unused-vars function hasClass(elem, className) { - return elem && elem.classList && elem.classList.contains(className); + return !!elem && !!elem.classList && elem.classList.contains(className); } +/** + * Add a class to a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ function addClass(elem, className) { if (elem && elem.classList) { elem.classList.add(className); } } +/** + * Remove a class from a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ // eslint-disable-next-line no-unused-vars function removeClass(elem, className) { if (elem && elem.classList) { @@ -49,8 +84,8 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. - * @param {Array} arr - The array to iterate over - * @param {function(?)} func - The callback + * @param {Array} arr - The array to iterate over + * @param {function(?): boolean|undefined} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -67,8 +102,8 @@ function onEach(arr, func) { * or a "live" NodeList while modifying it can be very slow. * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList - * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?)} func - The callback + * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over + * @param {function(?): boolean} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -77,6 +112,15 @@ function onEachLazy(lazyArray, func) { func); } +/** + * Set a configuration value. This uses localstorage, + * with a `rustdoc-` prefix, to avoid clashing with other + * web apps that may be running in the same domain (for example, mdBook). + * If localStorage is disabled, this function does nothing. + * + * @param {string} name + * @param {string} value + */ function updateLocalStorage(name, value) { try { window.localStorage.setItem("rustdoc-" + name, value); @@ -85,6 +129,15 @@ function updateLocalStorage(name, value) { } } +/** + * Get a configuration value. If localStorage is disabled, + * this function returns null. If the setting was never + * changed by the user, it also returns null; if you want to + * be able to use a default value, call `getSettingValue` instead. + * + * @param {string} name + * @returns {string|null} + */ function getCurrentValue(name) { try { return window.localStorage.getItem("rustdoc-" + name); @@ -93,19 +146,29 @@ function getCurrentValue(name) { } } -// Get a value from the rustdoc-vars div, which is used to convey data from -// Rust to the JS. If there is no such element, return null. -const getVar = (function getVar(name) { +/** + * Get a value from the rustdoc-vars div, which is used to convey data from + * Rust to the JS. If there is no such element, return null. + * + * @param {string} name + * @returns {string|null} + */ +function getVar(name) { const el = document.querySelector("head > meta[name='rustdoc-vars']"); - return el ? el.attributes["data-" + name].value : null; -}); + return el ? el.getAttribute("data-" + name) : null; +} +/** + * Change the current theme. + * @param {string|null} newThemeName + * @param {boolean} saveTheme + */ function switchTheme(newThemeName, saveTheme) { - const themeNames = getVar("themes").split(",").filter(t => t); + const themeNames = (getVar("themes") || "").split(",").filter(t => t); themeNames.push(...builtinThemes); // Ensure that the new theme name is among the defined themes - if (themeNames.indexOf(newThemeName) === -1) { + if (newThemeName === null || themeNames.indexOf(newThemeName) === -1) { return; } @@ -118,7 +181,7 @@ function switchTheme(newThemeName, saveTheme) { document.documentElement.setAttribute("data-theme", newThemeName); if (builtinThemes.indexOf(newThemeName) !== -1) { - if (window.currentTheme) { + if (window.currentTheme && window.currentTheme.parentNode) { window.currentTheme.parentNode.removeChild(window.currentTheme); window.currentTheme = null; } @@ -130,7 +193,10 @@ function switchTheme(newThemeName, saveTheme) { // rendering, but if we are done, it would blank the page. if (document.readyState === "loading") { document.write(``); - window.currentTheme = document.getElementById("themeStyle"); + window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; + })(); } else { window.currentTheme = document.createElement("link"); window.currentTheme.rel = "stylesheet"; @@ -179,11 +245,13 @@ const updateTheme = (function() { return updateTheme; })(); +// @ts-ignore if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732 if (getSettingValue("use-system-theme") === null && getSettingValue("preferred-dark-theme") === null + && localStoredTheme !== null && darkThemes.indexOf(localStoredTheme) >= 0) { updateLocalStorage("preferred-dark-theme", localStoredTheme); } diff --git a/src/librustdoc/html/static/js/tsconfig.json b/src/librustdoc/html/static/js/tsconfig.json new file mode 100644 index 000000000000..b81099bb9dfd --- /dev/null +++ b/src/librustdoc/html/static/js/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2023", + "module": "esnext", + "rootDir": "./", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true + }, + "typeAcquisition": { + "include": ["./rustdoc.d.ts"] + } +} From 0a9ee02d0a3ac8a45ab00b817c801f087a06828e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Tue, 28 Jan 2025 03:44:03 +0100 Subject: [PATCH 51/77] GCI: Don't try to collect mono items inside overly generic free const items --- compiler/rustc_monomorphize/src/collector.rs | 11 +++++++---- .../def-site-eval.fail.stderr | 11 +++++++++++ tests/ui/generic-const-items/def-site-eval.rs | 16 ++++++++++++++++ tests/ui/generic-const-items/def-site-mono.rs | 13 +++++++++++++ 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/ui/generic-const-items/def-site-eval.fail.stderr create mode 100644 tests/ui/generic-const-items/def-site-eval.rs create mode 100644 tests/ui/generic-const-items/def-site-mono.rs diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bb603df11294..d53848f7461f 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1454,11 +1454,14 @@ impl<'v> RootCollector<'_, 'v> { self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { - // const items only generate mono items if they are - // actually used somewhere. Just declaring them is insufficient. + // Const items only generate mono items if they are actually used somewhere. + // Just declaring them is insufficient. - // but even just declaring them must collect the items they refer to - if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { + // But even just declaring them must collect the items they refer to + // unless their generics require monomorphization. + if !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) + { collect_const_value(self.tcx, val, self.output); } } diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr new file mode 100644 index 000000000000..22a5f2916977 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -0,0 +1,11 @@ +error[E0080]: evaluation of `_::<'_>` failed + --> $DIR/def-site-eval.rs:14:20 + | +LL | const _<'_a>: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/def-site-eval.rs:14:20 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs new file mode 100644 index 000000000000..3ed7f96aed02 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -0,0 +1,16 @@ +//! Test that we only evaluate free const items (their def site to be clear) +//! whose generics don't require monomorphization. +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ revisions: fail pass +//@[fail] build-fail (we require monomorphization) +//@[pass] build-pass (we require monomorphization) + +const _<_T>: () = panic!(); +const _: () = panic!(); + +#[cfg(fail)] +const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of `_::<'_>` failed + +fn main() {} diff --git a/tests/ui/generic-const-items/def-site-mono.rs b/tests/ui/generic-const-items/def-site-mono.rs new file mode 100644 index 000000000000..f10d450f6bdb --- /dev/null +++ b/tests/ui/generic-const-items/def-site-mono.rs @@ -0,0 +1,13 @@ +//! Ensure that we don't try to collect monomorphizeable items inside free const +//! items (their def site to be clear) whose generics require monomorphization. +//! +//! Such items are to be collected at instantiation sites of free consts. + +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ build-pass (we require monomorphization) + +const _IDENTITY: fn(T) -> T = |x| x; + +fn main() {} From 93ee180cfa12cfca5e0ce79bb3d9a3eaf91cf7b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 18:30:17 +0100 Subject: [PATCH 52/77] ABI-required target features: warn when they are missing in base CPU (rather than silently enabling them) --- compiler/rustc_codegen_gcc/src/gcc_util.rs | 50 +---------------- compiler/rustc_codegen_gcc/src/lib.rs | 5 +- compiler/rustc_codegen_llvm/src/llvm_util.rs | 56 ++----------------- compiler/rustc_interface/messages.ftl | 5 ++ compiler/rustc_interface/src/errors.rs | 9 +++ compiler/rustc_interface/src/interface.rs | 2 + compiler/rustc_interface/src/util.rs | 38 ++++++++++++- .../x86/intrinsics-x86-pause-without-sse2.rs | 4 +- tests/codegen/target-feature-overrides.rs | 8 +-- tests/codegen/tied-features-strength.rs | 5 +- tests/ui-fulldeps/codegen-backend/hotplug.rs | 4 ++ .../feature-hierarchy.aarch64-sve2.stderr | 7 --- ...target-feature-flag-disable-implied.stderr | 2 +- ...at-target-feature-flag-disable-neon.stderr | 2 +- ...rdfloat-target-feature-flag-disable.stderr | 10 ++-- 15 files changed, 80 insertions(+), 127 deletions(-) delete mode 100644 tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 560aff43d653..4e8c8aaaf5c8 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,10 +1,8 @@ -use std::iter::FromIterator; - #[cfg(feature = "master")] use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; @@ -45,12 +43,6 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter(|(feature, _, _)| { // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { @@ -388,9 +387,13 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter_map(|(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + // The `allow_unstable` set is used by rustc internally to determined which target + // features are truly available, so we want to return even perma-unstable "forbidden" + // features. + if allow_unstable + || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { Some(*feature) } else { None @@ -670,12 +673,6 @@ pub(crate) fn global_llvm_features( // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones - // are disabled. - let abi_feature_constraints = sess.target.abi_required_features(); - let abi_incompatible_set = - FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); - // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { @@ -746,52 +743,11 @@ pub(crate) fn global_llvm_features( } } - // Ensure that the features we enable/disable are compatible with the ABI. - if enable { - if abi_incompatible_set.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "enabled", - reason: "this feature is incompatible with the target ABI", - }); - } - } else { - // FIXME: we have to request implied features here since - // negative features do not handle implied features above. - for &required in abi_feature_constraints.required.iter() { - let implied = - sess.target.implied_target_features(std::iter::once(required)); - if implied.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "disabled", - reason: "this feature is required by the target ABI", - }); - } - } - } - // FIXME(nagisa): figure out how to not allocate a full hashset here. featsmap.insert(feature, enable); } } - // To be sure the ABI-relevant features are all in the right state, we explicitly - // (un)set them here. This means if the target spec sets those features wrong, - // we will silently correct them rather than silently producing wrong code. - // (The target sanity check tries to catch this, but we can't know which features are - // enabled in LLVM by default so we can't be fully sure about that check.) - // We add these at the beginning of the list so that `-Ctarget-features` can - // still override it... that's unsound, but more compatible with past behavior. - all_rust_features.splice( - 0..0, - abi_feature_constraints - .required - .iter() - .map(|&f| (true, f)) - .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), - ); - // Translate this into LLVM features. let feats = all_rust_features .iter() diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 47dfbc1d7fbf..31123625369b 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -1,3 +1,8 @@ +interface_abi_required_feature = + target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +interface_abi_required_feature_issue = for more information, see issue #116344 + interface_cant_emit_mir = could not emit MIR: {$error} diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 939980a932fd..b62950d67096 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -103,3 +103,12 @@ pub struct IgnoringOutDir; #[derive(Diagnostic)] #[diag(interface_multiple_output_types_to_stdout)] pub struct MultipleOutputTypesToStdout; + +#[derive(Diagnostic)] +#[diag(interface_abi_required_feature)] +#[note] +#[note(interface_abi_required_feature_issue)] +pub(crate) struct AbiRequiredTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, +} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2113345eda3a..d9803236f85c 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -492,6 +492,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } sess.lint_store = Some(Lrc::new(lint_store)); + util::check_abi_required_features(&sess); + let compiler = Compiler { sess, codegen_backend, diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 984b8104f539..e900ec14fcab 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,21 +18,25 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use rustc_target::spec::Target; use tracing::info; use crate::errors; /// Function pointer type that constructs a new CodegenBackend. -pub type MakeBackendFn = fn() -> Box; +type MakeBackendFn = fn() -> Box; /// Adds `target_feature = "..."` cfgs for a variety of platform /// specific features (SSE, NEON etc.). /// /// This is performed by checking whether a set of permitted features /// is available on the target machine, by querying the codegen backend. -pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { +pub(crate) fn add_configuration( + cfg: &mut Cfg, + sess: &mut Session, + codegen_backend: &dyn CodegenBackend, +) { let tf = sym::target_feature; let unstable_target_features = codegen_backend.target_features_cfg(sess, true); @@ -48,6 +52,34 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } +/// Ensures that all target features required by the ABI are present. +/// Must be called after `unstable_target_features` has been populated! +pub(crate) fn check_abi_required_features(sess: &Session) { + let abi_feature_constraints = sess.target.abi_required_features(); + // We check this against `unstable_target_features` as that is conveniently already + // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. + // Just double-check that the features we care about are actually on our list. + for feature in + abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter()) + { + assert!( + sess.target.rust_target_features().iter().any(|(name, ..)| feature == name), + "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target" + ); + } + + for feature in abi_feature_constraints.required { + if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" }); + } + } + for feature in abi_feature_constraints.incompatible { + if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" }); + } + } +} + pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs index 4d5ddd75f385..6ca53c0eb6fc 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs @@ -1,5 +1,5 @@ -// We're testing x86 target specific features -//@only-target: x86_64 i686 +// We're testing x86-32 target specific features. SSE always exists on x86-64. +//@only-target: i686 //@compile-flags: -C target-feature=-sse2 #[cfg(target_arch = "x86")] diff --git a/tests/codegen/target-feature-overrides.rs b/tests/codegen/target-feature-overrides.rs index e7f70a1e24ab..f38a1ae72de5 100644 --- a/tests/codegen/target-feature-overrides.rs +++ b/tests/codegen/target-feature-overrides.rs @@ -39,8 +39,8 @@ pub unsafe fn banana() -> u32 { } // CHECK: attributes [[APPLEATTRS]] -// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx,+avx,{{.*}}" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx,+avx,{{.*}}" // CHECK: attributes [[BANANAATTRS]] -// COMPAT-SAME: "target-features"="+x87,+sse2,+avx,+avx2,{{.*}}" -// INCOMPAT-SAME: "target-features"="+x87,+sse2,-avx2,-avx" +// COMPAT-SAME: "target-features"="+avx,+avx2,{{.*}}" +// INCOMPAT-SAME: "target-features"="-avx2,-avx" diff --git a/tests/codegen/tied-features-strength.rs b/tests/codegen/tied-features-strength.rs index 6daa5cd7d5e2..1b2b63c3d1ac 100644 --- a/tests/codegen/tied-features-strength.rs +++ b/tests/codegen/tied-features-strength.rs @@ -11,11 +11,10 @@ // ENABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } //@ [DISABLE_SVE] compile-flags: -C target-feature=-sve -Copt-level=0 -// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?)|(\+fp-armv8,?))*}}" } +// DISABLE_SVE: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-sve,?)|(\+neon,?))*}}" } //@ [DISABLE_NEON] compile-flags: -C target-feature=-neon -Copt-level=0 -// `neon` and `fp-armv8` get enabled as target base features, but then disabled again at the end of the list. -// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fp-armv8,?)|(\+neon,?))*}},-neon,-fp-armv8{{(,\+fpmr)?}}" } +// DISABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(-fp-armv8,?)|(-neon,?))*}}" } //@ [ENABLE_NEON] compile-flags: -C target-feature=+neon -Copt-level=0 // ENABLE_NEON: attributes #0 = { {{.*}} "target-features"="{{((\+outline-atomics,?)|(\+v8a,?)|(\+fpmr,?)?|(\+fp-armv8,?)|(\+neon,?))*}}" } diff --git a/tests/ui-fulldeps/codegen-backend/hotplug.rs b/tests/ui-fulldeps/codegen-backend/hotplug.rs index 917b20fcdb56..ce310ba3e315 100644 --- a/tests/ui-fulldeps/codegen-backend/hotplug.rs +++ b/tests/ui-fulldeps/codegen-backend/hotplug.rs @@ -6,6 +6,10 @@ //@ normalize-stdout: "libthe_backend.dylib" -> "libthe_backend.so" //@ normalize-stdout: "the_backend.dll" -> "libthe_backend.so" +// Pick a target that requires no target features, so that no warning is shown +// about missing target features. +//@ compile-flags: --target arm-unknown-linux-gnueabi +//@ needs-llvm-components: arm //@ revisions: normal dep bindep //@ compile-flags: --crate-type=lib //@ [normal] compile-flags: --emit=link=- diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr deleted file mode 100644 index b6c3ccdedfb0..000000000000 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ /dev/null @@ -1,7 +0,0 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI - | - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #116344 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr index 72b2d03fe203..7ec8b04cfce0 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -1,4 +1,4 @@ -warning: target feature `sse` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index b6c3ccdedfb0..b1186d5d5dc7 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `neon` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 6191681286a3..02398d27501c 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,11 +1,11 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: target feature `x87` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + warning: 2 warnings emitted From 3f6ffa1462d8f9d4f6f645a330872b172074a2c5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 Jan 2025 20:17:39 +0100 Subject: [PATCH 53/77] update comments --- compiler/rustc_target/src/target_features.rs | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa..3bc4b92987f1 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -108,21 +108,19 @@ impl Stability { // per-function level, since we would then allow safe calls from functions with `+soft-float` to // functions without that feature! // -// It is important for soundness that features allowed here do *not* change the function call ABI. -// For example, disabling the `x87` feature on x86 changes how scalar floats are passed as -// arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature` -// will just allow unknown features (with a warning), we have to explicitly list features that change -// the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if -// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need -// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a -// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context. -// FIXME: add such "forbidden" features for non-x86 targets. +// It is important for soundness to consider the interaction of targets features and the function +// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as +// arguments, so letting people toggle that feature would be unsound. To this end, the +// `abi_required_features` function computes which target features must and must not be enabled for +// any given target, and individual features can also be marked as `Forbidden`. +// See https://github.com/rust-lang/rust/issues/116344 for some more context. // // The one exception to features that change the ABI is features that enable larger vector -// registers. Those are permitted to be listed here. This is currently unsound (see -// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that -// functions can only use such vectors as arguments/return types if the corresponding target feature -// is enabled. +// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store +// information about which target feature is ABI-required for which vector size; this is used to +// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For +// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.) +// Also see https://github.com/rust-lang/rust/issues/116558. // // Stabilizing a target feature requires t-lang approval. From 7f83f8ae727adb4893a83abea9d5dc0ea5a9b149 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 14:11:33 +0900 Subject: [PATCH 54/77] Reject unsound toggling of Arm atomics-32 target feature --- compiler/rustc_target/src/target_features.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa..46e29304502b 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -137,6 +137,11 @@ const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ( + "atomics-32", + Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + &[], + ), ("crc", Unstable(sym::arm_target_feature), &[]), ("d32", Unstable(sym::arm_target_feature), &[]), ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), From 7877d86163d802b2963d893850555d684c9d0eb1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Jan 2025 08:08:46 +0000 Subject: [PATCH 55/77] Add mir-opt pattern type tests --- .../pattern_types.main.PreCodegen.after.mir | 15 +++++++++++++++ tests/mir-opt/pattern_types.rs | 12 ++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/mir-opt/pattern_types.main.PreCodegen.after.mir create mode 100644 tests/mir-opt/pattern_types.rs diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir new file mode 100644 index 000000000000..e96526a01ff8 --- /dev/null +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -0,0 +1,15 @@ +// MIR for `main` after PreCodegen + +fn main() -> () { + let mut _0: (); + scope 1 { + debug x => const {transmute(0x00000002): (u32) is 1..=}; + scope 2 { + debug y => const {transmute(0x00000000): (u32) is 1..=}; + } + } + + bb0: { + return; + } +} diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs new file mode 100644 index 000000000000..3903fbad4ae8 --- /dev/null +++ b/tests/mir-opt/pattern_types.rs @@ -0,0 +1,12 @@ +#![feature(pattern_types)] +#![feature(pattern_type_macro)] + +use std::pat::pattern_type; + +// EMIT_MIR pattern_types.main.PreCodegen.after.mir +fn main() { + // CHECK: debug x => const {transmute(0x00000002): (u32) is 1..=} + let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; + // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=} + let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; +} From fd6713fce1d06ba283bc27331df354b7a9443d6b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 15:19:49 +0000 Subject: [PATCH 56/77] Make mir dumps more readable --- compiler/rustc_middle/src/ty/print/pretty.rs | 4 ++++ tests/mir-opt/pattern_types.main.PreCodegen.after.mir | 4 ++-- tests/mir-opt/pattern_types.rs | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ac900edefe12..027a4315b4bf 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1740,6 +1740,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { " as ", )?; } + ty::Pat(base_ty, pat) => { + self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; + p!(write(" is {pat:?}")); + } // Nontrivial types with scalar bit representation _ => { let print = |this: &mut Self| { diff --git a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir index e96526a01ff8..8c99902f9b8f 100644 --- a/tests/mir-opt/pattern_types.main.PreCodegen.after.mir +++ b/tests/mir-opt/pattern_types.main.PreCodegen.after.mir @@ -3,9 +3,9 @@ fn main() -> () { let mut _0: (); scope 1 { - debug x => const {transmute(0x00000002): (u32) is 1..=}; + debug x => const 2_u32 is 1..=; scope 2 { - debug y => const {transmute(0x00000000): (u32) is 1..=}; + debug y => const 0_u32 is 1..=; } } diff --git a/tests/mir-opt/pattern_types.rs b/tests/mir-opt/pattern_types.rs index 3903fbad4ae8..217c64b90cbb 100644 --- a/tests/mir-opt/pattern_types.rs +++ b/tests/mir-opt/pattern_types.rs @@ -5,8 +5,8 @@ use std::pat::pattern_type; // EMIT_MIR pattern_types.main.PreCodegen.after.mir fn main() { - // CHECK: debug x => const {transmute(0x00000002): (u32) is 1..=} + // CHECK: debug x => const 2_u32 is 1..= let x: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(2) }; - // CHECK: debug y => const {transmute(0x00000000): (u32) is 1..=} + // CHECK: debug y => const 0_u32 is 1..= let y: pattern_type!(u32 is 1..) = unsafe { std::mem::transmute(0) }; } From d62f885a8e055e977a5c0afdcca59376a388f8b2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Jan 2025 10:37:16 +0000 Subject: [PATCH 57/77] Edit the inputs to const == val check instead of duplicating logic --- .../src/builder/matches/test.rs | 66 ++++++++++--------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index f7b0f734b2de..afe6b4475be3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -141,47 +141,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, ty } => { + TestKind::Eq { value, mut ty } => { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); let expect_ty = value.ty(); let expect = self.literal_operand(test.span, value); - if let ty::Adt(def, _) = ty.kind() - && tcx.is_lang_item(def.did(), LangItem::String) - { - if !tcx.features().string_deref_patterns() { - span_bug!( + + let mut place = place; + let mut block = block; + match ty.kind() { + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { + if !tcx.features().string_deref_patterns() { + span_bug!( + test.span, + "matching on `String` went through without enabling string_deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_str = self.temp(ref_str_ty, test.span); + let eq_block = self.cfg.start_new_block(); + // `let ref_str: &str = ::deref(&place);` + self.call_deref( + block, + eq_block, + place, + Mutability::Not, + ty, + ref_str, test.span, - "matching on `String` went through without enabling string_deref_patterns" ); + // Since we generated a `ref_str = ::deref(&place) -> eq_block` terminator, + // we need to add all further statements to `eq_block`. + // Similarly, the normal test code should be generated for the `&str`, instead of the `String`. + block = eq_block; + place = ref_str; + ty = ref_str_ty; } - let re_erased = tcx.lifetimes.re_erased; - let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); - let ref_str = self.temp(ref_str_ty, test.span); - let eq_block = self.cfg.start_new_block(); - // `let ref_str: &str = ::deref(&place);` - self.call_deref( - block, - eq_block, - place, - Mutability::Not, - ty, - ref_str, - test.span, - ); - self.non_scalar_compare( - eq_block, - success_block, - fail_block, - source_info, - expect, - expect_ty, - Operand::Copy(ref_str), - ref_str_ty, - ); - } else if !ty.is_scalar() { + _ => {} + } + + if !ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) self.non_scalar_compare( From eee9df43e69e9841aaaa9bd4eae9f600a81f20b1 Mon Sep 17 00:00:00 2001 From: SpecificProtagonist Date: Sat, 25 Jan 2025 00:54:51 +0100 Subject: [PATCH 58/77] miri: optimize zeroed alloc Co-authored-by: Ralf Jung --- .../src/const_eval/machine.rs | 8 +++--- .../rustc_const_eval/src/interpret/memory.rs | 21 ++++++++++----- .../rustc_const_eval/src/interpret/place.rs | 8 +++--- .../rustc_const_eval/src/interpret/util.rs | 4 +-- .../src/mir/interpret/allocation.rs | 26 ++++++++++++++----- .../rustc_middle/src/mir/interpret/mod.rs | 4 +-- compiler/rustc_middle/src/ty/vtable.rs | 6 +++-- compiler/rustc_smir/src/rustc_smir/alloc.rs | 12 ++++++--- src/tools/miri/src/shims/alloc.rs | 19 +++++--------- src/tools/miri/src/shims/foreign_items.rs | 16 +++++------- src/tools/miri/src/shims/unix/fs.rs | 1 + src/tools/miri/src/shims/unix/linux/mem.rs | 10 +------ src/tools/miri/src/shims/unix/mem.rs | 16 +++++------- .../miri/src/shims/windows/foreign_items.rs | 16 ++++++------ 14 files changed, 88 insertions(+), 79 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cfdfbdb7880b..6a339d695426 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,10 @@ use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, - throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, + compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, + throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -420,6 +421,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { Size::from_bytes(size), align, interpret::MemoryKind::Machine(MemoryKind::Heap), + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0..1b8a222e9eaa 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -20,10 +20,10 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use tracing::{debug, instrument, trace}; use super::{ - AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, - CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, + Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -230,11 +230,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, align: Align, kind: MemoryKind, + init: AllocInit, ) -> InterpResult<'tcx, Pointer> { let alloc = if M::PANIC_ON_ALLOC_FAIL { - Allocation::uninit(size, align) + Allocation::new(size, align, init) } else { - Allocation::try_uninit(size, align)? + Allocation::try_new(size, align, init)? }; self.insert_allocation(alloc, kind) } @@ -270,6 +271,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } + /// If this grows the allocation, `init_growth` determines + /// whether the additional space will be initialized. pub fn reallocate_ptr( &mut self, ptr: Pointer>, @@ -277,6 +280,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { new_size: Size, new_align: Align, kind: MemoryKind, + init_growth: AllocInit, ) -> InterpResult<'tcx, Pointer> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { @@ -289,7 +293,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". // This happens so rarely, the perf advantage is outweighed by the maintenance cost. - let new_ptr = self.allocate_ptr(new_size, new_align, kind)?; + // If requested, we zero-init the entire allocation, to ensure that a growing + // allocation has its new bytes properly set. For the part that is copied, + // `mem_copy` below will de-initialize things as necessary. + let new_ptr = self.allocate_ptr(new_size, new_align, kind, init_growth)?; let old_size = match old_size_and_align { Some((size, _align)) => size, None => self.get_alloc_raw(alloc_id)?.size(), diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index c97922ac132b..f5d3de7b1b27 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -12,9 +12,9 @@ use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, - Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, - Scalar, alloc_range, interp_ok, mir_assign_valid_types, + AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, + InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, + Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -983,7 +983,7 @@ where let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else { span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; - let ptr = self.allocate_ptr(size, align, kind)?; + let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?; interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ecb7c3fc93cd..eb98e3b53805 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -76,7 +76,7 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?; + let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit)?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1b07846e0cf6..2e4d258ffff2 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -270,6 +270,12 @@ impl AllocRange { } } +/// Whether a new allocation should be initialized with zero-bytes. +pub enum AllocInit { + Uninit, + Zero, +} + // The constructors are all without extra; the extra gets added by a machine hook later. impl Allocation { /// Creates an allocation initialized by the given bytes @@ -294,7 +300,12 @@ impl Allocation { Allocation::from_bytes(slice, Align::ONE, Mutability::Not) } - fn uninit_inner(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result { + fn new_inner( + size: Size, + align: Align, + init: AllocInit, + fail: impl FnOnce() -> R, + ) -> Result { // We raise an error if we cannot create the allocation on the host. // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always @@ -306,7 +317,10 @@ impl Allocation { Ok(Allocation { bytes, provenance: ProvenanceMap::new(), - init_mask: InitMask::new(size, false), + init_mask: InitMask::new(size, match init { + AllocInit::Uninit => false, + AllocInit::Zero => true, + }), align, mutability: Mutability::Mut, extra: (), @@ -315,8 +329,8 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. - pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { - Self::uninit_inner(size, align, || { + pub fn try_new<'tcx>(size: Size, align: Align, init: AllocInit) -> InterpResult<'tcx, Self> { + Self::new_inner(size, align, init, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) @@ -328,8 +342,8 @@ impl Allocation { /// /// Example use case: To obtain an Allocation filled with specific data, /// first call this function and then call write_scalar to fill in the right data. - pub fn uninit(size: Size, align: Align) -> Self { - match Self::uninit_inner(size, align, || { + pub fn new(size: Size, align: Align, init: AllocInit) -> Self { + match Self::new_inner(size, align, init, || { panic!( "interpreter ran out of memory: cannot create allocation of {} bytes", size.bytes() diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b88137544bca..f4f9f221a751 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -30,8 +30,8 @@ pub use { }; pub use self::allocation::{ - AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk, - InitChunkIter, alloc_range, + AllocBytes, AllocError, AllocInit, AllocRange, AllocResult, Allocation, ConstAllocation, + InitChunk, InitChunkIter, alloc_range, }; pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 23e2e8ad3d33..455bd16ff8c2 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -4,7 +4,9 @@ use rustc_ast::Mutability; use rustc_macros::HashStable; use rustc_type_ir::elaborate; -use crate::mir::interpret::{AllocId, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range}; +use crate::mir::interpret::{ + AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, +}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; #[derive(Clone, Copy, PartialEq, HashStable)] @@ -108,7 +110,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let ptr_align = tcx.data_layout.pointer_align.abi; let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); - let mut vtable = Allocation::uninit(vtable_size, ptr_align); + let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by diff --git a/compiler/rustc_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 4e8db6096d4f..52c5b425c14f 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -1,6 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{AllocRange, Pointer, alloc_range}; +use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range}; use stable_mir::Error; use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; @@ -44,7 +44,8 @@ pub(crate) fn try_new_allocation<'tcx>( .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))? .align; - let mut allocation = rustc_middle::mir::interpret::Allocation::uninit(size, align.abi); + let mut allocation = + rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; @@ -68,8 +69,11 @@ pub(crate) fn try_new_allocation<'tcx>( .tcx .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))?; - let mut allocation = - rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + layout.size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar( &tables.tcx, diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 25c0b52d0618..0fda13e06160 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -1,5 +1,3 @@ -use std::iter; - use rustc_abi::{Align, Size}; use rustc_ast::expand::allocator::AllocatorKind; @@ -80,18 +78,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> { + fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } + let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; interp_ok(ptr.into()) } @@ -115,6 +105,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; this.write_pointer(ptr, &memptr)?; interp_ok(Scalar::from_i32(0)) @@ -134,7 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - self.malloc(new_size, /*zero_init*/ false) + self.malloc(new_size, AllocInit::Uninit) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -147,6 +138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), new_align, MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(new_ptr.into()) } @@ -187,6 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(ptr.into()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6c8ccc839859..1ce0c209de9e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -1,6 +1,5 @@ use std::collections::hash_map::Entry; use std::io::Write; -use std::iter; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, Size}; @@ -9,6 +8,7 @@ use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::AllocInit; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; use rustc_span::Symbol; @@ -442,7 +442,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [size] = this.check_shim(abi, Conv::C, link_name, args)?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { - let res = this.malloc(size, /*zero_init:*/ false)?; + let res = this.malloc(size, AllocInit::Uninit)?; this.write_pointer(res, dest)?; } else { // If this does not fit in an isize, return null and, on Unix, set errno. @@ -457,7 +457,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { - let res = this.malloc(size.bytes(), /*zero_init:*/ true)?; + let res = this.malloc(size.bytes(), AllocInit::Zero)?; this.write_pointer(res, dest)?; } else { // On size overflow, return null and, on Unix, set errno. @@ -509,6 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), memory_kind.into(), + AllocInit::Uninit )?; ecx.write_pointer(ptr, dest) @@ -537,14 +538,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), + AllocInit::Zero )?; - - // We just allocated this, the access is definitely in-bounds. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); this.write_pointer(ptr, dest) }); } @@ -604,6 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Rust.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest) }); diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 25594b780318..cafce62cfedb 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1109,6 +1109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), dirent_layout.align.abi, MiriMemoryKind::Runtime.into(), + AllocInit::Uninit )?; let entry: Pointer = entry.into(); diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 8e796d5dce55..6418d749d3d9 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -49,16 +49,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Mmap.into(), + AllocInit::Zero )?; - if let Some(increase) = new_size.checked_sub(old_size) { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.wrapping_offset(Size::from_bytes(old_size), this).into(), - std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), - ) - .unwrap(); - } interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 5531b944e17d..2d5d3a6471ab 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -111,15 +111,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(this.eval_libc("MAP_FAILED")); } - let ptr = - this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.into(), - std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), - ) - .unwrap(); + let ptr = this.allocate_ptr( + Size::from_bytes(map_length), + align, + MiriMemoryKind::Mmap.into(), + // mmap guarantees new mappings are zero-init. + AllocInit::Zero + )?; interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 0bf56c3d005f..4462d025bead 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -253,8 +253,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; - let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY - let zero_init = (flags & heap_zero_memory) == heap_zero_memory; + const HEAP_ZERO_MEMORY: u32 = 0x00000008; + let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY { + AllocInit::Zero + } else { + AllocInit::Uninit + }; // Alignment is twice the pointer size. // Source: let align = this.tcx.pointer_size().bytes().strict_mul(2); @@ -262,13 +266,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + init )?; - if zero_init { - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - )?; - } this.write_pointer(ptr, dest)?; } "HeapFree" => { @@ -300,6 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest)?; } From 4203627cedc5a6b5a7b7605888b5c33876e2b0e2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sat, 23 Nov 2024 23:47:45 +0800 Subject: [PATCH 59/77] Suggest considering casting fn item as fn pointer in more cases --- compiler/rustc_trait_selection/messages.ftl | 2 + .../src/error_reporting/infer/mod.rs | 2 +- .../src/error_reporting/infer/suggest.rs | 39 ++++++++++-- .../traits/fulfillment_errors.rs | 1 + compiler/rustc_trait_selection/src/errors.rs | 6 ++ .../casting-fn-item-to-fn-pointer.rs | 9 +++ .../casting-fn-item-to-fn-pointer.stderr | 59 +++++++++++++++++++ tests/ui/typeck/issue-107775.stderr | 2 + 8 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs create mode 100644 tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 1ab89ecde7a4..7bfc02c7909c 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` +trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}` + trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same trait_selection_fps_cast = consider casting to a fn pointer trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 929fa559d750..ff526b0cc5d6 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1661,7 +1661,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_tuple_pattern(cause, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, terr, diag); self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index fc2d0ba36f04..d2d481260786 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -12,6 +12,7 @@ use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, }; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{Span, sym}; @@ -20,7 +21,7 @@ use tracing::debug; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::hir::Path; use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; @@ -369,14 +370,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_function_pointers( + pub fn suggest_function_pointers_impl( &self, - cause: &ObligationCause<'tcx>, - span: Span, + span: Option, exp_found: &ty::error::ExpectedFound>, diag: &mut Diag<'_>, ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; let expected_inner = expected.peel_refs(); let found_inner = found.peel_refs(); @@ -399,6 +398,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } + let Some(span) = span else { + let casting = format!("{fn_name} as {sig}"); + diag.subdiagnostic(FnItemsAreDistinct); + diag.subdiagnostic(FnConsiderCasting { casting }); + return; + }; + let sugg = match (expected.is_ref(), found.is_ref()) { (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, @@ -433,6 +439,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let fn_name = self.tcx.def_path_str_with_args(*did2, args2); + + let Some(span) = span else { + diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig }); + return; + }; + let sug = if found.is_ref() { FunctionPointerSuggestion::CastBothRef { span, @@ -476,6 +488,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound>, + terr: TypeError<'tcx>, + diag: &mut Diag<'_>, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + + if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() { + self.suggest_function_pointers_impl(Some(span), exp_found, diag); + } else if let TypeError::Sorts(exp_found) = terr { + self.suggest_function_pointers_impl(None, &exp_found, diag); + } + } + pub fn should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 1109b11d2a71..294c20c629c8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -1969,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted(exp_found.found.to_string()), StringPart::normal("`"), ]); + self.suggest_function_pointers_impl(None, &exp_found, err); } true diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index afac6fc6004c..bd9751c117ea 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1498,6 +1498,12 @@ pub struct FnConsiderCasting { pub casting: String, } +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting_both)] +pub struct FnConsiderCastingBoth<'a> { + pub sig: Binder<'a, FnSig<'a>>, +} + #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs new file mode 100644 index 000000000000..fa1663d49eb2 --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 + +fn foo() {} + +fn main() { + let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR + let _: Vec = [foo].into_iter().collect(); //~ ERROR + let _: Vec = Vec::from([foo]); //~ ERROR +} diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr new file mode 100644 index 000000000000..d069d39514dc --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr @@ -0,0 +1,59 @@ +error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})` + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:59 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>` + but trait `FromIterator<(&_, fn())>` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:47 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here + | | + | this expression has type `[(&str, fn() {foo}); 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `fn() {foo}` + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:42 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator` is not implemented for `Vec` + but trait `FromIterator` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:30 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here + | | + | this expression has type `[fn() {foo}; 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/casting-fn-item-to-fn-pointer.rs:8:24 + | +LL | let _: Vec = Vec::from([foo]); + | --------- ^^^^^^^^^^^^^^^^ expected `Vec`, found `Vec` + | | + | expected due to this + | + = note: expected struct `Vec` + found struct `Vec` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 180b0183a3f2..dad7e1581e79 100644 --- a/tests/ui/typeck/issue-107775.stderr +++ b/tests/ui/typeck/issue-107775.stderr @@ -10,6 +10,8 @@ LL | Self { map } | = note: expected struct `HashMap Pin + Send + 'static)>>>` found struct `HashMap<{integer}, fn(_) -> Pin + Send>> {::do_something::<'_>}>` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `::do_something::<'_> as fn(u8) -> Pin + Send + 'static)>>` error: aborting due to 1 previous error From 9c4fd25f278d1a04e4d3cd5138bf8ffd9ce52e6a Mon Sep 17 00:00:00 2001 From: Ayush Singh Date: Tue, 28 Jan 2025 19:10:28 +0530 Subject: [PATCH 60/77] uefi: process: Fix args - While working on process env support, I found that args were currently broken. Not sure how I missed it in the PR, but well here is the fix. - Additionally, no point in adding space at the end of args. Signed-off-by: Ayush Singh --- library/std/src/sys/pal/uefi/process.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134dfb..3077a72eac66 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -460,7 +460,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +706,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,8 +720,6 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() From cfb8be52b35d156c7bb4e48feaab93afa2055abc Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 28 Jan 2025 23:49:02 +0900 Subject: [PATCH 61/77] Remove duplicated code in RISC-V asm bad-reg test --- tests/ui/asm/riscv/bad-reg.riscv32e.stderr | 66 +++++++++---------- tests/ui/asm/riscv/bad-reg.riscv32gc.stderr | 26 +++----- tests/ui/asm/riscv/bad-reg.riscv32i.stderr | 34 ++++------ .../ui/asm/riscv/bad-reg.riscv32imafc.stderr | 30 ++++----- tests/ui/asm/riscv/bad-reg.riscv64gc.stderr | 26 +++----- tests/ui/asm/riscv/bad-reg.riscv64imac.stderr | 34 ++++------ tests/ui/asm/riscv/bad-reg.rs | 2 - 7 files changed, 90 insertions(+), 128 deletions(-) diff --git a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr index 409178df9c5a..27c8e958e536 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32e.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32e.stderr @@ -22,170 +22,164 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("x16") _); | ^^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("x17") _); | ^^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", out("x18") _); | ^^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", out("x19") _); | ^^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:54:18 | LL | asm!("", out("x20") _); | ^^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:56:18 | LL | asm!("", out("x21") _); | ^^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:58:18 | LL | asm!("", out("x22") _); | ^^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("x23") _); | ^^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:62:18 | LL | asm!("", out("x24") _); | ^^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", out("x25") _); | ^^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("x26") _); | ^^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:68:18 | LL | asm!("", out("x27") _); | ^^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:72:18 + --> $DIR/bad-reg.rs:70:18 | LL | asm!("", out("x28") _); | ^^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:74:18 + --> $DIR/bad-reg.rs:72:18 | LL | asm!("", out("x29") _); | ^^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:76:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", out("x30") _); | ^^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:78:18 + --> $DIR/bad-reg.rs:76:18 | LL | asm!("", out("x31") _); | ^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -193,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -201,12 +195,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 33 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 4770e70cc2b7..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index ae7db1554b19..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index 8bc5c9a87fce..57664cfe893b 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:86:35 + --> $DIR/bad-reg.rs:84:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -73,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:89:36 + --> $DIR/bad-reg.rs:87:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -81,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -89,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -97,12 +91,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 4770e70cc2b7..4ff03d819e60 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index ae7db1554b19..fbe63eb0563c 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index 7f0fc00d5489..7d032d277aa9 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -38,8 +38,6 @@ fn f() { //~^ ERROR invalid register `sp`: the stack pointer cannot be used as an operand for inline asm asm!("", out("gp") _); //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm - asm!("", out("gp") _); - //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm asm!("", out("tp") _); //~^ ERROR invalid register `tp`: the thread pointer cannot be used as an operand for inline asm asm!("", out("zero") _); From 6509596dd7c663ea2b72c5c92ae025a8d585c476 Mon Sep 17 00:00:00 2001 From: MarcoIeni <11428655+MarcoIeni@users.noreply.github.com> Date: Tue, 28 Jan 2025 16:02:38 +0100 Subject: [PATCH 62/77] ci: remove unused windows runner --- src/ci/github-actions/jobs.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 7730d29d28f6..d5c949a0eb7a 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -43,10 +43,6 @@ runners: os: windows-2022-8core-32gb <<: *base-job - - &job-windows-16c - os: windows-2022-16core-64gb - <<: *base-job - - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true From 7e68422859f2e6e3514a1af68d0a7ba3629e2553 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 28 Jan 2025 17:52:28 +0000 Subject: [PATCH 63/77] Properly check that array length is valid type during built-in unsizing in index --- compiler/rustc_hir_typeck/src/place_op.rs | 11 +++++++++-- compiler/rustc_middle/src/traits/mod.rs | 3 +++ .../src/error_reporting/traits/suggestions.rs | 3 +++ .../rustc_trait_selection/src/traits/wf.rs | 2 +- tests/crashes/131103.rs | 6 ------ .../const-generics/bad-subst-const-kind.stderr | 2 ++ .../generic_const_exprs/type_mismatch.stderr | 2 ++ .../issues/index_array_bad_type.rs | 13 +++++++++++++ .../issues/index_array_bad_type.stderr | 18 ++++++++++++++++++ tests/ui/const-generics/transmute-fail.stderr | 4 ++++ tests/ui/const-generics/type_mismatch.stderr | 2 ++ .../consts/bad-array-size-in-type-err.stderr | 4 ++++ ...const-in-impl-fn-return-type.current.stderr | 2 ++ 13 files changed, 63 insertions(+), 9 deletions(-) delete mode 100644 tests/crashes/131103.rs create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.rs create mode 100644 tests/ui/const-generics/issues/index_array_bad_type.stderr diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba6350711357..e1bd9ae2e672 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,6 +1,7 @@ use rustc_errors::Applicability; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; +use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, @@ -136,8 +137,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = Ty::new_slice(self.tcx, *element_ty); + if let ty::Array(element_ty, ct) = *adjusted_ty.kind() { + self.register_predicate(Obligation::new( + self.tcx, + self.cause(base_expr.span, ObligationCauseCode::ArrayLen(adjusted_ty)), + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, self.tcx.types.usize), + )); + self_ty = Ty::new_slice(self.tcx, element_ty); } else { continue; } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 55d78e083e07..8a9110f842a9 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -194,6 +194,9 @@ pub enum ObligationCauseCode<'tcx> { /// A slice or array is WF only if `T: Sized`. SliceOrArrayElem, + /// An array `[T; N]` can only be indexed (and is only well-formed if) `N` has type usize. + ArrayLen(Ty<'tcx>), + /// A tuple is WF only if its middle elements are `Sized`. TupleElem, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 471105773e2b..67c870f387eb 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2770,6 +2770,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } + ObligationCauseCode::ArrayLen(array_ty) => { + err.note(format!("the length of array `{array_ty}` must be type `usize`")); + } ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9d32eb053860..20b675bcb76b 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -689,7 +689,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); // Note that the len being WF is implicitly checked while visiting. // Here we just check that it's of type usize. - let cause = self.cause(ObligationCauseCode::Misc); + let cause = self.cause(ObligationCauseCode::ArrayLen(t)); self.out.push(traits::Obligation::with_depth( tcx, cause, diff --git a/tests/crashes/131103.rs b/tests/crashes/131103.rs deleted file mode 100644 index 70193e8b3bd3..000000000000 --- a/tests/crashes/131103.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #131103 -struct Struct(pub [u8; N]); - -pub fn function(value: Struct<3>) -> u8 { - value.0[0] -} diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 5c8d9c903635..b36052696425 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] { | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `13` is not of type `u64` --> $DIR/bad-subst-const-kind.rs:13:24 diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr index e03580ec007c..7cb67252da52 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] {} | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error[E0046]: not all trait items implemented, missing: `ASSOC` --> $DIR/type_mismatch.rs:8:1 diff --git a/tests/ui/const-generics/issues/index_array_bad_type.rs b/tests/ui/const-generics/issues/index_array_bad_type.rs new file mode 100644 index 000000000000..91b89cd3fff3 --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.rs @@ -0,0 +1,13 @@ +struct Struct(pub [u8; N]); +//~^ ERROR the constant `N` is not of type `usize` + +pub fn function(value: Struct<3>) -> u8 { + value.0[0] + //~^ ERROR the constant `3` is not of type `usize` + + // FIXME(const_generics): Ideally we wouldn't report the above error + // b/c `Struct<_>` is never well formed, but I'd rather report too many + // errors rather than ICE the compiler. +} + +fn main() {} diff --git a/tests/ui/const-generics/issues/index_array_bad_type.stderr b/tests/ui/const-generics/issues/index_array_bad_type.stderr new file mode 100644 index 000000000000..ceea09733776 --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.stderr @@ -0,0 +1,18 @@ +error: the constant `N` is not of type `usize` + --> $DIR/index_array_bad_type.rs:1:34 + | +LL | struct Struct(pub [u8; N]); + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; N]` must be type `usize` + +error: the constant `3` is not of type `usize` + --> $DIR/index_array_bad_type.rs:5:5 + | +LL | value.0[0] + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; 3]` must be type `usize` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 978a9744e88a..0e26daa3a0f1 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -3,6 +3,8 @@ error: the constant `W` is not of type `usize` | LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:11:9 @@ -18,6 +20,8 @@ error: the constant `W` is not of type `usize` | LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:26:9 diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index d1bb5c1242f0..bd169ed2ec8f 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | fn bar() -> [u8; N] {} | ^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `N` is not of type `u8` --> $DIR/type_mismatch.rs:2:11 diff --git a/tests/ui/consts/bad-array-size-in-type-err.stderr b/tests/ui/consts/bad-array-size-in-type-err.stderr index 25d14d80c3ec..c3ff216432ee 100644 --- a/tests/ui/consts/bad-array-size-in-type-err.stderr +++ b/tests/ui/consts/bad-array-size-in-type-err.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | arr: [i32; N], | ^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; N]` must be type `usize` error[E0308]: mismatched types --> $DIR/bad-array-size-in-type-err.rs:7:38 @@ -15,6 +17,8 @@ error: the constant `2` is not of type `usize` | LL | let _ = BadArraySize::<2> { arr: [0, 0, 0] }; | ^^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; 2]` must be type `usize` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr index 1bcc0dbaf672..92ad83c33000 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr @@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize` | LL | fn func() -> [(); N]; | ^^^^^^^ expected `usize`, found `u32` + | + = note: the length of array `[(); N]` must be type `usize` error: aborting due to 2 previous errors From 3026545ab50e65fcd1c888b77272032000e36147 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Thu, 23 Jan 2025 10:16:08 +0100 Subject: [PATCH 64/77] parse_format optimize import use --- compiler/rustc_builtin_macros/src/asm.rs | 2 +- compiler/rustc_builtin_macros/src/format.rs | 2 +- compiler/rustc_parse_format/src/lib.rs | 41 ++++++++----------- compiler/rustc_parse_format/src/tests.rs | 18 ++++---- .../traits/on_unimplemented.rs | 4 +- .../crates/hir-def/src/body/lower/asm.rs | 2 +- .../crates/hir-def/src/hir/format_args.rs | 2 +- 7 files changed, 33 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5062cf55bb9a..eb5b345e49ec 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -651,7 +651,7 @@ fn expand_preparsed_asm( .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); for piece in unverified_pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into())) } parse::Piece::NextArgument(arg) => { diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401..a0ab6375a666 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -406,7 +406,7 @@ fn make_format_args( for piece in &pieces { match *piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => { diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 5418f054bebd..09c88e7f83bb 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -16,11 +16,8 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::{iter, str, string}; - pub use Alignment::*; pub use Count::*; -pub use Piece::*; pub use Position::*; use rustc_lexer::unescape; @@ -86,7 +83,7 @@ impl InnerOffset { #[derive(Clone, Debug, PartialEq)] pub enum Piece<'a> { /// A literal string which should directly be emitted - String(&'a str), + Lit(&'a str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. NextArgument(Box>), @@ -205,11 +202,11 @@ pub enum Count<'a> { } pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, + pub description: String, + pub note: Option, + pub label: String, pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, + pub secondary_label: Option<(String, InnerSpan)>, pub suggestion: Suggestion, } @@ -225,7 +222,7 @@ pub enum Suggestion { /// `format!("{foo:?#}")` -> `format!("{foo:#?}")` /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` - ReorderFormatParameter(InnerSpan, string::String), + ReorderFormatParameter(InnerSpan, String), } /// The parser structure for interpreting the input format string. This is @@ -237,7 +234,7 @@ pub enum Suggestion { pub struct Parser<'a> { mode: ParseMode, input: &'a str, - cur: iter::Peekable>, + cur: std::iter::Peekable>, /// Error messages accumulated during parsing pub errors: Vec, /// Current position of implicit positional argument pointer @@ -278,7 +275,7 @@ impl<'a> Iterator for Parser<'a> { if self.consume('{') { self.last_opening_brace = curr_last_brace; - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { @@ -299,13 +296,13 @@ impl<'a> Iterator for Parser<'a> { _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } - Some(NextArgument(Box::new(arg))) + Some(Piece::NextArgument(Box::new(arg))) } } '}' => { self.cur.next(); if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let err_pos = self.to_span_index(pos); self.err_with_note( @@ -317,7 +314,7 @@ impl<'a> Iterator for Parser<'a> { None } } - _ => Some(String(self.string(pos))), + _ => Some(Piece::Lit(self.string(pos))), } } else { if self.is_source_literal { @@ -336,7 +333,7 @@ impl<'a> Parser<'a> { pub fn new( s: &'a str, style: Option, - snippet: Option, + snippet: Option, append_newline: bool, mode: ParseMode, ) -> Parser<'a> { @@ -366,7 +363,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err, S2: Into>( + fn err, S2: Into>( &mut self, description: S1, label: S2, @@ -385,11 +382,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( + fn err_with_note, S2: Into, S3: Into>( &mut self, description: S1, label: S2, @@ -968,7 +961,7 @@ impl<'a> Parser<'a> { /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( input: &str, - snippet: Option, + snippet: Option, str_style: Option, ) -> InputStringKind { let snippet = match snippet { @@ -1083,8 +1076,8 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } -fn unescape_string(string: &str) -> Option { - let mut buf = string::String::new(); +fn unescape_string(string: &str) -> Option { + let mut buf = String::new(); let mut ok = true; unescape::unescape_unicode(string, unescape::Mode::Str, &mut |_, unescaped_char| { match unescaped_char { diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 81e5bca0ba9f..fbb217b16fc3 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -1,3 +1,5 @@ +use Piece::*; + use super::*; #[track_caller] @@ -32,12 +34,12 @@ fn musterr(s: &str) { #[test] fn simple() { - same("asdf", &[String("asdf")]); - same("a{{b", &[String("a"), String("{b")]); - same("a}}b", &[String("a"), String("}b")]); - same("a}}", &[String("a"), String("}")]); - same("}}", &[String("}")]); - same("\\}}", &[String("\\"), String("}")]); + same("asdf", &[Lit("asdf")]); + same("a{{b", &[Lit("a"), Lit("{b")]); + same("a}}b", &[Lit("a"), Lit("}b")]); + same("a}}", &[Lit("a"), Lit("}")]); + same("}}", &[Lit("}")]); + same("\\}}", &[Lit("\\"), Lit("}")]); } #[test] @@ -370,7 +372,7 @@ fn format_flags() { #[test] fn format_mixture() { same("abcd {3:x} efg", &[ - String("abcd "), + Lit("abcd "), NextArgument(Box::new(Argument { position: ArgumentIs(3), position_span: InnerSpan { start: 7, end: 8 }, @@ -390,7 +392,7 @@ fn format_mixture() { ty_span: None, }, })), - String(" efg"), + Lit(" efg"), ]); } #[test] diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 4e0b097db4c6..3d79b0acf83a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -799,7 +799,7 @@ impl<'tcx> OnUnimplementedFormatString { let mut result = Ok(()); for token in &mut parser { match token { - Piece::String(_) => (), // Normal string, no need to check it + Piece::Lit(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => { let format_spec = a.format; if self.is_diagnostic_namespace_variant @@ -950,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString { let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); let constructed_message = (&mut parser) .map(|p| match p { - Piece::String(s) => s.to_owned(), + Piece::Lit(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(arg) => { let s = Symbol::intern(arg); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 68c7173d1e40..994ba2aa069d 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -229,7 +229,7 @@ impl ExprCollector<'_> { }; for piece in unverified_pieces { match piece { - rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::Lit(_) => {} rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index e64e498c1707..28c824fd31d7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -287,7 +287,7 @@ pub(crate) fn parse( for piece in pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(arg) => { From 6fb07164c202592831b8bd42f501be104a856213 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 28 Jan 2025 11:01:48 -0800 Subject: [PATCH 65/77] Update mdbook to 0.4.44 Updates to mdbook 0.4.44. Changelog: https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#mdbook-0444 --- src/tools/rustbook/Cargo.lock | 52 +++++++++++++++++------------------ src/tools/rustbook/Cargo.toml | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 86d2abcacb7f..b31bf61a6fba 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.16.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "byteorder" @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.26" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ "clap_builder", "clap_derive", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.26" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ "anstream", "anstyle", @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a7e468e750fa4b6be660e8b5651ad47372e8fb114030b594c2d75d48c5ffd0" +checksum = "0952013545c9c6dca60f491602655b795c6c062ab180c9cb0bccb83135461861" dependencies = [ "clap", ] @@ -253,9 +253,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -750,9 +750,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", @@ -867,9 +867,9 @@ dependencies = [ [[package]] name = "mdbook" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148" +checksum = "f9da1e54401fe5d45a664c57e112e70f18e8c5a73e268c179305b932ee864574" dependencies = [ "ammonia", "anyhow", @@ -1372,9 +1372,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.8.0", "errno", @@ -1391,9 +1391,9 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "same-file" @@ -1412,9 +1412,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" [[package]] name = "serde" @@ -1438,9 +1438,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa", "memchr", @@ -1732,9 +1732,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.14" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "unicode-width" @@ -1972,9 +1972,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.24" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310" dependencies = [ "memchr", ] diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index c2ce8fef4d0b..9f9846cdee07 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -14,6 +14,6 @@ mdbook-i18n-helpers = "0.3.3" mdbook-spec = { path = "../../doc/reference/mdbook-spec" } [dependencies.mdbook] -version = "0.4.37" +version = "0.4.44" default-features = false features = ["search"] From 3f8ce7c973e57c99e51df0c94ab3440be9580315 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Jan 2025 21:07:10 +0000 Subject: [PATCH 66/77] Do not assume child bound assumptions for rigid alias --- compiler/rustc_middle/src/ty/context.rs | 14 +++++ .../src/solve/assembly/mod.rs | 63 ++++++++++++++----- compiler/rustc_type_ir/src/interner.rs | 10 +++ ...opy-bound-from-child-rigid.current.stderr} | 2 +- ...ee-copy-bound-from-child-rigid.next.stderr | 14 +++++ .../cant-see-copy-bound-from-child-rigid.rs | 4 ++ .../const-traits/const-impl-trait.stderr | 52 ++++++++------- 7 files changed, 123 insertions(+), 36 deletions(-) rename tests/ui/associated-type-bounds/{cant-see-copy-bound-from-child-rigid.stderr => cant-see-copy-bound-from-child-rigid.current.stderr} (88%) create mode 100644 tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1f0710e24152..da4a86814820 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -345,6 +345,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.item_bounds(def_id).map_bound(IntoIterator::into_iter) } + fn item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_super_predicates(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_non_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_non_self_assumptions(def_id).map_bound(IntoIterator::into_iter) + } + fn predicates_of( self, def_id: DefId, diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63432dc199b9..d0b01b14d635 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -19,6 +19,11 @@ use crate::solve::{ MaybeCause, NoSolution, QueryResult, }; +enum AliasBoundKind { + SelfBounds, + NonSelfBounds, +} + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -510,7 +515,12 @@ where candidates: &mut Vec>, ) { let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); + ecx.assemble_alias_bound_candidates_recur( + goal.predicate.self_ty(), + goal, + candidates, + AliasBoundKind::SelfBounds, + ); }); } @@ -528,6 +538,7 @@ where self_ty: I::Ty, goal: Goal, candidates: &mut Vec>, + consider_self_bounds: AliasBoundKind, ) { let (kind, alias_ty) = match self_ty.kind() { ty::Bool @@ -580,16 +591,37 @@ where } }; - for assumption in - self.cx().item_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) - { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::AliasBound, - goal, - assumption, - [], - )); + match consider_self_bounds { + AliasBoundKind::SelfBounds => { + for assumption in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } + AliasBoundKind::NonSelfBounds => { + for assumption in self + .cx() + .item_non_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } } candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); @@ -600,9 +632,12 @@ where // Recurse on the self type of the projection. match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) { - Ok(next_self_ty) => { - self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates) - } + Ok(next_self_ty) => self.assemble_alias_bound_candidates_recur( + next_self_ty, + goal, + candidates, + AliasBoundKind::NonSelfBounds, + ), Err(NoSolution) => {} } } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 4fec606a8315..0c3b0758f0f9 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -203,6 +203,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + fn predicates_of( self, def_id: Self::DefId, diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr similarity index 88% rename from tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr rename to tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr index 3ed73918de31..0d57d9d0142d 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 | LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr new file mode 100644 index 000000000000..0d57d9d0142d --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs index 6b3fd7e898d1..fe135031b313 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Id { type This: ?Sized; } diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr index 4e3200594485..27d7957c0014 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -99,26 +99,6 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:23:22 - | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^ can't be applied to `PartialEq` - | -note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - -error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:23:22 - | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; - | ^^^^^^ can't be applied to `PartialEq` - | -note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` - --> $SRC_DIR/core/src/cmp.rs:LL:COL - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: `~const` can only be applied to `#[const_trait]` traits --> $DIR/const-impl-trait.rs:27:22 | @@ -149,6 +129,36 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:23:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error[E0015]: cannot call non-const operator in constants --> $DIR/const-impl-trait.rs:35:13 | @@ -181,7 +191,7 @@ LL | a == a | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. From 009d68740f660562c81c8565f5a5e6ec7ccfb4ae Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 22 Jan 2025 21:07:54 +0000 Subject: [PATCH 67/77] Make item self/non-self bound naming less whack --- compiler/rustc_hir_analysis/src/check/mod.rs | 2 +- compiler/rustc_hir_analysis/src/collect.rs | 14 +++--- .../src/collect/item_bounds.rs | 11 +++-- compiler/rustc_hir_typeck/src/closure.rs | 4 +- compiler/rustc_hir_typeck/src/coercion.rs | 44 +++++++++---------- .../rustc_infer/src/infer/outlives/verify.rs | 2 +- compiler/rustc_lint/src/unused.rs | 33 +++++++------- .../src/rmeta/decoder/cstore_impl.rs | 2 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 ++--- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 6 +-- compiler/rustc_middle/src/ty/context.rs | 6 +-- .../ty/return_position_impl_trait_in_trait.rs | 2 +- .../src/error_reporting/infer/mod.rs | 2 +- .../error_reporting/infer/note_and_explain.rs | 4 +- .../src/error_reporting/traits/suggestions.rs | 33 +++++++------- .../src/traits/select/mod.rs | 4 +- .../clippy_lints/src/future_not_send.rs | 2 +- src/tools/clippy/clippy_utils/src/ty/mod.rs | 6 +-- 19 files changed, 91 insertions(+), 100 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 69b4aa47ebad..cc0b7fdd8dd1 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -457,7 +457,7 @@ fn fn_sig_suggestion<'tcx>( let asyncness = if tcx.asyncness(assoc.def_id).is_async() { output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_super_predicates(alias_ty.def_id) + tcx.explicit_item_self_bounds(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| { bound.as_projection_clause()?.no_bound_vars()?.term.as_type() diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 447050ea7d20..cad7b2a1e57b 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -65,9 +65,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, - item_super_predicates: item_bounds::item_super_predicates, - explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, - item_non_self_assumptions: item_bounds::item_non_self_assumptions, + item_self_bounds: item_bounds::item_self_bounds, + explicit_item_self_bounds: item_bounds::explicit_item_self_bounds, + item_non_self_bounds: item_bounds::item_non_self_bounds, impl_super_outlives: item_bounds::impl_super_outlives, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, @@ -328,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().generics_of(def_id); self.tcx.ensure().predicates_of(def_id); self.tcx.ensure().explicit_item_bounds(def_id); - self.tcx.ensure().explicit_item_super_predicates(def_id); + self.tcx.ensure().explicit_item_self_bounds(def_id); self.tcx.ensure().item_bounds(def_id); - self.tcx.ensure().item_super_predicates(def_id); + self.tcx.ensure().item_self_bounds(def_id); if self.tcx.is_conditionally_const(def_id) { self.tcx.ensure().explicit_implied_const_bounds(def_id); self.tcx.ensure().const_conditions(def_id); @@ -822,7 +822,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, Some(_)) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -839,7 +839,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, None) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d3ff1f7bebe6..e37a11b68445 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -350,7 +350,7 @@ pub(super) fn explicit_item_bounds( explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) } -pub(super) fn explicit_item_super_predicates( +pub(super) fn explicit_item_self_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { @@ -434,11 +434,11 @@ pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, }) } -pub(super) fn item_super_predicates( +pub(super) fn item_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { - tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.explicit_item_self_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter( util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), ) @@ -447,13 +447,12 @@ pub(super) fn item_super_predicates( /// This exists as an optimization to compute only the item bounds of the item /// that are not `Self` bounds. -pub(super) fn item_non_self_assumptions( +pub(super) fn item_non_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); - let own_bounds: FxIndexSet<_> = - tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = tcx.item_self_bounds(def_id).skip_binder().iter().collect(); if all_bounds.len() == own_bounds.len() { ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) } else { diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8652d82d91b..d569d5f59c9e 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -308,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -981,7 +981,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 47abba1cc29c..153fd6001bbc 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1847,30 +1847,26 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( - |clause| { - let predicate = clause - .kind() - .map_bound(|clause| match clause { - ty::ClauseKind::Trait(trait_pred) => Some( - ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)), - ), - ty::ClauseKind::Projection(proj_pred) => { - Some(ty::ClauseKind::Projection( - proj_pred.with_self_ty(fcx.tcx, ty), - )) - } - _ => None, - }) - .transpose()?; - Some(Obligation::new( - fcx.tcx, - ObligationCause::dummy(), - fcx.param_env, - predicate, - )) - }, - ), + fcx.tcx.item_self_bounds(rpit_def_id).iter_identity().filter_map(|clause| { + let predicate = clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ClauseKind::Trait( + trait_pred.with_self_ty(fcx.tcx, ty), + )), + ty::ClauseKind::Projection(proj_pred) => Some( + ty::ClauseKind::Projection(proj_pred.with_self_ty(fcx.tcx, ty)), + ), + _ => None, + }) + .transpose()?; + Some(Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + predicate, + )) + }), ); ocx.select_where_possible().is_empty() }) diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 7a21c2883d1a..d68f3639176f 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -281,7 +281,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_super_predicates(alias_ty.def_id); + let bounds = tcx.item_self_bounds(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3059adb3fda1..5696fcaed137 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -289,25 +289,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate( - cx.tcx, - cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), - ) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) + is_def_must_use(cx, def_id, span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) } ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 527f2f10205a..da07ad8f6c07 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -241,7 +241,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { table_defaulted_array } - explicit_item_super_predicates => { table_defaulted_array } + explicit_item_self_bounds => { table_defaulted_array } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c538ab99fb54..904409dd777e 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1554,7 +1554,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { @@ -1667,10 +1667,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } - fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); - let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + fn encode_explicit_item_self_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_self_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_self_bounds(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds); } #[instrument(level = "debug", skip(self))] @@ -1685,7 +1685,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::Trait => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4f9cdc9a474b..d1d356c5220a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -386,7 +386,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, - explicit_item_super_predicates: Table, Span)>>, + explicit_item_self_bounds: Table, Span)>>, inferred_outlives_of: Table, Span)>>, explicit_super_predicates_of: Table, Span)>>, explicit_implied_predicates_of: Table, Span)>>, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e27a98236390..d83bc19a6a2f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -393,7 +393,7 @@ rustc_queries! { /// like closure signature deduction. /// /// [explicit item bounds]: Self::explicit_item_bounds - query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -427,11 +427,11 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } - query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index da4a86814820..0c22c056dab5 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -349,14 +349,14 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_super_predicates(def_id).map_bound(IntoIterator::into_iter) + self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) } fn item_non_self_bounds( self, def_id: DefId, ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { - self.item_non_self_assumptions(def_id).map_bound(IntoIterator::into_iter) + self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) } fn predicates_of( @@ -2591,7 +2591,7 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_self_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs index 21c605f8296d..cbc02097d827 100644 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -64,7 +64,7 @@ impl<'tcx> TyCtxt<'tcx> { args: ty::GenericArgsRef<'tcx>, ) -> &'tcx ty::List> { let mut bounds: Vec<_> = self - .item_super_predicates(def_id) + .item_self_bounds(def_id) .iter_instantiated(self, args) .filter_map(|clause| { clause diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bcb6ac13b8fa..5f3f65e46963 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(predicate, _)| { predicate diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 1dd09fe7aafa..e8d14b89d698 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -293,7 +293,7 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx - .explicit_item_super_predicates(alias.def_id) + .explicit_item_self_bounds(alias.def_id) .skip_binder() .iter() .any(|(pred, _span)| match pred.kind().skip_binder() { @@ -422,7 +422,7 @@ impl Trait for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); + let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 471105773e2b..f9cda83a5754 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1087,28 +1087,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self - .tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && self .tcx .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }), + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }, + ) + } ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0cc0d7f786b0..6b6e0b32385c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1620,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // projections, we will never be able to equate, e.g. `::A` // with `<::A as Tr>::A`. let relevant_bounds = if in_parent_alias_type { - self.tcx().item_non_self_assumptions(alias_ty.def_id) + self.tcx().item_non_self_bounds(alias_ty.def_id) } else { - self.tcx().item_super_predicates(alias_ty.def_id) + self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index bb2dc9995df7..3ccfa51ab70b 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) { - let preds = cx.tcx.explicit_item_super_predicates(def_id); + let preds = cx.tcx.explicit_item_self_bounds(def_id); let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { p.as_trait_clause() .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 32e7c2bbf7cb..607d76201028 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() { + for (predicate, _span) in cx.tcx.explicit_item_self_bounds(def_id).iter_identity_copied() { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. @@ -322,7 +322,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { - for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() { + for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; @@ -712,7 +712,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option sig_from_bounds( cx, ty, - cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), + cx.tcx.item_self_bounds(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), From c22a27130dde7d7ccf5d76fd021476ddbfdecfa0 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Mon, 27 Jan 2025 16:30:02 -0800 Subject: [PATCH 68/77] Refactor FnKind variant to hold &Fn --- compiler/rustc_ast/src/mut_visit.rs | 36 ++++++++----------- compiler/rustc_ast/src/visit.rs | 25 +++++++------ compiler/rustc_ast_lowering/src/expr.rs | 4 +-- .../rustc_ast_passes/src/ast_validation.rs | 22 ++++++------ .../rustc_ast_pretty/src/pprust/state/item.rs | 28 +++++---------- compiler/rustc_lint/src/builtin.rs | 8 +++-- compiler/rustc_resolve/src/def_collector.rs | 9 +++-- compiler/rustc_resolve/src/late.rs | 6 ++-- .../rustc_resolve/src/late/diagnostics.rs | 10 +++--- .../src/multiple_bound_locations.rs | 4 +-- src/tools/rustfmt/src/items.rs | 17 ++++----- src/tools/rustfmt/src/visitor.rs | 15 ++++++-- 12 files changed, 93 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3459d39131ac..7caf7c4c3568 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -954,8 +954,14 @@ fn walk_coroutine_kind(vis: &mut T, coroutine_kind: &mut Coroutin fn walk_fn(vis: &mut T, kind: FnKind<'_>) { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span }, _visibility, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness, generics, body, sig: FnSig { header, decl, span } }, + ) => { // Identifier and visibility are visited as a part of the item. + visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); @@ -1205,13 +1211,8 @@ impl WalkItemKind for ItemKind { ItemKind::Const(item) => { visit_const_item(item, vis); } - ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(vis, defaultness); - vis.visit_fn( - FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body), - span, - id, - ); + ItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id); } ItemKind::Mod(safety, mod_kind) => { visit_safety(vis, safety); @@ -1329,10 +1330,9 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::Const(item) => { visit_const_item(item, visitor); } - AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + AssocItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func), span, id, ); @@ -1476,10 +1476,9 @@ impl WalkItemKind for ForeignItemKind { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + ForeignItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func), span, id, ); @@ -1965,14 +1964,7 @@ impl DummyAstNode for crate::ast_traits::AstNo #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn( - FnCtxt, - &'a mut Ident, - &'a mut FnSig, - &'a mut Visibility, - &'a mut Generics, - &'a mut Option>, - ), + Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn), /// E.g., `|x, y| body`. Closure( diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1d6d7330757d..232fd546de9a 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -65,7 +65,7 @@ impl BoundKind { #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option>), + Fn(FnCtxt, &'a Ident, &'a Visibility, &'a Fn), /// E.g., `|x, y| body`. Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), @@ -74,7 +74,7 @@ pub enum FnKind<'a> { impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), + FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header), FnKind::Closure(..) => None, } } @@ -88,7 +88,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { - FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, + FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl, FnKind::Closure(_, _, decl, _) => decl, } } @@ -374,8 +374,8 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body); + ItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { @@ -715,8 +715,8 @@ impl WalkItemKind for ForeignItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body); + ForeignItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ForeignItemKind::TyAlias(box TyAlias { @@ -858,7 +858,12 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>( pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body }, + ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); @@ -892,8 +897,8 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body); + AssocItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } AssocItemKind::Type(box TyAlias { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f31e2c65c792..b932915dc298 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -2125,7 +2125,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_mut(span, e, args)) } - fn expr_call_lang_item_fn_mut( + pub(super) fn expr_call_lang_item_fn_mut( &mut self, span: Span, lang_item: hir::LangItem, @@ -2135,7 +2135,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr_call_mut(span, path, args) } - fn expr_call_lang_item_fn( + pub(super) fn expr_call_lang_item_fn( &mut self, span: Span, lang_item: hir::LangItem, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 236ca7ba7035..ea1f4a6559ac 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => { self.check_defaultness(item.span, *defaultness); let is_intrinsic = @@ -947,7 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Free, &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func); self.visit_fn(kind, item.span, item.id); walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. @@ -1348,19 +1348,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } if let FnKind::Fn( - _, - _, - FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, _, _, _, + Fn { + sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, + .. + }, ) = fk { self.maybe_lint_missing_abi(*extern_span, id); } // Functions without bodies cannot have patterns. - if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk { + if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { @@ -1394,7 +1395,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { - FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span }, FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); @@ -1470,15 +1471,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); match &item.kind { - AssocItemKind::Fn(box Fn { sig, generics, body, .. }) + AssocItemKind::Fn(func) if parent_is_const || ctxt == AssocCtxt::Trait - || matches!(sig.header.constness, Const::Yes(_)) => + || matches!(func.sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = - FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func); walk_list!(self, visit_attribute, &item.attrs); self.visit_fn(kind, item.span, item.id); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 897c275d850c..4cfcaa95233d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -34,8 +34,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::ForeignItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { self.print_item_const( @@ -199,16 +199,8 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - *defaultness, - body.as_deref(), - &item.attrs, - ); + ast::ItemKind::Fn(func) => { + self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func); } ast::ItemKind::Mod(safety, mod_kind) => { self.head(Self::to_string(|s| { @@ -542,8 +534,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::AssocItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( @@ -653,19 +645,17 @@ impl<'a> State<'a> { fn print_fn_full( &mut self, - sig: &ast::FnSig, name: Ident, - generics: &ast::Generics, vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, attrs: &[ast::Attribute], + func: &ast::Fn, ) { + let ast::Fn { defaultness, generics, sig, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); - self.print_defaultness(defaultness); + self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c03de687a338..e8a4e9a84c46 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -330,10 +330,12 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, _, - _, - body, + ast::Fn { + sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, + body, + .. + }, ) = fk { let decorator = match ctxt { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 87024c487df7..16c0a345f879 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -170,9 +170,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { match fn_kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) - if let Some(coroutine_kind) = header.coroutine_kind => - { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. }, + ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 68d3351f174c..4842cbd556c3 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -986,8 +986,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r match fn_kind { // Bail if the function is foreign, and thus cannot validly have // a body, or if there's no body for some other reason. - FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) - | FnKind::Fn(_, _, sig, _, generics, None) => { + FnKind::Fn(FnCtxt::Foreign, _, _, Fn { sig, generics, .. }) + | FnKind::Fn(_, _, _, Fn { sig, generics, body: None, .. }) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); self.with_lifetime_rib( @@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, sig, _, generics, body) => { + FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2db8087fd832..57679d595da3 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -224,7 +224,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diag_metadata.current_function && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() - && let FnKind::Fn(_, _, sig, ..) = fn_kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = fn_kind && let Some(items) = self.diag_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { i.ident.name == item_str.name @@ -560,7 +560,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Applicability::MaybeIncorrect, ); if !self.self_value_is_available(path[0].ident.span) { - if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = + if let Some((FnKind::Fn(_, _, _, ast::Fn { sig, .. }), fn_span)) = &self.diag_metadata.current_function { let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) { @@ -3249,7 +3249,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { let pre = if lt.kind == MissingLifetimeKind::Ampersand && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && !sig.decl.inputs.is_empty() && let sugg = sig .decl @@ -3290,7 +3290,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else if (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output && !sig.decl.inputs.is_empty() && let arg_refs = sig @@ -3350,7 +3350,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 882ab2dda7aa..0e1980a6acb6 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -1,5 +1,5 @@ use rustc_ast::visit::FnKind; -use rustc_ast::{NodeId, WherePredicateKind}; +use rustc_ast::{Fn, NodeId, WherePredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, _, generics, _) = kind + if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index e7d0fba048b4..457d0afe3b5b 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -333,19 +333,19 @@ impl<'a> FnSig<'a> { defaultness: ast::Defaultness, ) -> FnSig<'a> { match *fn_kind { - visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, fn_sig, vis, generics, _) => { - let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); + visit::FnKind::Fn(visit::FnCtxt::Assoc(..), _, vis, ast::Fn { sig, generics, .. }) => { + let mut fn_sig = FnSig::from_method_sig(sig, generics, vis); fn_sig.defaultness = defaultness; fn_sig } - visit::FnKind::Fn(_, _, fn_sig, vis, generics, _) => FnSig { + visit::FnKind::Fn(_, _, vis, ast::Fn { sig, generics, .. }) => FnSig { decl, generics, - ext: fn_sig.header.ext, - constness: fn_sig.header.constness, - coroutine_kind: Cow::Borrowed(&fn_sig.header.coroutine_kind), + ext: sig.header.ext, + constness: sig.header.constness, + coroutine_kind: Cow::Borrowed(&sig.header.coroutine_kind), defaultness, - safety: fn_sig.header.safety, + safety: sig.header.safety, visibility: vis, }, _ => unreachable!(), @@ -3453,6 +3453,7 @@ impl Rewrite for ast::ForeignItem { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let mut visitor = FmtVisitor::from_context(context); @@ -3461,7 +3462,7 @@ impl Rewrite for ast::ForeignItem { let inner_attrs = inner_attributes(&self.attrs); let fn_ctxt = visit::FnCtxt::Foreign; visitor.visit_fn( - visit::FnKind::Fn(fn_ctxt, &self.ident, sig, &self.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &self.ident, &self.vis, fn_kind), &sig.decl, self.span, defaultness, diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs index 805e13b78036..bdcb619153d8 100644 --- a/src/tools/rustfmt/src/visitor.rs +++ b/src/tools/rustfmt/src/visitor.rs @@ -386,7 +386,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { let indent = self.block_indent; let block; let rewrite = match fk { - visit::FnKind::Fn(_, ident, _, _, _, Some(ref b)) => { + visit::FnKind::Fn( + _, + ident, + _, + ast::Fn { + body: Some(ref b), .. + }, + ) => { block = b; self.rewrite_fn_before_block( indent, @@ -539,6 +546,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let inner_attrs = inner_attributes(&item.attrs); @@ -547,7 +555,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { _ => visit::FnCtxt::Foreign, }; self.visit_fn( - visit::FnKind::Fn(fn_ctxt, &item.ident, sig, &item.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &item.ident, &item.vis, fn_kind), &sig.decl, item.span, defaultness, @@ -640,12 +648,13 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { ref sig, ref generics, ref body, + .. } = **fn_kind; if body.is_some() { let inner_attrs = inner_attributes(&ai.attrs); let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt); self.visit_fn( - visit::FnKind::Fn(fn_ctxt, &ai.ident, sig, &ai.vis, generics, body), + visit::FnKind::Fn(fn_ctxt, &ai.ident, &ai.vis, fn_kind), &sig.decl, ai.span, defaultness, From 130b0d294a3404b5827869de5712009f91724700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 28 Jan 2025 19:35:31 +0000 Subject: [PATCH 69/77] Tweak `&mut self` suggestion span ``` error[E0596]: cannot borrow `*self.s` as mutable, as it is behind a `&` reference --> $DIR/issue-38147-1.rs:17:9 | LL | self.s.push('x'); | ^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable | help: consider changing this to be a mutable reference | LL | fn f(&mut self) { | +++ ``` Note the suggestion to add `mut` instead of replacing the entire `&self` with `&mut self`. --- .../src/diagnostics/mutability_errors.rs | 25 ++++++++----------- tests/ui/borrowck/issue-93093.rs | 2 +- tests/ui/borrowck/issue-93093.stderr | 2 +- .../trait-impl-argument-difference-ice.stderr | 4 +-- tests/ui/did_you_mean/issue-38147-1.stderr | 2 +- tests/ui/did_you_mean/issue-39544.stderr | 6 ++--- tests/ui/mut/mutable-class-fields-2.stderr | 2 +- tests/ui/suggestions/suggest-ref-mut.rs | 2 +- tests/ui/suggestions/suggest-ref-mut.stderr | 2 +- 9 files changed, 21 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a6ca038282d9..e841a5e4c948 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1140,10 +1140,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let amp_mut_sugg = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { - let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); - let additional = - local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); - Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional }) + let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); + let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); + Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1202,10 +1201,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info: None, .. })) => { - let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span); + let (span, sugg) = + suggest_ampmut_self(self.infcx.tcx, decl_span); Some(AmpMutSugg { has_sugg: true, - span: decl_span, + span, suggestion: sugg, additional: None, }) @@ -1461,17 +1461,12 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option(tcx: TyCtxt<'tcx>, span: Span) -> String { +fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { match tcx.sess.source_map().span_to_snippet(span) { - Ok(snippet) => { - let lt_pos = snippet.find('\''); - if let Some(lt_pos) = lt_pos { - format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) - } else { - "&mut self".to_string() - } + Ok(snippet) if snippet.ends_with("self") => { + (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string()) } - _ => "&mut self".to_string(), + _ => (span, "&mut self".to_string()), } } diff --git a/tests/ui/borrowck/issue-93093.rs b/tests/ui/borrowck/issue-93093.rs index e85b296c983f..1521b2072389 100644 --- a/tests/ui/borrowck/issue-93093.rs +++ b/tests/ui/borrowck/issue-93093.rs @@ -4,7 +4,7 @@ struct S { } impl S { async fn bar(&self) { //~ HELP consider changing this to be a mutable reference - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594] } } diff --git a/tests/ui/borrowck/issue-93093.stderr b/tests/ui/borrowck/issue-93093.stderr index b6a2768b61da..d788ce331973 100644 --- a/tests/ui/borrowck/issue-93093.stderr +++ b/tests/ui/borrowck/issue-93093.stderr @@ -7,7 +7,7 @@ LL | self.foo += 1; help: consider changing this to be a mutable reference | LL | async fn bar(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr index 5c70eccfbd35..190ddeaa8f28 100644 --- a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -41,7 +41,7 @@ LL | let a16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference --> $DIR/trait-impl-argument-difference-ice.rs:18:19 @@ -52,7 +52,7 @@ LL | let b16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error: aborting due to 5 previous errors; 1 warning emitted diff --git a/tests/ui/did_you_mean/issue-38147-1.stderr b/tests/ui/did_you_mean/issue-38147-1.stderr index a0392113ab15..6def86e4ba8f 100644 --- a/tests/ui/did_you_mean/issue-38147-1.stderr +++ b/tests/ui/did_you_mean/issue-38147-1.stderr @@ -7,7 +7,7 @@ LL | self.s.push('x'); help: consider changing this to be a mutable reference | LL | fn f(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39544.stderr b/tests/ui/did_you_mean/issue-39544.stderr index 8ccb4cbb0c16..62dc027e31f2 100644 --- a/tests/ui/did_you_mean/issue-39544.stderr +++ b/tests/ui/did_you_mean/issue-39544.stderr @@ -18,7 +18,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo<'z>(&'z mut self) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:20:17 @@ -29,7 +29,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo1(&mut self, other: &Z) { - | ~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:21:17 @@ -51,7 +51,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo2<'a>(&'a mut self, other: &Z) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:26:17 diff --git a/tests/ui/mut/mutable-class-fields-2.stderr b/tests/ui/mut/mutable-class-fields-2.stderr index eb0c54f885ba..7a6ff4da2bf5 100644 --- a/tests/ui/mut/mutable-class-fields-2.stderr +++ b/tests/ui/mut/mutable-class-fields-2.stderr @@ -7,7 +7,7 @@ LL | self.how_hungry -= 5; help: consider changing this to be a mutable reference | LL | pub fn eat(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index b40439b8e372..9f5df9303c33 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -3,7 +3,7 @@ struct X(usize); impl X { fn zap(&self) { //~^ HELP - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.0 = 32; //~^ ERROR } diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index cc00022ab8e3..935a04c052ac 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -7,7 +7,7 @@ LL | self.0 = 32; help: consider changing this to be a mutable reference | LL | fn zap(&mut self) { - | ~~~~~~~~~ + | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference --> $DIR/suggest-ref-mut.rs:15:5 From 5dfe0f8cf49110491ec0391c4b94b56834d65aef Mon Sep 17 00:00:00 2001 From: Mohammad Omidvar Date: Tue, 28 Jan 2025 19:45:20 +0000 Subject: [PATCH 70/77] Make crate AST mutation accessible for driver callback --- compiler/rustc_driver_impl/src/lib.rs | 6 +++--- src/doc/rustc-dev-guide/examples/rustc-driver-example.rs | 2 +- .../examples/rustc-driver-interacting-with-the-ast.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 20be21446098..c9d38a0f9325 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -160,7 +160,7 @@ pub trait Callbacks { fn after_crate_root_parsing( &mut self, _compiler: &interface::Compiler, - _queries: &ast::Crate, + _krate: &mut ast::Crate, ) -> Compilation { Compilation::Continue } @@ -311,7 +311,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - let krate = passes::parse(sess); + let mut krate = passes::parse(sess); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { @@ -328,7 +328,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) return early_exit(); } - if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop { + if callbacks.after_crate_root_parsing(compiler, &mut krate) == Compilation::Stop { return early_exit(); } diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 8983915d78a9..b0f9af1b8d10 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index c894b60444ac..8766a8173446 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); From dd616d6a16288a0e8d79405650db2e68762a4367 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:01:49 +0800 Subject: [PATCH 71/77] run-make-support: add `sysroot` helper Convenience helper for `rustc --print=sysroot`. --- src/tools/run-make-support/src/external_deps/rustc.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8894ea7fb209..b70db7130f67 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -1,5 +1,6 @@ use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::FromStr as _; use crate::command::Command; use crate::env::env_var; @@ -390,3 +391,10 @@ impl Rustc { self } } + +/// Query the sysroot path corresponding `rustc --print=sysroot`. +#[track_caller] +pub fn sysroot() -> PathBuf { + let path = rustc().print("sysroot").run().stdout_utf8(); + PathBuf::from_str(path.trim()).unwrap() +} From 5f513a6a888b4d71d7949a7d6ff2688c86febd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:03:21 +0800 Subject: [PATCH 72/77] run-make-support: improve docs for `assert_exit_code` --- src/tools/run-make-support/src/command.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index b4dc753ab534..70a72bd1abeb 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -388,9 +388,13 @@ impl CompletedProcess { self } + /// Check the **exit status** of the process. On Unix, this is *not* the **wait status**. + /// + /// See [`std::process::ExitStatus::code`]. This is not to be confused with + /// [`std::process::ExitCode`]. #[track_caller] pub fn assert_exit_code(&self, code: i32) -> &Self { - assert!(self.output.status.code() == Some(code)); + assert_eq!(self.output.status.code(), Some(code)); self } } From 6f5a1035f8b3694e519598ddb27d884a591e0d32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:03:18 +0800 Subject: [PATCH 73/77] tests: port `translation` to rmake.rs Co-authored-by: Oneirical --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - tests/run-make/translation/Makefile | 78 ------- tests/run-make/translation/rmake.rs | 194 ++++++++++++++++++ 3 files changed, 194 insertions(+), 79 deletions(-) delete mode 100644 tests/run-make/translation/Makefile create mode 100644 tests/run-make/translation/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 6f0fd09b353a..e75d3dc2147b 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,3 +1,2 @@ run-make/split-debuginfo/Makefile run-make/symbol-mangling-hashed/Makefile -run-make/translation/Makefile diff --git a/tests/run-make/translation/Makefile b/tests/run-make/translation/Makefile deleted file mode 100644 index 07e0547cfa09..000000000000 --- a/tests/run-make/translation/Makefile +++ /dev/null @@ -1,78 +0,0 @@ -include ../tools.mk - -# This test uses `ln -s` rather than copying to save testing time, but its -# usage doesn't work on Windows. -# ignore-windows - -SYSROOT:=$(shell $(RUSTC) --print sysroot) -FAKEROOT=$(TMPDIR)/fakeroot -RUSTC_LOG:=rustc_error_messages -export RUSTC_TRANSLATION_NO_DEBUG_ASSERT:=1 - -all: normal custom missing broken sysroot sysroot-invalid sysroot-missing - -# Check that the test works normally, using the built-in fallback bundle. -normal: test.rs - $(RUSTC) $< 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a primary bundle can be loaded and will be preferentially used -# where possible. -custom: test.rs working.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/working.ftl 2>&1 | $(CGREP) "this is a test message" - -# Check that a primary bundle with a broken message (e.g. a interpolated -# variable is missing) will use the fallback bundle. -missing: test.rs missing.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/missing.ftl 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a primary bundle without the desired message will use the fallback -# bundle. -broken: test.rs broken.ftl - $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/broken.ftl 2>&1 | $(CGREP) "struct literal body without path" - -# Check that a locale can be loaded from the sysroot given a language -# identifier by making a local copy of the sysroot and adding the custom locale -# to it. -sysroot: test.rs working.ftl - rm -rf $(FAKEROOT) - mkdir $(FAKEROOT) - ln -s $(SYSROOT)/* $(FAKEROOT) - rm -f $(FAKEROOT)/lib - mkdir $(FAKEROOT)/lib - ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib - rm -f $(FAKEROOT)/lib/rustlib - mkdir $(FAKEROOT)/lib/rustlib - ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib - rm -f $(FAKEROOT)/lib/rustlib/src - mkdir $(FAKEROOT)/lib/rustlib/src - ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src - # When download-rustc is enabled, `$(SYSROOT)` will have a share directory. Delete the link to it. - rm -f $(FAKEROOT)/share - mkdir -p $(FAKEROOT)/share/locale/zh-CN/ - ln -s $(CURDIR)/working.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl - $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | $(CGREP) "this is a test message" - -# Check that the compiler errors out when the sysroot requested cannot be -# found. This test might start failing if there actually exists a Klingon -# translation of rustc's error messages. -sysroot-missing: - $(RUSTC) $< -Ztranslate-lang=tlh 2>&1 | $(CGREP) "missing locale directory" - -# Check that the compiler errors out when the directory for the locale in the -# sysroot is actually a file. -sysroot-invalid: test.rs working.ftl - rm -rf $(FAKEROOT) - mkdir $(FAKEROOT) - ln -s $(SYSROOT)/* $(FAKEROOT) - rm -f $(FAKEROOT)/lib - mkdir $(FAKEROOT)/lib - ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib - rm -f $(FAKEROOT)/lib/rustlib - mkdir $(FAKEROOT)/lib/rustlib - ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib - rm -f $(FAKEROOT)/lib/rustlib/src - mkdir $(FAKEROOT)/lib/rustlib/src - ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src - mkdir -p $(FAKEROOT)/share/locale - touch $(FAKEROOT)/share/locale/zh-CN - $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | $(CGREP) "`\$sysroot/share/locales/\$locale` is not a directory" diff --git a/tests/run-make/translation/rmake.rs b/tests/run-make/translation/rmake.rs new file mode 100644 index 000000000000..86078888c2e1 --- /dev/null +++ b/tests/run-make/translation/rmake.rs @@ -0,0 +1,194 @@ +//! Smoke test for the rustc diagnostics translation infrastructure. +//! +//! # References +//! +//! - Current tracking issue: . +//! - Old tracking issue: +//! - Initial translation infra implementation: . + +// This test uses symbolic links to stub out a fake sysroot to save testing time. +//@ needs-symlink +//@ needs-subprocess + +#![deny(warnings)] + +use std::path::{Path, PathBuf}; + +use run_make_support::rustc::sysroot; +use run_make_support::{cwd, rfs, run_in_tmpdir, rustc}; + +fn main() { + builtin_fallback_bundle(); + additional_primary_bundle(); + missing_slug_prefers_fallback_bundle(); + broken_primary_bundle_prefers_fallback_bundle(); + locale_sysroot(); + missing_sysroot(); + file_sysroot(); +} + +/// Check that the test works normally, using the built-in fallback bundle. +fn builtin_fallback_bundle() { + rustc().input("test.rs").run_fail().assert_stderr_contains("struct literal body without path"); +} + +/// Check that a primary bundle can be loaded and will be preferentially used where possible. +fn additional_primary_bundle() { + rustc() + .input("test.rs") + .arg("-Ztranslate-additional-ftl=working.ftl") + .run_fail() + .assert_stderr_contains("this is a test message"); +} + +/// Check that a primary bundle without the desired message will use the fallback bundle. +fn missing_slug_prefers_fallback_bundle() { + rustc() + .input("test.rs") + .arg("-Ztranslate-additional-ftl=missing.ftl") + .run_fail() + .assert_stderr_contains("struct literal body without path"); +} + +/// Check that a primary bundle with a broken message (e.g. an interpolated variable is not +/// provided) will use the fallback bundle. +fn broken_primary_bundle_prefers_fallback_bundle() { + // FIXME(#135817): as of the rmake.rs port, the compiler actually ICEs on the additional + // `broken.ftl`, even though the original intention seems to be that it should gracefully + // failover to the fallback bundle. On `aarch64-apple-darwin`, somehow it *doesn't* ICE. + + rustc() + .env("RUSTC_ICE", "0") // disable ICE dump file, not needed + .input("test.rs") + .arg("-Ztranslate-additional-ftl=broken.ftl") + .run_fail(); +} + +#[track_caller] +fn shallow_symlink_dir_entries(src_dir: &Path, dst_dir: &Path) { + for entry in rfs::read_dir(src_dir) { + let entry = entry.unwrap(); + let src_entry_path = entry.path(); + let src_filename = src_entry_path.file_name().unwrap(); + let meta = rfs::symlink_metadata(&src_entry_path); + if meta.is_symlink() || meta.is_file() { + rfs::symlink_file(&src_entry_path, dst_dir.join(src_filename)); + } else if meta.is_dir() { + rfs::symlink_dir(&src_entry_path, dst_dir.join(src_filename)); + } else { + unreachable!() + } + } +} + +#[track_caller] +fn shallow_symlink_dir_entries_materialize_single_dir( + src_dir: &Path, + dst_dir: &Path, + dir_filename: &str, +) { + shallow_symlink_dir_entries(src_dir, dst_dir); + + let dst_symlink_meta = rfs::symlink_metadata(dst_dir.join(dir_filename)); + + if dst_symlink_meta.is_file() || dst_symlink_meta.is_dir() { + unreachable!(); + } + + #[cfg(windows)] + { + use std::os::windows::fs::FileTypeExt as _; + if dst_symlink_meta.file_type().is_symlink_file() { + rfs::remove_file(dst_dir.join(dir_filename)); + } else if dst_symlink_meta.file_type().is_symlink_dir() { + rfs::remove_dir(dst_dir.join(dir_filename)); + } else { + unreachable!(); + } + } + #[cfg(not(windows))] + { + rfs::remove_file(dst_dir.join(dir_filename)); + } + + rfs::create_dir_all(dst_dir.join(dir_filename)); +} + +#[track_caller] +fn setup_fakeroot_parents() -> PathBuf { + let sysroot = sysroot(); + let fakeroot = cwd().join("fakeroot"); + rfs::create_dir_all(&fakeroot); + shallow_symlink_dir_entries_materialize_single_dir(&sysroot, &fakeroot, "lib"); + shallow_symlink_dir_entries_materialize_single_dir( + &sysroot.join("lib"), + &fakeroot.join("lib"), + "rustlib", + ); + shallow_symlink_dir_entries_materialize_single_dir( + &sysroot.join("lib").join("rustlib"), + &fakeroot.join("lib").join("rustlib"), + "src", + ); + shallow_symlink_dir_entries( + &sysroot.join("lib").join("rustlib").join("src"), + &fakeroot.join("lib").join("rustlib").join("src"), + ); + fakeroot +} + +/// Check that a locale can be loaded from the sysroot given a language identifier by making a local +/// copy of the sysroot and adding the custom locale to it. +fn locale_sysroot() { + run_in_tmpdir(|| { + let fakeroot = setup_fakeroot_parents(); + + // When download-rustc is enabled, real sysroot will have a share directory. Delete the link + // to it. + let _ = std::fs::remove_file(fakeroot.join("share")); + + let fake_locale_path = fakeroot.join("share").join("locale").join("zh-CN"); + rfs::create_dir_all(&fake_locale_path); + rfs::symlink_file( + cwd().join("working.ftl"), + fake_locale_path.join("basic-translation.ftl"), + ); + + rustc() + .env("RUSTC_ICE", "0") + .input("test.rs") + .sysroot(&fakeroot) + .arg("-Ztranslate-lang=zh-CN") + .run_fail() + .assert_stderr_contains("this is a test message"); + }); +} + +/// Check that the compiler errors out when the sysroot requested cannot be found. This test might +/// start failing if there actually exists a Klingon translation of rustc's error messages. +fn missing_sysroot() { + run_in_tmpdir(|| { + rustc() + .input("test.rs") + .arg("-Ztranslate-lang=tlh") + .run_fail() + .assert_stderr_contains("missing locale directory"); + }); +} + +/// Check that the compiler errors out when the directory for the locale in the sysroot is actually +/// a file. +fn file_sysroot() { + run_in_tmpdir(|| { + let fakeroot = setup_fakeroot_parents(); + rfs::create_dir_all(fakeroot.join("share").join("locale")); + rfs::write(fakeroot.join("share").join("locale").join("zh-CN"), b"not a dir"); + + rustc() + .input("test.rs") + .sysroot(&fakeroot) + .arg("-Ztranslate-lang=zh-CN") + .run_fail() + .assert_stderr_contains("is not a directory"); + }); +} From f47ad710595c1d87daa8f6b67045574c7304f83e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 12 Dec 2024 10:37:37 +0000 Subject: [PATCH 74/77] Eliminate PatKind::Path --- compiler/rustc_ast_lowering/src/expr.rs | 6 +- compiler/rustc_ast_lowering/src/pat.rs | 33 +++++++---- compiler/rustc_hir/src/hir.rs | 7 +-- compiler/rustc_hir/src/intravisit.rs | 3 - compiler/rustc_hir/src/pat_util.rs | 5 +- .../rustc_hir_analysis/src/check/region.rs | 1 - .../src/hir_ty_lowering/lint.rs | 4 +- compiler/rustc_hir_pretty/src/lib.rs | 3 - compiler/rustc_hir_typeck/src/expr.rs | 1 - .../rustc_hir_typeck/src/expr_use_visitor.rs | 12 ++-- .../src/fn_ctxt/suggestions.rs | 10 ++-- .../rustc_hir_typeck/src/method/suggest.rs | 3 +- compiler/rustc_hir_typeck/src/pat.rs | 56 +++++++++++-------- compiler/rustc_lint/src/internal.rs | 10 ++-- compiler/rustc_lint/src/nonstandard_style.rs | 8 ++- .../rustc_mir_build/src/thir/pattern/mod.rs | 4 -- compiler/rustc_passes/src/dead.rs | 7 +-- compiler/rustc_passes/src/input_stats.rs | 1 - src/librustdoc/clean/utils.rs | 3 +- src/librustdoc/html/render/span_map.rs | 26 +++++---- .../clippy_lints/src/equatable_if_let.rs | 2 +- .../clippy_lints/src/manual_let_else.rs | 9 ++- .../src/manual_unwrap_or_default.rs | 4 +- .../src/matches/collapsible_match.rs | 8 ++- .../clippy_lints/src/matches/manual_ok_err.rs | 13 ++++- .../src/matches/manual_unwrap_or.rs | 8 ++- .../clippy_lints/src/matches/manual_utils.rs | 10 ++-- .../clippy_lints/src/matches/match_as_ref.rs | 4 +- .../src/matches/match_same_arms.rs | 8 ++- .../src/matches/match_wild_enum.rs | 11 +++- .../src/matches/needless_match.rs | 12 +++- .../src/matches/redundant_pattern_match.rs | 36 +++++++++--- .../clippy_lints/src/matches/single_match.rs | 21 +++---- .../clippy_lints/src/option_if_let_else.rs | 10 +++- src/tools/clippy/clippy_lints/src/use_self.rs | 4 +- .../clippy/clippy_lints/src/utils/author.rs | 5 -- .../clippy/clippy_utils/src/hir_utils.rs | 2 - src/tools/clippy/clippy_utils/src/lib.rs | 25 +++++++-- tests/ui/thir-print/thir-tree-match.stdout | 52 ++++++++--------- 39 files changed, 273 insertions(+), 174 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index b932915dc298..1267281f73eb 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1391,7 +1391,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); // Destructure like a unit struct. - let unit_struct_pat = hir::PatKind::Path(qpath); + let unit_struct_pat = hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + hir_id: self.next_id(), + span: self.lower_span(lhs.span), + })); return self.pat_without_dbm(lhs.span, unit_struct_pat); } } diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 3c78ed0497d7..cde8ddbfe03c 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -69,7 +69,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); - break hir::PatKind::Path(qpath); + let kind = hir::PatExprKind::Path(qpath); + let span = self.lower_span(pattern.span); + let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind }; + let expr = self.arena.alloc(expr); + return hir::Pat { + hir_id: self.next_id(), + kind: hir::PatKind::Expr(expr), + span, + default_binding_modes: true, + }; } PatKind::Struct(qself, path, fields, etc) => { let qpath = self.lower_qpath( @@ -304,16 +313,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } Some(res) => { - let hir_id = self.next_id(); let res = self.lower_res(res); - hir::PatKind::Path(hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - span: self.lower_span(ident.span), - res, - segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], - }), - )) + let span = self.lower_span(ident.span); + hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)], + }), + )), + hir_id: self.next_id(), + span, + })) } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a396d705cbb6..af2f86b67e00 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1437,7 +1437,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), @@ -1464,7 +1464,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), @@ -1618,9 +1618,6 @@ pub enum PatKind<'hir> { /// A never pattern `!`. Never, - /// A path pattern for a unit struct/variant or a (maybe-associated) constant. - Path(QPath<'hir>), - /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option` denotes its position. /// `0 <= position <= subpats.len()` diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b0d80d0f8092..31764ab12094 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -709,9 +709,6 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat, children); } - PatKind::Path(ref qpath) => { - try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); - } PatKind::Struct(ref qpath, fields, _) => { try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat_field, fields); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index bb853985c7d8..8dd1e4e82927 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -105,7 +105,10 @@ impl hir::Pat<'_> { let mut variants = vec![]; self.walk(|p| match &p.kind { PatKind::Or(_) => false, - PatKind::Path(hir::QPath::Resolved(_, path)) + PatKind::Expr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved(_, path)), + .. + }) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) = diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 83c69dc2ef41..d43c65c0023b 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -703,7 +703,6 @@ fn resolve_local<'tcx>( | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Never - | PatKind::Path(_) | PatKind::Expr(_) | PatKind::Range(_, _, _) | PatKind::Err(_) => false, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 539c5f6a20af..44f7a035a10f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -41,8 +41,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) - | hir::Node::Pat(hir::Pat { - kind: hir::PatKind::Path(hir::QPath::TypeRelative(qself, _)), + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) if qself.hir_id == self_ty.hir_id => true, _ => false, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index afc0f627f696..a91afa51230d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1906,9 +1906,6 @@ impl<'a> State<'a> { } self.pclose(); } - PatKind::Path(ref qpath) => { - self.print_qpath(qpath, true); - } PatKind::Struct(ref qpath, fields, etc) => { self.print_qpath(qpath, true); self.nbsp(); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1c828591bcbd..f79667e59bae 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -480,7 +480,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::Binding(_, _, _, _) | hir::PatKind::Struct(_, _, _) | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) | hir::PatKind::Tuple(_, _) | hir::PatKind::Box(_) | hir::PatKind::Ref(_, _) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 1f48b703e4ac..d37c68f82fb2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -11,10 +11,9 @@ use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, PatKind}; +use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. @@ -564,11 +563,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // FIXME(never_patterns): does this do what I expect? needs_to_be_read = true; } - PatKind::Path(qpath) => { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant - let res = self.cx.typeck_results().qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); match res { Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { // Named constants have to be equated with the value @@ -581,7 +580,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. needs_to_be_read |= - self.is_multivariant_adt(place.place.ty(), pat.span); + self.is_multivariant_adt(place.place.ty(), *span); } } } @@ -1801,8 +1800,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Path(_) - | PatKind::Binding(.., None) + PatKind::Binding(.., None) | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 928010c03c2e..aea2e0fd3a30 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,12 +5,12 @@ use hir::def_id::LocalDefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, + self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, + GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, + WherePredicateKind, expr_needs_parens, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -1422,8 +1422,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // since the user probably just misunderstood how `let else` // and `&&` work together. if let Some((_, hir::Node::LetStmt(local))) = cond_parent - && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = - &local.pat.kind + && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath && let Some(did) = path .res diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 5d4e67d1a0e6..e4a6a0fedc5a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -177,8 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) | hir::Node::Pat(&hir::Pat { kind: - hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) - | hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) + hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) | hir::PatKind::TupleStruct(QPath::TypeRelative(rcvr, segment), ..), span, .. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cbd1db2ca255..0aa2f1110fb3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,8 +11,8 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ - self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, - expr_needs_parens, + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, + PatExprKind, PatKind, expr_needs_parens, }; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; @@ -312,9 +312,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; - let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + let path_res = match pat.kind { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) } _ => None, }; @@ -333,6 +333,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + let ty = self.check_pat_path( + *hir_id, + pat.hir_id, + *span, + qpath, + path_res.unwrap(), + expected, + ti, + ); + self.write_ty(*hir_id, ty); + ty + } PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, ident, sub) => { @@ -341,9 +354,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat.hir_id, pat.span, qpath, path_res.unwrap(), expected, ti) - } PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } @@ -456,16 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. PatKind::Never => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() { // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. // Peeling the reference types too early will cause type checking failures. // Although it would be possible to *also* peel the types of the constants too. @@ -476,6 +477,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { + ty::Ref(..) => AdjustMode::Pass, + _ => AdjustMode::Peel, + }, + // Ref patterns are complicated, we handle them in `check_pat_ref`. PatKind::Ref(..) => AdjustMode::Pass, // A `_` pattern works with any expected type, so there's no need to do anything. @@ -1001,7 +1013,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Never | PatKind::Binding(..) - | PatKind::Path(..) | PatKind::Box(..) | PatKind::Deref(_) | PatKind::Ref(..) @@ -1139,7 +1150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path( &self, - hir_id: HirId, + path_id: HirId, + pat_id_for_diag: HirId, span: Span, qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), @@ -1193,11 +1205,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, hir_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); if let Err(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) { - self.emit_bad_pat_path(err, hir_id, span, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); } pat_ty } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 166ff60f7e1e..546df4497adf 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -6,7 +6,7 @@ use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, - PatKind, Path, PathSegment, QPath, Ty, TyKind, + PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,11 +164,9 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::Pat(Pat { - kind: - PatKind::Path(qpath) - | PatKind::TupleStruct(qpath, ..) - | PatKind::Struct(qpath, ..), + Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | Node::Pat(Pat { + kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. }) | Node::Expr( diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e09049f322fa..0c180ab95706 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,7 @@ use rustc_abi::ExternAbi; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; @@ -527,7 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { // Lint for constants that look like binding identifiers (#7526) - if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { + if let PatKind::Expr(hir::PatExpr { + kind: PatExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = p.kind + { if let Res::Def(DefKind::Const, _) = path.res { if let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 44b038bb5faf..20a728d6d5b2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -332,10 +332,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .unwrap_or_else(PatKind::Error) } - hir::PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - hir::PatKind::Deref(subpattern) => { let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e5b63b9b4a60..bc2bebdfef68 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,11 +10,10 @@ use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatExpr, PatExprKind, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -637,8 +636,8 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, pat.hir_id); + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, .. }) => { + let res = self.typeck_results().qpath_res(qpath, *hir_id); self.handle_res(res); } PatKind::TupleStruct(ref qpath, fields, dotdot) => { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 8b10543f6fdb..75b62a40ff9f 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -298,7 +298,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TupleStruct, Or, Never, - Path, Tuple, Box, Deref, diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 80dc6b7250cc..77040aeb94d7 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::TupleStruct(ref p, ..) + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), PatKind::Or(pats) => { pats.iter().map(|p| name_from_pat(p).to_string()).collect::>().join(" | ") } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 85f87f01afdf..a15ac155123d 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,7 +4,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatKind, QPath}; +use rustc_hir::{ + ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, +}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -191,17 +193,21 @@ impl SpanMapVisitor<'_> { } fn handle_pat(&mut self, p: &Pat<'_>) { + let mut check_qpath = |qpath, hir_id| match qpath { + QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { + self.infer_id(path.hir_id, Some(hir_id), qpath.span()); + } + QPath::Resolved(_, path) => self.handle_path(path), + _ => {} + }; match p.kind { PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) - | PatKind::TupleStruct(qpath, _, _) - | PatKind::Path(qpath) => match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(p.hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }, + PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { + check_qpath(qpath, p.hir_id) + } + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { + check_qpath(*qpath, *hir_id) + } PatKind::Or(pats) => { for pat in pats { self.handle_pat(pat); diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 8a5cf7f56d5f..7ca2c9536998 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), - PatKind::Path(_) | PatKind::Expr(_) => true, + PatKind::Expr(_) => true, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 8503dde3fb6b..274785061b3f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -292,7 +292,12 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo // Only do the check if the type is "spelled out" in the pattern if !matches!( pat.kind, - PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Expr(PatExpr { + kind: PatExprKind::Path(..), + .. + },) ) { return; } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 8f8390b6f3f9..7b95399c907c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::GenericArgKind; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { } fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 99a7b8c74be8..97e8423695d9 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; +use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -119,7 +119,11 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index b1a555b91d1b..3deaaf96c1e8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -6,7 +6,7 @@ use rustc_ast::BindingMode; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::symbol::Ident; @@ -60,7 +60,16 @@ pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Exp /// accepted. fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool { match pat.kind { - PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true, + PatKind::Wild + | PatKind::Expr(PatExpr { + kind: PatExprKind::Path(_), + .. + }) + | PatKind::Binding(_, _, _, None) + if can_be_wild => + { + true + }, PatKind::TupleStruct(qpath, ..) => { is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err }, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 59d375200117..b69294d567d1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -7,7 +7,7 @@ use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatKind}; +use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::sym; @@ -89,7 +89,11 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(& if arms.len() == 2 && arms.iter().all(|arm| arm.guard.is_none()) && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(qpath), + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => { matches!(pat.kind, PatKind::Wild) && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index 0b57740064c1..2ad55d9bf1fd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def::Res; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -256,9 +256,11 @@ pub(super) fn try_parse_pattern<'tcx>( match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), PatKind::TupleStruct(ref qpath, [pattern], _) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 6c123649afc2..b1889d26c932 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -59,7 +59,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { matches!( arm.pat.kind, - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) ) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 28e05c273d5c..41e4c75f843e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -292,7 +292,11 @@ impl<'a> NormalizedPat<'a> { Self::Tuple(var_id, pats) }, PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))), - PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => Self::Path(cx.qpath_res(path, *hir_id).opt_def_id()), PatKind::Tuple(pats, wild_idx) => { let field_count = match cx.typeck_results().pat_ty(pat).kind() { ty::Tuple(subs) => subs.len(), diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 595655600890..11b588b33554 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; +use rustc_hir::{Arm, Expr, PatExpr, PatExprKind, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; use rustc_span::sym; @@ -60,8 +60,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // covered by the set of guards that cover it, but that's really hard to do. recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { - PatKind::Path(path) => { - let id = match cx.qpath_res(path, pat.hir_id) { + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(path), + .. + }) => { + // FIXME(clippy): don't you want to use the hir id of the peeled pat? + let id = match cx.qpath_res(path, *hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, _, diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 0d5575efc220..7e65d586110e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; +use rustc_hir::{ + Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, +}; use rustc_lint::LateContext; use rustc_span::sym; @@ -183,7 +185,13 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` - (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(QPath::Resolved(_, p_path)), + .. + }), + ExprKind::Path(QPath::Resolved(_, e_path)), + ) => { return over(p_path.segments, e_path.segments, |p_seg, e_seg| { p_seg.ident.name == e_seg.ident.name }); diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index edac97344a03..393399660131 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -149,8 +149,12 @@ fn find_method_and_type<'tcx>( None } }, - PatKind::Path(ref path) => { - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => { + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, *hir_id) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) { let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { @@ -351,10 +355,20 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right)) - | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _)) - if patterns.len() == 1 => - { + ( + PatKind::TupleStruct(path_left, patterns, _), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_right), + .. + }), + ) + | ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::TupleStruct(path_right, patterns, _), + ) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( cx, @@ -389,7 +403,13 @@ fn found_good_method<'tcx>( None } }, - (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::Wild, + ) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 38f876fed802..2f46eaaabb36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind + let (msg, sugg) = if let PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -331,14 +331,16 @@ impl<'a> PatState<'a> { #[expect(clippy::similar_names)] fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { match pat.kind { - PatKind::Path(_) - if match *cx.typeck.pat_ty(pat).peel_refs().kind() { - ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), - ty::Tuple(tys) => !tys.is_empty(), - ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), - ty::Slice(..) => true, - _ => false, - } => + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(_), + .. + }) if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), + ty::Slice(..) => true, + _ => false, + } => { matches!(self, Self::Wild) }, @@ -386,7 +388,6 @@ impl<'a> PatState<'a> { | PatKind::Binding(_, _, _, None) | PatKind::Expr(_) | PatKind::Range(..) - | PatKind::Path(_) | PatKind::Never | PatKind::Err(_) => { *self = PatState::Wild; diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 6d9e75f51d66..de9f055863cb 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -7,7 +7,9 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; -use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; +use rustc_hir::{ + Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -281,7 +283,11 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) && matches!(first_pat.kind, PatKind::Wild) diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 84b6430294f3..47ce2243aa09 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -258,7 +258,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() // get the path from the pattern - && let PatKind::Path(QPath::Resolved(_, path)) + && let PatKind::Expr(&PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 4dcc8ac7fb0a..6bad78cf8718 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -708,11 +708,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.qpath(qpath); self.slice(fields, |pat| self.pat(pat)); }, - PatKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, PatKind::Tuple(fields, skip_pos) => { bind!(self, fields); kind!("Tuple({fields}, {skip_pos:?})"); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index d76231a6eeaa..d0eb5318e645 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -524,7 +524,6 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -1120,7 +1119,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); } }, - PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { self.hash_pat_expr(s); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index e471dfb6ef19..5a5227af9074 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -106,8 +106,8 @@ use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, - TyKind, UnOp, def, + PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, + TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -560,7 +560,20 @@ macro_rules! maybe_path { }; } maybe_path!(Expr, ExprKind); -maybe_path!(Pat, PatKind); +impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn hir_id(&self) -> HirId { + self.hir_id + } + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match &self.kind { + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + .. + }) => Some(qpath), + _ => None, + } + } +} maybe_path!(Ty, TyKind); /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` @@ -1753,7 +1766,11 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), - PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_enum_variant(cx, qpath, *hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set are_refutable(cx, pats) diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 916f296ccfc6..910582ae4d9e 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -26,16 +26,16 @@ params: [ body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Scope { - region_scope: Node(26) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + region_scope: Node(28) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).28)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Block { @@ -47,7 +47,7 @@ body: expr: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Scope { @@ -56,14 +56,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Match { scrutinee: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: Scope { @@ -72,7 +72,7 @@ body: value: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: VarRef { @@ -123,16 +123,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { - region_scope: Node(14) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + region_scope: Node(15) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).15)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -140,8 +140,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).13)) - scope: Node(13) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + scope: Node(14) span: $DIR/thir-tree-match.rs:17:9: 17:40 (#0) } Arm { @@ -175,16 +175,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { - region_scope: Node(20) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + region_scope: Node(21) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).21)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -192,8 +192,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).19)) - scope: Node(19) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + scope: Node(20) span: $DIR/thir-tree-match.rs:18:9: 18:32 (#0) } Arm { @@ -219,16 +219,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { - region_scope: Node(25) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).25)) + region_scope: Node(27) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).27)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) @@ -236,8 +236,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).24)) - scope: Node(24) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + scope: Node(26) span: $DIR/thir-tree-match.rs:19:9: 19:28 (#0) } ] From 8f09abb49757a65f143c6794fcb22a70945e67d1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 29 Jan 2025 08:38:56 +0000 Subject: [PATCH 75/77] Add regression test showing we don't realize some consts are used --- tests/ui/lint/dead-code/lint-dead-code-1.rs | 2 ++ tests/ui/lint/dead-code/lint-dead-code-1.stderr | 14 +++++++------- tests/ui/pattern/issue-110508.rs | 8 +++++++- tests/ui/pattern/issue-110508.stderr | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 tests/ui/pattern/issue-110508.stderr diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5..a7f654b5d8bf 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -29,6 +29,7 @@ const used_const: isize = 0; pub const used_const2: isize = used_const; const USED_CONST: isize = 1; const CONST_USED_IN_ENUM_DISCRIMINANT: isize = 11; +const CONST_USED_IN_RANGE_PATTERN: isize = 12; pub type typ = *const UsedStruct4; pub struct PubStruct; @@ -81,6 +82,7 @@ pub fn pub_fn() { match i { USED_STATIC => (), USED_CONST => (), + CONST_USED_IN_RANGE_PATTERN..100 => {} _ => () } f::(); diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b9305..c4410114cea8 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -17,19 +17,19 @@ LL | const priv_const: isize = 0; | ^^^^^^^^^^ error: struct `PrivStruct` is never constructed - --> $DIR/lint-dead-code-1.rs:35:8 + --> $DIR/lint-dead-code-1.rs:36:8 | LL | struct PrivStruct; | ^^^^^^^^^^ error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:65:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:68:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +38,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:90:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:95:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:100:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:104:4 | LL | fn baz() -> impl Copy { | ^^^ diff --git a/tests/ui/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 6ed0476183ec..980d8d52f1d5 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,4 +1,4 @@ -//@ run-pass +#![deny(dead_code)] #[derive(PartialEq, Eq)] pub enum Foo { @@ -11,6 +11,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); + const A5: u32 = 1; //~ ERROR: dead_code } fn main() { @@ -35,4 +36,9 @@ fn main() { Foo::A4 => {}, _ => {}, } + + match 3 { + Foo::A5..5 => {} + _ => {} + } } diff --git a/tests/ui/pattern/issue-110508.stderr b/tests/ui/pattern/issue-110508.stderr new file mode 100644 index 000000000000..aaec91167692 --- /dev/null +++ b/tests/ui/pattern/issue-110508.stderr @@ -0,0 +1,17 @@ +error: associated constant `A5` is never used + --> $DIR/issue-110508.rs:14:11 + | +LL | impl Foo { + | -------- associated constant in this implementation +... +LL | const A5: u32 = 1; + | ^^ + | +note: the lint level is defined here + --> $DIR/issue-110508.rs:1:9 + | +LL | #![deny(dead_code)] + | ^^^^^^^^^ + +error: aborting due to 1 previous error + From 559648a0a4c942993c321d999f708e64f169b245 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 29 Jan 2025 08:52:43 +0000 Subject: [PATCH 76/77] Handle all `PatExpr`s in dead code analysis --- compiler/rustc_passes/src/dead.rs | 17 ++++++++++++----- tests/ui/pattern/issue-110508.rs | 4 +++- tests/ui/pattern/issue-110508.stderr | 17 ----------------- 3 files changed, 15 insertions(+), 23 deletions(-) delete mode 100644 tests/ui/pattern/issue-110508.stderr diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bc2bebdfef68..95f18eaa7ef1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -13,7 +13,7 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatExpr, PatExprKind, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -636,10 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, .. }) => { - let res = self.typeck_results().qpath_res(qpath, *hir_id); - self.handle_res(res); - } PatKind::TupleStruct(ref qpath, fields, dotdot) => { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_tuple_field_pattern_match(pat, res, fields, dotdot); @@ -651,6 +647,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = false; } + fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { + match &expr.kind { + rustc_hir::PatExprKind::Path(qpath) => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + self.handle_res(res); + } + _ => {} + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { self.handle_res(path.res); intravisit::walk_path(self, path); diff --git a/tests/ui/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 980d8d52f1d5..74a8d673e837 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,3 +1,5 @@ +//@ run-pass + #![deny(dead_code)] #[derive(PartialEq, Eq)] @@ -11,7 +13,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); - const A5: u32 = 1; //~ ERROR: dead_code + const A5: u32 = 1; } fn main() { diff --git a/tests/ui/pattern/issue-110508.stderr b/tests/ui/pattern/issue-110508.stderr deleted file mode 100644 index aaec91167692..000000000000 --- a/tests/ui/pattern/issue-110508.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: associated constant `A5` is never used - --> $DIR/issue-110508.rs:14:11 - | -LL | impl Foo { - | -------- associated constant in this implementation -... -LL | const A5: u32 = 1; - | ^^ - | -note: the lint level is defined here - --> $DIR/issue-110508.rs:1:9 - | -LL | #![deny(dead_code)] - | ^^^^^^^^^ - -error: aborting due to 1 previous error - From d78193381249740d769970d4ec453c5bc265f082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Tue, 28 Jan 2025 08:53:19 +0000 Subject: [PATCH 77/77] add constraint graph to polonius MIR dump --- compiler/rustc_borrowck/src/polonius/dump.rs | 110 +++++++++++++++++-- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index f71e6f3e6f3a..6d32ee17f4c2 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,17 +1,19 @@ use std::io; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; -use rustc_middle::mir::{Body, ClosureRegionRequirements}; +use rustc_middle::mir::{Body, ClosureRegionRequirements, Location}; use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::region_infer::values::LivenessValues; use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; @@ -80,14 +82,27 @@ fn emit_polonius_dump<'tcx>( body, regioncx, borrow_set, - localized_outlives_constraints, + &localized_outlives_constraints, closure_region_requirements, out, )?; writeln!(out, "")?; writeln!(out, "")?; - // Section 2: mermaid visualization of the CFG. + // Section 2: mermaid visualization of the polonius constraint graph. + writeln!(out, "
")?; + writeln!(out, "Polonius constraint graph")?; + writeln!(out, "
")?;
+    let edge_count = emit_mermaid_constraint_graph(
+        borrow_set,
+        regioncx.liveness_constraints(),
+        &localized_outlives_constraints,
+        out,
+    )?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; writeln!(out, "
")?;
@@ -95,7 +110,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "
")?; writeln!(out, "
")?; - // Section 3: mermaid visualization of the NLL region graph. + // Section 4: mermaid visualization of the NLL region graph. writeln!(out, "
")?; writeln!(out, "NLL regions")?; writeln!(out, "
")?;
@@ -103,7 +118,7 @@ fn emit_polonius_dump<'tcx>(
     writeln!(out, "
")?; writeln!(out, "
")?; - // Section 4: mermaid visualization of the NLL SCC graph. + // Section 5: mermaid visualization of the NLL SCC graph. writeln!(out, "
")?; writeln!(out, "NLL SCCs")?; writeln!(out, "
")?;
@@ -117,7 +132,11 @@ fn emit_polonius_dump<'tcx>(
         ""
     )?;
     writeln!(out, "")?;
     writeln!(out, "")?;
@@ -132,7 +151,7 @@ fn emit_html_mir<'tcx>(
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
-    localized_outlives_constraints: LocalizedOutlivesConstraintSet,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
     closure_region_requirements: &Option>,
     out: &mut dyn io::Write,
 ) -> io::Result<()> {
@@ -160,7 +179,7 @@ fn emit_html_mir<'tcx>(
                 regioncx,
                 closure_region_requirements,
                 borrow_set,
-                &localized_outlives_constraints,
+                localized_outlives_constraints,
                 pass_where,
                 out,
             )
@@ -392,3 +411,76 @@ fn emit_mermaid_nll_sccs<'tcx>(
 
     Ok(())
 }
+
+/// Emits a mermaid flowchart of the polonius localized outlives constraints, with subgraphs per
+/// region, and loan introductions.
+fn emit_mermaid_constraint_graph<'tcx>(
+    borrow_set: &BorrowSet<'tcx>,
+    liveness: &LivenessValues,
+    localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
+    out: &mut dyn io::Write,
+) -> io::Result {
+    let location_name = |location: Location| {
+        // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id,
+        // transform it into `BB5_2`.
+        format!("BB{}_{}", location.block.index(), location.statement_index)
+    };
+    let region_name = |region: RegionVid| format!("'{}", region.index());
+    let node_name = |region: RegionVid, point: PointIndex| {
+        let location = liveness.location_from_point(point);
+        format!("{}_{}", region_name(region), location_name(location))
+    };
+
+    // The mermaid chart type: a top-down flowchart, which supports subgraphs.
+    writeln!(out, "flowchart TD")?;
+
+    // The loans subgraph: a node per loan.
+    writeln!(out, "    subgraph \"Loans\"")?;
+    for loan_idx in 0..borrow_set.len() {
+        writeln!(out, "        L{loan_idx}")?;
+    }
+    writeln!(out, "    end\n")?;
+
+    // And an edge from that loan node to where it enters the constraint graph.
+    for (loan_idx, loan) in borrow_set.iter_enumerated() {
+        writeln!(
+            out,
+            "    L{} --> {}_{}",
+            loan_idx.index(),
+            region_name(loan.region),
+            location_name(loan.reserve_location),
+        )?;
+    }
+    writeln!(out, "")?;
+
+    // The regions subgraphs containing the region/point nodes.
+    let mut points_per_region: FxIndexMap> =
+        FxIndexMap::default();
+    for constraint in &localized_outlives_constraints.outlives {
+        points_per_region.entry(constraint.source).or_default().insert(constraint.from);
+        points_per_region.entry(constraint.target).or_default().insert(constraint.to);
+    }
+    for (region, points) in points_per_region {
+        writeln!(out, "    subgraph \"{}\"", region_name(region))?;
+        for point in points {
+            writeln!(out, "        {}", node_name(region, point))?;
+        }
+        writeln!(out, "    end\n")?;
+    }
+
+    // The constraint graph edges.
+    for constraint in &localized_outlives_constraints.outlives {
+        // FIXME: add killed loans and constraint kind as edge labels.
+        writeln!(
+            out,
+            "    {} --> {}",
+            node_name(constraint.source, constraint.from),
+            node_name(constraint.target, constraint.to),
+        )?;
+    }
+
+    // Return the number of edges: this is the biggest graph in the dump and its edge count will be
+    // mermaid's max edge count to support.
+    let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len();
+    Ok(edge_count)
+}