diff --git a/.mailmap b/.mailmap index 6c168bbbcb554..c0638de4aeec5 100644 --- a/.mailmap +++ b/.mailmap @@ -9,7 +9,7 @@ Aaron Todd Abhishek Chanda Abhishek Chanda Abhijeet Bhagat Abroskin Alexander -Adolfo Ochagavía +Adolfo Ochagavía Adrian Heine né Lang Adrien Tétar Ahmed Charles @@ -36,6 +36,7 @@ Amanda Stjerna Amanda Stjerna Amanieu d'Antras Amos Onn +Amos Wenger Ana-Maria Mihalache Anatoly Ikorsky Andre Bogus @@ -276,7 +277,8 @@ Irina Popa Ivan Ivaschenko ivan tkachenko J. J. Weber -Jack Huey +Jack Huey +Jack Huey <31162821+jackh726@users.noreply.github.com> Jacob Jacob Hoffman-Andrews Jacob Greenfield @@ -292,6 +294,8 @@ Jakub Adam Wieczorek Jakub Adam Wieczorek Jakub Adam Wieczorek Jakub Adam Wieczorek +Jakub Adam Wieczorek +Jakub Adam Wieczorek Jakub Beránek James [Undefined] James Deng @@ -303,6 +307,7 @@ Jamie Hill-Daniel Jana Dönszelmann Jana Dönszelmann Jana Dönszelmann +Jane Lusby Jan-Erik Rediger Jaro Fietz Jason Fager @@ -313,6 +318,7 @@ Jason Toffaletti Jason Toffaletti Jauhien Piatlicki Jauhien Piatlicki Jay True Jeremy Letang +Jeremy Soller Jeremy Sorensen Jeremy Stucki Jeremy Stucki @@ -336,6 +342,7 @@ John Kåre Alsaker John Kåre Alsaker John Talling John Van Enk +Jon Gjengset Jonas Tepe Jonathan Bailey Jonathan Chan Kwan Yin @@ -424,7 +431,7 @@ Malo Jaffré Manish Goregaokar Mara Bos Marcell Pardavi -Marco Ieni <11428655+MarcoIeni@users.noreply.github.com> +Marco Ieni <11428655+MarcoIeni@users.noreply.github.com> Marcus Klaas de Vries Margaret Meyerhofer Marijn Schouten @@ -531,6 +538,7 @@ Oliver Scherer Oliver Scherer Oliver Scherer Oliver Scherer +Onur Özkan Onur Özkan Onur Özkan Ömer Sinan Ağacan @@ -591,6 +599,7 @@ Rusty Blitzerr RustyYato Ruud van Asseldonk Ruud van Asseldonk Ryan Leung +Ryan Levick Ryan Scheel Ryan Sullivant Ryan Wiedemann @@ -686,6 +695,8 @@ Weihang Lo Weihang Lo Wesley Wiser whitequark +Will Crichton +Will Crichton William Ting Wim Looman Wim Looman @@ -695,6 +706,8 @@ Xinye Tao Xuefeng Wu Xuefeng Wu Xuefeng Wu XuefengWu York Xiang +Yoshua Wuyts +Yoshua Wuyts <2467194+yoshuawuyts@users.noreply.github.com> Yotam Ofek Youngsoo Son Youngsuk Kim diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index d441cd119a747..f7f911b654761 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -16,7 +16,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature, TargetFeatureKind}; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, TyAndLayout, @@ -1417,14 +1417,18 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { // Attributes on the function definition being called let fn_defn_attrs = self.cx.tcx.codegen_fn_attrs(instance.def_id()); if let Some(fn_call_attrs) = fn_call_attrs - && !fn_call_attrs.target_features.is_empty() // If there is an inline attribute and a target feature that matches // we will add the attribute to the callsite otherwise we'll omit // this and not add the attribute to prevent soundness issues. && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, instance) && self.cx.tcx.is_target_feature_call_safe( - &fn_call_attrs.target_features, &fn_defn_attrs.target_features, + &fn_call_attrs.target_features.iter().cloned().chain( + self.cx.tcx.sess.target_features.iter().map(|feat| TargetFeature { + name: *feat, + kind: TargetFeatureKind::Implied, + }) + ).collect::>(), ) { attributes::apply_to_callsite( diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs index 751674f3f068d..bae423840ee1b 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics/simd.rs @@ -3,7 +3,7 @@ use rustc_abi::{BackendRepr, Endian}; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_apfloat::{Float, Round}; use rustc_middle::mir::interpret::{InterpErrorKind, Pointer, UndefinedBehaviorInfo}; -use rustc_middle::ty::{FloatTy, SimdAlign}; +use rustc_middle::ty::{FloatTy, ScalarInt, SimdAlign}; use rustc_middle::{bug, err_ub_format, mir, span_bug, throw_unsup_format, ty}; use rustc_span::{Symbol, sym}; use tracing::trace; @@ -744,6 +744,58 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_scalar(val, &dest)?; } } + sym::simd_funnel_shl | sym::simd_funnel_shr => { + let (left, _) = self.project_to_simd(&args[0])?; + let (right, _) = self.project_to_simd(&args[1])?; + let (shift, _) = self.project_to_simd(&args[2])?; + let (dest, _) = self.project_to_simd(&dest)?; + + let (len, elem_ty) = args[0].layout.ty.simd_size_and_type(*self.tcx); + let (elem_size, _signed) = elem_ty.int_size_and_signed(*self.tcx); + let elem_size_bits = u128::from(elem_size.bits()); + + let is_left = intrinsic_name == sym::simd_funnel_shl; + + for i in 0..len { + let left = + self.read_scalar(&self.project_index(&left, i)?)?.to_bits(elem_size)?; + let right = + self.read_scalar(&self.project_index(&right, i)?)?.to_bits(elem_size)?; + let shift_bits = + self.read_scalar(&self.project_index(&shift, i)?)?.to_bits(elem_size)?; + + if shift_bits >= elem_size_bits { + throw_ub_format!( + "overflowing shift by {shift_bits} in `{intrinsic_name}` in lane {i}" + ); + } + let inv_shift_bits = u32::try_from(elem_size_bits - shift_bits).unwrap(); + + // A funnel shift left by S can be implemented as `(x << S) | y.unbounded_shr(SIZE - S)`. + // The `unbounded_shr` is needed because otherwise if `S = 0`, it would be `x | y` + // when it should be `x`. + // + // This selects the least-significant `SIZE - S` bits of `x`, followed by the `S` most + // significant bits of `y`. As `left` and `right` both occupy the lower `SIZE` bits, + // we can treat the lower `SIZE` bits as an integer of the right width and use + // the same implementation, but on a zero-extended `x` and `y`. This works because + // `x << S` just pushes the `SIZE-S` MSBs out, and `y >> (SIZE - S)` shifts in + // zeros, as it is zero-extended. To the lower `SIZE` bits, this looks just like a + // funnel shift left. + // + // Note that the `unbounded_sh{l,r}`s are needed only in case we are using this on + // `u128xN` and `inv_shift_bits == 128`. + let result_bits = if is_left { + (left << shift_bits) | right.unbounded_shr(inv_shift_bits) + } else { + left.unbounded_shl(inv_shift_bits) | (right >> shift_bits) + }; + let (result, _overflow) = ScalarInt::truncate_from_uint(result_bits, elem_size); + + let dest = self.project_index(&dest, i)?; + self.write_scalar(result, &dest)?; + } + } // Unsupported intrinsic: skip the return_to_block below. _ => return interp_ok(false), diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 6e62e50d3af82..190b57ecea4d0 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1263,6 +1263,21 @@ pub fn suggest_impl_trait<'tcx>( infcx.tcx.lang_items().future_output(), format_as_assoc, ), + ( + infcx.tcx.lang_items().async_fn_trait(), + infcx.tcx.lang_items().async_fn_once_output(), + format_as_parenthesized, + ), + ( + infcx.tcx.lang_items().async_fn_mut_trait(), + infcx.tcx.lang_items().async_fn_once_output(), + format_as_parenthesized, + ), + ( + infcx.tcx.lang_items().async_fn_once_trait(), + infcx.tcx.lang_items().async_fn_once_output(), + format_as_parenthesized, + ), ( infcx.tcx.lang_items().fn_trait(), infcx.tcx.lang_items().fn_once_output(), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a00eaef20c083..7182747386933 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2088,6 +2088,8 @@ impl<'tcx> TyCtxt<'tcx> { self.sess.dcx() } + /// Checks to see if the caller (`body_features`) has all the features required by the callee + /// (`callee_features`). pub fn is_target_feature_call_safe( self, callee_features: &[TargetFeature], diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d253deb2fe8fd..faa8420c20d0e 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1512,9 +1512,8 @@ impl<'tcx> TyCtxt<'tcx> { field_shuffle_seed ^= user_seed; } - if let Some(reprs) = - find_attr!(self.get_all_attrs(did), AttributeKind::Repr { reprs, .. } => reprs) - { + let attributes = self.get_all_attrs(did); + if let Some(reprs) = find_attr!(attributes, AttributeKind::Repr { reprs, .. } => reprs) { for (r, _) in reprs { flags.insert(match *r { attr::ReprRust => ReprFlags::empty(), @@ -1574,10 +1573,7 @@ impl<'tcx> TyCtxt<'tcx> { } // See `TyAndLayout::pass_indirectly_in_non_rustic_abis` for details. - if find_attr!( - self.get_all_attrs(did), - AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) - ) { + if find_attr!(attributes, AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)) { flags.insert(ReprFlags::PASS_INDIRECTLY_IN_NON_RUSTIC_ABIS); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 1d00a6b81fabc..ad3493d93e80a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1125,6 +1125,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } } + + self.suggest_ident_hidden_by_hygiene(err, path, span); } else if err_code == E0412 { if let Some(correct) = Self::likely_rust_type(path) { err.span_suggestion( @@ -1138,6 +1140,28 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } + fn suggest_ident_hidden_by_hygiene(&self, err: &mut Diag<'_>, path: &[Segment], span: Span) { + let [segment] = path else { return }; + + let ident = segment.ident; + let callsite_span = span.source_callsite(); + for rib in self.ribs[ValueNS].iter().rev() { + for (binding_ident, _) in &rib.bindings { + if binding_ident.name == ident.name + && !binding_ident.span.eq_ctxt(span) + && !binding_ident.span.from_expansion() + && binding_ident.span.lo() < callsite_span.lo() + { + err.span_help( + binding_ident.span, + "an identifier with the same name exists, but is not accessible due to macro hygiene", + ); + return; + } + } + } + } + /// Emit special messages for unresolved `Self` and `self`. fn suggest_self_ty( &self, diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs index 17b371f05e530..82811cda00ce9 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs @@ -8,6 +8,7 @@ pub(crate) fn target() -> Target { base.features = "-small-data,+hvx-length128b".into(); base.has_rpath = true; + base.linker = Some("rust-lld".into()); base.linker_flavor = LinkerFlavor::Unix(Cc::Yes); base.c_enum_min_bits = Some(8); diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs index 6379cd30c3559..55ec3697a15e9 100644 --- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs @@ -27,6 +27,7 @@ pub(crate) fn target() -> Target { max_atomic_width: Some(32), emit_debug_gdb_scripts: false, c_enum_min_bits: Some(8), + linker: Some("rust-lld".into()), ..Default::default() }, } diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs index 4f3f140af67d1..8e6376b22ce61 100644 --- a/compiler/rustc_type_ir/src/search_graph/mod.rs +++ b/compiler/rustc_type_ir/src/search_graph/mod.rs @@ -23,7 +23,7 @@ use derive_where::derive_where; #[cfg(feature = "nightly")] use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext}; use rustc_type_ir::data_structures::HashMap; -use tracing::{debug, instrument}; +use tracing::{debug, instrument, trace}; mod stack; use stack::{Stack, StackDepth, StackEntry}; @@ -916,6 +916,7 @@ impl, X: Cx> SearchGraph { /// heads from the stack. This may not necessarily mean that we've actually /// reached a fixpoint for that cycle head, which impacts the way we rebase /// provisional cache entries. +#[derive_where(Debug; X: Cx)] enum RebaseReason { NoCycleUsages, Ambiguity(X::AmbiguityInfo), @@ -950,6 +951,7 @@ impl, X: Cx> SearchGraph { /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested /// goals whose result doesn't actually depend on this cycle head, but that's acceptable /// to me. + #[instrument(level = "trace", skip(self, cx))] fn rebase_provisional_cache_entries( &mut self, cx: X, @@ -969,6 +971,7 @@ impl, X: Cx> SearchGraph { let popped_head = if heads.highest_cycle_head_index() == popped_head_index { heads.remove_highest_cycle_head() } else { + debug_assert!(heads.highest_cycle_head_index() < popped_head_index); return true; }; @@ -1057,6 +1060,8 @@ impl, X: Cx> SearchGraph { new_highest_head_index, )); + trace!(?input, ?entry, "rebased provisional cache entry"); + true }); !entries.is_empty() @@ -1379,7 +1384,8 @@ impl, X: Cx> SearchGraph { } // Clear all provisional cache entries which depend on a previous provisional - // result of this goal and rerun. + // result of this goal and rerun. This does not remove goals which accessed this + // goal without depending on its result. self.clear_dependent_provisional_results_for_rerun(); debug!(?result, "fixpoint changed provisional results"); @@ -1399,7 +1405,12 @@ impl, X: Cx> SearchGraph { // similar to the previous iterations when reevaluating, it's better // for caching if the reevaluation also starts out with `false`. encountered_overflow: false, - usages: None, + // We keep provisional cache entries around if they used this goal + // without depending on its result. + // + // We still need to drop or rebase these cache entries once we've + // finished evaluating this goal. + usages: Some(HeadUsages::default()), candidate_usages: None, }); } diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index 92bca0eebfd93..8cd8b0850e947 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -660,12 +660,15 @@ macro_rules! nonzero_integer { without modifying the original"] #[inline(always)] pub const fn isolate_highest_one(self) -> Self { - let n = self.get() & (((1 as $Int) << (<$Int>::BITS - 1)).wrapping_shr(self.leading_zeros())); - // SAFETY: // `self` is non-zero, so masking to preserve only the most // significant set bit will result in a non-zero `n`. - unsafe { NonZero::new_unchecked(n) } + // and self.leading_zeros() is always < $INT::BITS since + // at least one of the bits in the number is not zero + unsafe { + let bit = (((1 as $Uint) << (<$Uint>::BITS - 1)).unchecked_shr(self.leading_zeros())); + NonZero::new_unchecked(bit as $Int) + } } /// Returns `self` with only the least significant bit set. diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index daa25c5a50dd6..701b3be6894da 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -57,7 +57,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] use core::ptr::NonNull; -use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] @@ -287,7 +287,7 @@ unsafe impl Allocator for System { } } -static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. /// @@ -344,7 +344,12 @@ pub fn take_alloc_error_hook() -> fn(Layout) { if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } +#[optimize(size)] fn default_alloc_error_hook(layout: Layout) { + if cfg!(panic = "immediate-abort") { + return; + } + unsafe extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. // Its value depends on the -Zoom={panic,abort} compiler option. @@ -354,16 +359,65 @@ fn default_alloc_error_hook(layout: Layout) { if unsafe { __rust_alloc_error_handler_should_panic_v2() != 0 } { panic!("memory allocation of {} bytes failed", layout.size()); + } + + // This is the default path taken on OOM, and the only path taken on stable with std. + // Crucially, it does *not* call any user-defined code, and therefore users do not have to + // worry about allocation failure causing reentrancy issues. That makes it different from + // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error + // handler that is called when there is no `#[alloc_error_handler]`), which triggers a + // regular panic and thus can invoke a user-defined panic hook, executing arbitrary + // user-defined code. + + static PREV_ALLOC_FAILURE: AtomicBool = AtomicBool::new(false); + if PREV_ALLOC_FAILURE.swap(true, Ordering::Relaxed) { + // Don't try to print a backtrace if a previous alloc error happened. This likely means + // there is not enough memory to print a backtrace, although it could also mean that two + // threads concurrently run out of memory. + rtprintpanic!( + "memory allocation of {} bytes failed\nskipping backtrace printing to avoid potential recursion\n", + layout.size() + ); + return; } else { - // This is the default path taken on OOM, and the only path taken on stable with std. - // Crucially, it does *not* call any user-defined code, and therefore users do not have to - // worry about allocation failure causing reentrancy issues. That makes it different from - // the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error - // handler that is called when there is no `#[alloc_error_handler]`), which triggers a - // regular panic and thus can invoke a user-defined panic hook, executing arbitrary - // user-defined code. rtprintpanic!("memory allocation of {} bytes failed\n", layout.size()); } + + let Some(mut out) = crate::sys::stdio::panic_output() else { + return; + }; + + // Use a lock to prevent mixed output in multithreading context. + // Some platforms also require it when printing a backtrace, like `SymFromAddr` on Windows. + // Make sure to not take this lock until after checking PREV_ALLOC_FAILURE to avoid deadlocks + // when there is too little memory to print a backtrace. + let mut lock = crate::sys::backtrace::lock(); + + match crate::panic::get_backtrace_style() { + Some(crate::panic::BacktraceStyle::Short) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Short)) + } + Some(crate::panic::BacktraceStyle::Full) => { + drop(lock.print(&mut out, crate::backtrace_rs::PrintFmt::Full)) + } + Some(crate::panic::BacktraceStyle::Off) => { + use crate::io::Write; + let _ = writeln!( + out, + "note: run with `RUST_BACKTRACE=1` environment variable to display a \ + backtrace" + ); + if cfg!(miri) { + let _ = writeln!( + out, + "note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` \ + for the environment variable to have an effect" + ); + } + } + // If backtraces aren't supported or are forced-off, do nothing. + None => {} + } } #[cfg(not(test))] @@ -371,11 +425,13 @@ fn default_alloc_error_hook(layout: Layout) { #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] pub fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::Acquire); - let hook: fn(Layout) = - if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; - hook(layout); - crate::process::abort() + crate::sys::backtrace::__rust_end_short_backtrace(|| { + let hook = HOOK.load(Ordering::Acquire); + let hook: fn(Layout) = + if hook.is_null() { default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; + hook(layout); + crate::process::abort() + }) } #[cfg(not(test))] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 9af3e5f63ffb4..ac3f6891051ae 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -293,7 +293,6 @@ fn default_hook(info: &PanicHookInfo<'_>) { static FIRST_PANIC: Atomic = AtomicBool::new(true); match backtrace { - // SAFETY: we took out a lock just a second ago. Some(BacktraceStyle::Short) => { drop(lock.print(err, crate::backtrace_rs::PrintFmt::Short)) } diff --git a/library/std/src/sys/backtrace.rs b/library/std/src/sys/backtrace.rs index 57682207e078e..8e4e6aab0e49a 100644 --- a/library/std/src/sys/backtrace.rs +++ b/library/std/src/sys/backtrace.rs @@ -20,8 +20,6 @@ pub(crate) fn lock<'a>() -> BacktraceLock<'a> { impl BacktraceLock<'_> { /// Prints the current backtrace. - /// - /// NOTE: this function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. pub(crate) fn print(&mut self, w: &mut dyn Write, format: PrintFmt) -> io::Result<()> { // There are issues currently linking libbacktrace into tests, and in // general during std's own unit tests we're not testing this path. In @@ -36,6 +34,7 @@ impl BacktraceLock<'_> { } impl fmt::Display for DisplayBacktrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: the backtrace lock is held unsafe { _print_fmt(fmt, self.format) } } } @@ -43,6 +42,9 @@ impl BacktraceLock<'_> { } } +/// # Safety +/// +/// This function is not Sync. The caller must hold a mutex lock, or there must be only one thread in the program. unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::Result { // Always 'fail' to get the cwd when running under Miri - // this allows Miri to display backtraces in isolation mode diff --git a/package-lock.json b/package-lock.json index d0297bf70b63f..72129f115845e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,10 @@ "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", - "eslint-js": "github:eslint/js", "typescript": "^5.8.3" + }, + "devDependencies": { + "@types/node": "^24.10.0" } }, "node_modules/@babel/code-frame": { @@ -57,6 +59,12 @@ "node": ">= 10" } }, + "node_modules/@caporal/core/node_modules/@types/node": { + "version": "13.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz", + "integrity": "sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==", + "license": "MIT" + }, "node_modules/@colors/colors": { "version": "1.6.0", "license": "MIT", @@ -225,8 +233,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "13.9.3", - "license": "MIT" + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.0.tgz", + "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } }, "node_modules/@types/table": { "version": "5.0.0", @@ -944,13 +957,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-js": { - "version": "1.0.0", - "resolved": "git+ssh://git@github.com/eslint/js.git#9e5b4fabf073b915abc56d6c14cc24177036d43e", - "workspaces": [ - "packages/*" - ] - }, "node_modules/eslint-scope": { "version": "7.2.2", "license": "BSD-2-Clause", @@ -2575,6 +2581,12 @@ "through": "^2.3.8" } }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/untildify": { "version": "3.0.3", "license": "MIT", diff --git a/package.json b/package.json index 04e0f6af19a0c..7420c53fd8cca 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "browser-ui-test": "^0.22.2", "es-check": "^6.2.1", "eslint": "^8.57.1", - "eslint-js": "github:eslint/js", "typescript": "^5.8.3" + }, + "devDependencies": { + "@types/node": "^24.10.0" } } diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 6622aae069d5c..30e7fbb265ac4 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -73,7 +73,7 @@ book!( EditionGuide, "src/doc/edition-guide", "edition-guide", &[]; EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[]; Nomicon, "src/doc/nomicon", "nomicon", &[]; - RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja", "zh"]; + RustByExample, "src/doc/rust-by-example", "rust-by-example", &["es", "ja", "zh"]; RustdocBook, "src/doc/rustdoc", "rustdoc", &[]; StyleGuide, "src/doc/style-guide", "style-guide", &[]; ); @@ -1228,9 +1228,12 @@ impl Step for ErrorIndex { t!(fs::create_dir_all(&out)); tool::ErrorIndex::command(builder, self.compilers) .arg("html") - .arg(out) + .arg(&out) .arg(&builder.version) .run(builder); + + let index = out.join("error-index.html"); + builder.maybe_open_in_browser::(index); } fn metadata(&self) -> Option { diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md index be6e17883f4eb..d74dd843eb259 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md @@ -39,6 +39,12 @@ dynamically linked executables. # /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello ``` +## Linking + +This target selects `rust-lld` by default. Another option to use is +[eld](https://github.com/qualcomm/eld), which is also provided with +the opensource hexagon toolchain and the Hexagon SDK. + ## Building the target Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this target. diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md index b07b0bb08d60a..a906e895b7743 100644 --- a/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md +++ b/src/doc/rustc/src/platform-support/hexagon-unknown-none-elf.md @@ -25,6 +25,13 @@ Functions marked `extern "C"` use the [Hexagon architecture calling convention]( This target generates PIC ELF binaries. +## Linking + +This target selects `rust-lld` by default. Another option to use is +[eld](https://github.com/qualcomm/eld), which is also provided with +[the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon) +and the Hexagon SDK. + ## Building the target You can build Rust with support for the target by adding it to the `target` diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 856e637a4587b..337429a6248d9 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -47,71 +47,69 @@ pub(crate) fn print_generic_bounds( bounds .iter() .filter(move |b| bounds_dup.insert(*b)) - .map(|bound| bound.print(cx)) + .map(|bound| print_generic_bound(bound, cx)) .joined(" + ", f) }) } -impl clean::GenericParamDef { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| match &self.kind { - clean::GenericParamDefKind::Lifetime { outlives } => { - write!(f, "{}", self.name)?; - - if !outlives.is_empty() { - f.write_str(": ")?; - outlives.iter().map(|lt| lt.print()).joined(" + ", f)?; - } +pub(crate) fn print_generic_param_def( + generic_param: &clean::GenericParamDef, + cx: &Context<'_>, +) -> impl Display { + fmt::from_fn(move |f| match &generic_param.kind { + clean::GenericParamDefKind::Lifetime { outlives } => { + write!(f, "{}", generic_param.name)?; - Ok(()) + if !outlives.is_empty() { + f.write_str(": ")?; + outlives.iter().map(|lt| print_lifetime(lt)).joined(" + ", f)?; } - clean::GenericParamDefKind::Type { bounds, default, .. } => { - f.write_str(self.name.as_str())?; - if !bounds.is_empty() { - f.write_str(": ")?; - print_generic_bounds(bounds, cx).fmt(f)?; - } + Ok(()) + } + clean::GenericParamDefKind::Type { bounds, default, .. } => { + f.write_str(generic_param.name.as_str())?; - if let Some(ty) = default { - f.write_str(" = ")?; - ty.print(cx).fmt(f)?; - } + if !bounds.is_empty() { + f.write_str(": ")?; + print_generic_bounds(bounds, cx).fmt(f)?; + } - Ok(()) + if let Some(ty) = default { + f.write_str(" = ")?; + print_type(ty, cx).fmt(f)?; } - clean::GenericParamDefKind::Const { ty, default, .. } => { - write!(f, "const {}: ", self.name)?; - ty.print(cx).fmt(f)?; - - if let Some(default) = default { - f.write_str(" = ")?; - if f.alternate() { - write!(f, "{default}")?; - } else { - write!(f, "{}", Escape(default))?; - } - } - Ok(()) + Ok(()) + } + clean::GenericParamDefKind::Const { ty, default, .. } => { + write!(f, "const {}: ", generic_param.name)?; + print_type(ty, cx).fmt(f)?; + + if let Some(default) = default { + f.write_str(" = ")?; + if f.alternate() { + write!(f, "{default}")?; + } else { + write!(f, "{}", Escape(default))?; + } } - }) - } -} -impl clean::Generics { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - let mut real_params = self.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); - if real_params.peek().is_none() { - None - } else { - Some( - Wrapped::with_angle_brackets() - .wrap_fn(move |f| real_params.clone().map(|g| g.print(cx)).joined(", ", f)), - ) + Ok(()) } - .maybe_display() + }) +} + +pub(crate) fn print_generics(generics: &clean::Generics, cx: &Context<'_>) -> impl Display { + let mut real_params = generics.params.iter().filter(|p| !p.is_synthetic_param()).peekable(); + if real_params.peek().is_none() { + None + } else { + Some(Wrapped::with_angle_brackets().wrap_fn(move |f| { + real_params.clone().map(|g| print_generic_param_def(g, cx)).joined(", ", f) + })) } + .maybe_display() } #[derive(Clone, Copy, PartialEq, Eq)] @@ -125,7 +123,7 @@ fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> match predicate { clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => { print_higher_ranked_params_with_space(bound_params, cx, "for").fmt(f)?; - ty.print(cx).fmt(f)?; + print_type(ty, cx).fmt(f)?; f.write_str(":")?; if !bounds.is_empty() { f.write_str(" ")?; @@ -136,7 +134,7 @@ fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> clean::WherePredicate::RegionPredicate { lifetime, bounds } => { // We don't need to check `alternate` since we can be certain that neither // the lifetime nor the bounds contain any characters which need escaping. - write!(f, "{}:", lifetime.print())?; + write!(f, "{}:", print_lifetime(lifetime))?; if !bounds.is_empty() { write!(f, " {}", print_generic_bounds(bounds, cx))?; } @@ -144,7 +142,12 @@ fn print_where_predicate(predicate: &clean::WherePredicate, cx: &Context<'_>) -> } clean::WherePredicate::EqPredicate { lhs, rhs } => { let opts = WithOpts::from(f); - write!(f, "{} == {}", opts.display(lhs.print(cx)), opts.display(rhs.print(cx))) + write!( + f, + "{} == {}", + opts.display(print_qpath_data(lhs, cx)), + opts.display(print_term(rhs, cx)), + ) } } }) @@ -229,92 +232,91 @@ pub(crate) fn print_where_clause( })) } -impl clean::Lifetime { - pub(crate) fn print(&self) -> impl Display { - self.0.as_str() - } +#[inline] +pub(crate) fn print_lifetime(lt: &clean::Lifetime) -> &str { + lt.0.as_str() } -impl clean::ConstantKind { - pub(crate) fn print(&self, tcx: TyCtxt<'_>) -> impl Display { - let expr = self.expr(tcx); - fmt::from_fn(move |f| { +pub(crate) fn print_constant_kind( + constant_kind: &clean::ConstantKind, + tcx: TyCtxt<'_>, +) -> impl Display { + let expr = constant_kind.expr(tcx); + fmt::from_fn( + move |f| { if f.alternate() { f.write_str(&expr) } else { write!(f, "{}", Escape(&expr)) } - }) - } + }, + ) } -impl clean::PolyTrait { - fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - print_higher_ranked_params_with_space(&self.generic_params, cx, "for").fmt(f)?; - self.trait_.print(cx).fmt(f) - }) - } +fn print_poly_trait(poly_trait: &clean::PolyTrait, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| { + print_higher_ranked_params_with_space(&poly_trait.generic_params, cx, "for").fmt(f)?; + print_path(&poly_trait.trait_, cx).fmt(f) + }) } -impl clean::GenericBound { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| match self { - clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()), - clean::GenericBound::TraitBound(ty, modifiers) => { - // `const` and `[const]` trait bounds are experimental; don't render them. - let hir::TraitBoundModifiers { polarity, constness: _ } = modifiers; - f.write_str(match polarity { - hir::BoundPolarity::Positive => "", - hir::BoundPolarity::Maybe(_) => "?", - hir::BoundPolarity::Negative(_) => "!", - })?; - ty.print(cx).fmt(f) - } - clean::GenericBound::Use(args) => { - f.write_str("use")?; - Wrapped::with_angle_brackets() - .wrap_fn(|f| args.iter().map(|arg| arg.name()).joined(", ", f)) - .fmt(f) - } - }) - } +pub(crate) fn print_generic_bound( + generic_bound: &clean::GenericBound, + cx: &Context<'_>, +) -> impl Display { + fmt::from_fn(move |f| match generic_bound { + clean::GenericBound::Outlives(lt) => f.write_str(print_lifetime(lt)), + clean::GenericBound::TraitBound(ty, modifiers) => { + // `const` and `[const]` trait bounds are experimental; don't render them. + let hir::TraitBoundModifiers { polarity, constness: _ } = modifiers; + f.write_str(match polarity { + hir::BoundPolarity::Positive => "", + hir::BoundPolarity::Maybe(_) => "?", + hir::BoundPolarity::Negative(_) => "!", + })?; + print_poly_trait(ty, cx).fmt(f) + } + clean::GenericBound::Use(args) => { + f.write_str("use")?; + Wrapped::with_angle_brackets() + .wrap_fn(|f| args.iter().map(|arg| arg.name()).joined(", ", f)) + .fmt(f) + } + }) } -impl clean::GenericArgs { - fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - match self { - clean::GenericArgs::AngleBracketed { args, constraints } => { - if !args.is_empty() || !constraints.is_empty() { - Wrapped::with_angle_brackets() - .wrap_fn(|f| { - [Either::Left(args), Either::Right(constraints)] - .into_iter() - .flat_map(Either::factor_into_iter) - .map(|either| { - either.map_either( - |arg| arg.print(cx), - |constraint| constraint.print(cx), - ) - }) - .joined(", ", f) - }) - .fmt(f)?; - } - } - clean::GenericArgs::Parenthesized { inputs, output } => { - Wrapped::with_parens() - .wrap_fn(|f| inputs.iter().map(|ty| ty.print(cx)).joined(", ", f)) +fn print_generic_args(generic_args: &clean::GenericArgs, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| { + match generic_args { + clean::GenericArgs::AngleBracketed { args, constraints } => { + if !args.is_empty() || !constraints.is_empty() { + Wrapped::with_angle_brackets() + .wrap_fn(|f| { + [Either::Left(args), Either::Right(constraints)] + .into_iter() + .flat_map(Either::factor_into_iter) + .map(|either| { + either.map_either( + |arg| print_generic_arg(arg, cx), + |constraint| print_assoc_item_constraint(constraint, cx), + ) + }) + .joined(", ", f) + }) .fmt(f)?; - if let Some(ref ty) = *output { - f.write_str(if f.alternate() { " -> " } else { " -> " })?; - ty.print(cx).fmt(f)?; - } } - clean::GenericArgs::ReturnTypeNotation => { - f.write_str("(..)")?; + } + clean::GenericArgs::Parenthesized { inputs, output } => { + Wrapped::with_parens() + .wrap_fn(|f| inputs.iter().map(|ty| print_type(ty, cx)).joined(", ", f)) + .fmt(f)?; + if let Some(ref ty) = *output { + f.write_str(if f.alternate() { " -> " } else { " -> " })?; + print_type(ty, cx).fmt(f)?; } } - Ok(()) - }) - } + clean::GenericArgs::ReturnTypeNotation => { + f.write_str("(..)")?; + } + } + Ok(()) + }) } // Possible errors when computing href link source for a `DefId` @@ -684,7 +686,7 @@ fn resolved_path( } } if w.alternate() { - write!(w, "{}{:#}", last.name, last.args.print(cx))?; + write!(w, "{}{:#}", last.name, print_generic_args(&last.args, cx))?; } else { let path = fmt::from_fn(|f| { if use_absolute { @@ -702,7 +704,7 @@ fn resolved_path( write!(f, "{}", print_anchor(did, last.name, cx)) } }); - write!(w, "{path}{args}", args = last.args.print(cx))?; + write!(w, "{path}{args}", args = print_generic_args(&last.args, cx))?; } Ok(()) } @@ -791,11 +793,11 @@ fn print_tybounds( cx: &Context<'_>, ) -> impl Display { fmt::from_fn(move |f| { - bounds.iter().map(|bound| bound.print(cx)).joined(" + ", f)?; + bounds.iter().map(|bound| print_poly_trait(bound, cx)).joined(" + ", f)?; if let Some(lt) = lt { // We don't need to check `alternate` since we can be certain that // the lifetime doesn't contain any characters which need escaping. - write!(f, " + {}", lt.print())?; + write!(f, " + {}", print_lifetime(lt))?; } Ok(()) }) @@ -810,7 +812,9 @@ fn print_higher_ranked_params_with_space( if !params.is_empty() { f.write_str(keyword)?; Wrapped::with_angle_brackets() - .wrap_fn(|f| params.iter().map(|lt| lt.print(cx)).joined(", ", f)) + .wrap_fn(|f| { + params.iter().map(|lt| print_generic_param_def(lt, cx)).joined(", ", f) + }) .fmt(f)?; f.write_char(' ')?; } @@ -868,11 +872,11 @@ fn fmt_type( } else { primitive_link(f, PrimitiveType::Fn, format_args!("fn"), cx)?; } - decl.decl.print(cx).fmt(f) + print_fn_decl(&decl.decl, cx).fmt(f) } clean::UnsafeBinder(binder) => { print_higher_ranked_params_with_space(&binder.generic_params, cx, "unsafe").fmt(f)?; - binder.ty.print(cx).fmt(f) + print_type(&binder.ty, cx).fmt(f) } clean::Tuple(typs) => match &typs[..] { &[] => primitive_link(f, PrimitiveType::Unit, format_args!("()"), cx), @@ -881,7 +885,7 @@ fn fmt_type( primitive_link(f, PrimitiveType::Tuple, format_args!("({name},)"), cx) } else { write!(f, "(")?; - one.print(cx).fmt(f)?; + print_type(one, cx).fmt(f)?; write!(f, ",)") } } @@ -907,7 +911,7 @@ fn fmt_type( ) } else { Wrapped::with_parens() - .wrap_fn(|f| many.iter().map(|item| item.print(cx)).joined(", ", f)) + .wrap_fn(|f| many.iter().map(|item| print_type(item, cx)).joined(", ", f)) .fmt(f) } } @@ -915,9 +919,9 @@ fn fmt_type( clean::Slice(box clean::Generic(name)) => { primitive_link(f, PrimitiveType::Slice, format_args!("[{name}]"), cx) } - clean::Slice(t) => Wrapped::with_square_brackets().wrap(t.print(cx)).fmt(f), + clean::Slice(t) => Wrapped::with_square_brackets().wrap(print_type(t, cx)).fmt(f), clean::Type::Pat(t, pat) => { - fmt::Display::fmt(&t.print(cx), f)?; + fmt::Display::fmt(&print_type(t, cx), f)?; write!(f, " is {pat}") } clean::Array(box clean::Generic(name), n) if !f.alternate() => primitive_link( @@ -928,7 +932,7 @@ fn fmt_type( ), clean::Array(t, n) => Wrapped::with_square_brackets() .wrap(fmt::from_fn(|f| { - t.print(cx).fmt(f)?; + print_type(t, cx).fmt(f)?; f.write_str("; ")?; if f.alternate() { f.write_str(n) @@ -944,17 +948,17 @@ fn fmt_type( primitive_link( f, clean::PrimitiveType::RawPointer, - format_args!("*{m} {ty}", ty = WithOpts::from(f).display(t.print(cx))), + format_args!("*{m} {ty}", ty = WithOpts::from(f).display(print_type(t, cx))), cx, ) } else { primitive_link(f, clean::PrimitiveType::RawPointer, format_args!("*{m} "), cx)?; - t.print(cx).fmt(f) + print_type(t, cx).fmt(f) } } clean::BorrowedRef { lifetime: l, mutability, type_: ty } => { let lt = fmt::from_fn(|f| match l { - Some(l) => write!(f, "{} ", l.print()), + Some(l) => write!(f, "{} ", print_lifetime(l)), _ => Ok(()), }); let m = mutability.print_with_space(); @@ -989,133 +993,133 @@ fn fmt_type( f.write_str("impl ")?; print_generic_bounds(bounds, cx).fmt(f) } - clean::QPath(qpath) => qpath.print(cx).fmt(f), + clean::QPath(qpath) => print_qpath_data(qpath, cx).fmt(f), } } -impl clean::Type { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| fmt_type(self, f, false, cx)) - } +pub(crate) fn print_type(type_: &clean::Type, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| fmt_type(type_, f, false, cx)) } -impl clean::Path { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| resolved_path(f, self.def_id(), self, false, false, cx)) - } +pub(crate) fn print_path(path: &clean::Path, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| resolved_path(f, path.def_id(), path, false, false, cx)) } -impl clean::QPathData { - fn print(&self, cx: &Context<'_>) -> impl Display { - let Self { ref assoc, ref self_type, should_fully_qualify, ref trait_ } = *self; +fn print_qpath_data(qpath_data: &clean::QPathData, cx: &Context<'_>) -> impl Display { + let clean::QPathData { ref assoc, ref self_type, should_fully_qualify, ref trait_ } = + *qpath_data; - fmt::from_fn(move |f| { - // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), - // we need to surround them with angle brackets in some cases (e.g. `::P`). + fmt::from_fn(move |f| { + // FIXME(inherent_associated_types): Once we support non-ADT self-types (#106719), + // we need to surround them with angle brackets in some cases (e.g. `::P`). - if let Some(trait_) = trait_ - && should_fully_qualify - { - let opts = WithOpts::from(f); - Wrapped::with_angle_brackets() - .wrap(format_args!( - "{} as {}", - opts.display(self_type.print(cx)), - opts.display(trait_.print(cx)) - )) - .fmt(f)? - } else { - self_type.print(cx).fmt(f)?; - } - f.write_str("::")?; - // It's pretty unsightly to look at `::C` in output, and - // we've got hyperlinking on our side, so try to avoid longer - // notation as much as possible by making `C` a hyperlink to trait - // `B` to disambiguate. - // - // FIXME: this is still a lossy conversion and there should probably - // be a better way of representing this in general? Most of - // the ugliness comes from inlining across crates where - // everything comes in as a fully resolved QPath (hard to - // look at). - if !f.alternate() { - // FIXME(inherent_associated_types): We always link to the very first associated - // type (in respect to source order) that bears the given name (`assoc.name`) and that is - // affiliated with the computed `DefId`. This is obviously incorrect when we have - // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself - // through here and map it to the corresponding HTML ID that was generated by - // `render::Context::derive_id` when the impl blocks were rendered. - // There is no such mapping unfortunately. - // As a hack, we could badly imitate `derive_id` here by keeping *count* when looking - // for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()` - // considering privacy, `doc(hidden)`, etc. - // I don't feel like that right now :cold_sweat:. - - let parent_href = match trait_ { - Some(trait_) => href(trait_.def_id(), cx).ok(), - None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()), - }; + if let Some(trait_) = trait_ + && should_fully_qualify + { + let opts = WithOpts::from(f); + Wrapped::with_angle_brackets() + .wrap(format_args!( + "{} as {}", + opts.display(print_type(self_type, cx)), + opts.display(print_path(trait_, cx)) + )) + .fmt(f)? + } else { + print_type(self_type, cx).fmt(f)?; + } + f.write_str("::")?; + // It's pretty unsightly to look at `::C` in output, and + // we've got hyperlinking on our side, so try to avoid longer + // notation as much as possible by making `C` a hyperlink to trait + // `B` to disambiguate. + // + // FIXME: this is still a lossy conversion and there should probably + // be a better way of representing this in general? Most of + // the ugliness comes from inlining across crates where + // everything comes in as a fully resolved QPath (hard to + // look at). + if !f.alternate() { + // FIXME(inherent_associated_types): We always link to the very first associated + // type (in respect to source order) that bears the given name (`assoc.name`) and that is + // affiliated with the computed `DefId`. This is obviously incorrect when we have + // multiple impl blocks. Ideally, we would thread the `DefId` of the assoc ty itself + // through here and map it to the corresponding HTML ID that was generated by + // `render::Context::derive_id` when the impl blocks were rendered. + // There is no such mapping unfortunately. + // As a hack, we could badly imitate `derive_id` here by keeping *count* when looking + // for the assoc ty `DefId` in `tcx.associated_items(self_ty_did).in_definition_order()` + // considering privacy, `doc(hidden)`, etc. + // I don't feel like that right now :cold_sweat:. + + let parent_href = match trait_ { + Some(trait_) => href(trait_.def_id(), cx).ok(), + None => self_type.def_id(cx.cache()).and_then(|did| href(did, cx).ok()), + }; - if let Some((url, _, path)) = parent_href { - write!( - f, - "{name}", - shortty = ItemType::AssocType, - name = assoc.name, - path = join_path_syms(path), - ) - } else { - write!(f, "{}", assoc.name) - } + if let Some((url, _, path)) = parent_href { + write!( + f, + "{name}", + shortty = ItemType::AssocType, + name = assoc.name, + path = join_path_syms(path), + ) } else { write!(f, "{}", assoc.name) - }?; + } + } else { + write!(f, "{}", assoc.name) + }?; - assoc.args.print(cx).fmt(f) - }) - } + print_generic_args(&assoc.args, cx).fmt(f) + }) } -impl clean::Impl { - pub(crate) fn print(&self, use_absolute: bool, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - f.write_str("impl")?; - self.generics.print(cx).fmt(f)?; - f.write_str(" ")?; +pub(crate) fn print_impl( + impl_: &clean::Impl, + use_absolute: bool, + cx: &Context<'_>, +) -> impl Display { + fmt::from_fn(move |f| { + f.write_str("impl")?; + print_generics(&impl_.generics, cx).fmt(f)?; + f.write_str(" ")?; - if let Some(ref ty) = self.trait_ { - if self.is_negative_trait_impl() { - f.write_char('!')?; - } - if self.kind.is_fake_variadic() - && let Some(generics) = ty.generics() - && let Ok(inner_type) = generics.exactly_one() - { - let last = ty.last(); - if f.alternate() { - write!(f, "{last}")?; - } else { - write!(f, "{}", print_anchor(ty.def_id(), last, cx))?; - }; - Wrapped::with_angle_brackets() - .wrap_fn(|f| self.print_type(inner_type, f, use_absolute, cx)) - .fmt(f)?; - } else { - ty.print(cx).fmt(f)?; - } - f.write_str(" for ")?; + if let Some(ref ty) = impl_.trait_ { + if impl_.is_negative_trait_impl() { + f.write_char('!')?; } - - if let Some(ty) = self.kind.as_blanket_ty() { - fmt_type(ty, f, use_absolute, cx)?; + if impl_.kind.is_fake_variadic() + && let Some(generics) = ty.generics() + && let Ok(inner_type) = generics.exactly_one() + { + let last = ty.last(); + if f.alternate() { + write!(f, "{last}")?; + } else { + write!(f, "{}", print_anchor(ty.def_id(), last, cx))?; + }; + Wrapped::with_angle_brackets() + .wrap_fn(|f| impl_.print_type(inner_type, f, use_absolute, cx)) + .fmt(f)?; } else { - self.print_type(&self.for_, f, use_absolute, cx)?; + print_path(ty, cx).fmt(f)?; } + f.write_str(" for ")?; + } - print_where_clause(&self.generics, cx, 0, Ending::Newline).maybe_display().fmt(f) - }) - } + if let Some(ty) = impl_.kind.as_blanket_ty() { + fmt_type(ty, f, use_absolute, cx)?; + } else { + impl_.print_type(&impl_.for_, f, use_absolute, cx)?; + } + + print_where_clause(&impl_.generics, cx, 0, Ending::Newline).maybe_display().fmt(f) + }) +} + +impl clean::Impl { fn print_type( &self, type_: &clean::Type, @@ -1191,7 +1195,7 @@ pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> imp if let Some(name) = param.name { write!(f, "{name}: ")?; } - param.type_.print(cx).fmt(f) + print_type(¶m.type_, cx).fmt(f) }) }) .joined(", ", f) @@ -1221,76 +1225,73 @@ impl Display for Indent { } } -impl clean::Parameter { - fn print(&self, cx: &Context<'_>) -> impl fmt::Display { - fmt::from_fn(move |f| { - if let Some(self_ty) = self.to_receiver() { - match self_ty { - clean::SelfTy => f.write_str("self"), - clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { - f.write_str(if f.alternate() { "&" } else { "&" })?; - if let Some(lt) = lifetime { - write!(f, "{lt} ", lt = lt.print())?; - } - write!(f, "{mutability}self", mutability = mutability.print_with_space()) - } - _ => { - f.write_str("self: ")?; - self_ty.print(cx).fmt(f) +fn print_parameter(parameter: &clean::Parameter, cx: &Context<'_>) -> impl fmt::Display { + fmt::from_fn(move |f| { + if let Some(self_ty) = parameter.to_receiver() { + match self_ty { + clean::SelfTy => f.write_str("self"), + clean::BorrowedRef { lifetime, mutability, type_: box clean::SelfTy } => { + f.write_str(if f.alternate() { "&" } else { "&" })?; + if let Some(lt) = lifetime { + write!(f, "{lt} ", lt = print_lifetime(lt))?; } + write!(f, "{mutability}self", mutability = mutability.print_with_space()) } - } else { - if self.is_const { - write!(f, "const ")?; - } - if let Some(name) = self.name { - write!(f, "{name}: ")?; + _ => { + f.write_str("self: ")?; + print_type(self_ty, cx).fmt(f) } - self.type_.print(cx).fmt(f) } - }) - } + } else { + if parameter.is_const { + write!(f, "const ")?; + } + if let Some(name) = parameter.name { + write!(f, "{name}: ")?; + } + print_type(¶meter.type_, cx).fmt(f) + } + }) } -impl clean::FnDecl { - pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display { - fmt::from_fn(move |f| { - let ellipsis = if self.c_variadic { ", ..." } else { "" }; - Wrapped::with_parens() - .wrap_fn(|f| { - print_params(&self.inputs, cx).fmt(f)?; - f.write_str(ellipsis) - }) - .fmt(f)?; - self.print_output(cx).fmt(f) - }) - } +fn print_fn_decl(fn_decl: &clean::FnDecl, cx: &Context<'_>) -> impl Display { + fmt::from_fn(move |f| { + let ellipsis = if fn_decl.c_variadic { ", ..." } else { "" }; + Wrapped::with_parens() + .wrap_fn(|f| { + print_params(&fn_decl.inputs, cx).fmt(f)?; + f.write_str(ellipsis) + }) + .fmt(f)?; + fn_decl.print_output(cx).fmt(f) + }) +} - /// * `header_len`: The length of the function header and name. In other words, the number of - /// characters in the function declaration up to but not including the parentheses. - /// This is expected to go into a `
`/`code-header` block, so indentation and newlines
-    ///   are preserved.
-    /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
-    ///   necessary.
-    pub(crate) fn full_print(
-        &self,
-        header_len: usize,
-        indent: usize,
-        cx: &Context<'_>,
-    ) -> impl Display {
-        fmt::from_fn(move |f| {
-            // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
-            let mut counter = WriteCounter(0);
-            write!(&mut counter, "{:#}", fmt::from_fn(|f| { self.inner_full_print(None, f, cx) }))?;
-            // If the text form was over 80 characters wide, we will line-wrap our output.
-            let line_wrapping_indent =
-                if header_len + counter.0 > 80 { Some(indent) } else { None };
-            // Generate the final output. This happens to accept `{:#}` formatting to get textual
-            // output but in practice it is only formatted with `{}` to get HTML output.
-            self.inner_full_print(line_wrapping_indent, f, cx)
-        })
-    }
+/// * `header_len`: The length of the function header and name. In other words, the number of
+///   characters in the function declaration up to but not including the parentheses.
+///   This is expected to go into a `
`/`code-header` block, so indentation and newlines
+///   are preserved.
+/// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
+///   necessary.
+pub(crate) fn full_print_fn_decl(
+    fn_decl: &clean::FnDecl,
+    header_len: usize,
+    indent: usize,
+    cx: &Context<'_>,
+) -> impl Display {
+    fmt::from_fn(move |f| {
+        // First, generate the text form of the declaration, with no line wrapping, and count the bytes.
+        let mut counter = WriteCounter(0);
+        write!(&mut counter, "{:#}", fmt::from_fn(|f| { fn_decl.inner_full_print(None, f, cx) }))?;
+        // If the text form was over 80 characters wide, we will line-wrap our output.
+        let line_wrapping_indent = if header_len + counter.0 > 80 { Some(indent) } else { None };
+        // Generate the final output. This happens to accept `{:#}` formatting to get textual
+        // output but in practice it is only formatted with `{}` to get HTML output.
+        fn_decl.inner_full_print(line_wrapping_indent, f, cx)
+    })
+}
 
+impl clean::FnDecl {
     fn inner_full_print(
         &self,
         // For None, the declaration will not be line-wrapped. For Some(n),
@@ -1316,7 +1317,7 @@ impl clean::FnDecl {
                         }
                     });
 
-                    self.inputs.iter().map(|param| param.print(cx)).joined(sep, f)?;
+                    self.inputs.iter().map(|param| print_parameter(param, cx)).joined(sep, f)?;
 
                     if line_wrapping_indent.is_some() {
                         writeln!(f, ",")?
@@ -1348,7 +1349,7 @@ impl clean::FnDecl {
             }
 
             f.write_str(if f.alternate() { " -> " } else { " -> " })?;
-            self.output.print(cx).fmt(f)
+            print_type(&self.output, cx).fmt(f)
         })
     }
 }
@@ -1461,67 +1462,68 @@ pub(crate) fn print_constness_with_space(
     }
 }
 
-impl clean::Import {
-    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
-        fmt::from_fn(move |f| match self.kind {
-            clean::ImportKind::Simple(name) => {
-                if name == self.source.path.last() {
-                    write!(f, "use {};", self.source.print(cx))
-                } else {
-                    write!(f, "use {source} as {name};", source = self.source.print(cx))
-                }
+pub(crate) fn print_import(import: &clean::Import, cx: &Context<'_>) -> impl Display {
+    fmt::from_fn(move |f| match import.kind {
+        clean::ImportKind::Simple(name) => {
+            if name == import.source.path.last() {
+                write!(f, "use {};", print_import_source(&import.source, cx))
+            } else {
+                write!(
+                    f,
+                    "use {source} as {name};",
+                    source = print_import_source(&import.source, cx)
+                )
             }
-            clean::ImportKind::Glob => {
-                if self.source.path.segments.is_empty() {
-                    write!(f, "use *;")
-                } else {
-                    write!(f, "use {}::*;", self.source.print(cx))
-                }
+        }
+        clean::ImportKind::Glob => {
+            if import.source.path.segments.is_empty() {
+                write!(f, "use *;")
+            } else {
+                write!(f, "use {}::*;", print_import_source(&import.source, cx))
             }
-        })
-    }
+        }
+    })
 }
 
-impl clean::ImportSource {
-    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
-        fmt::from_fn(move |f| match self.did {
-            Some(did) => resolved_path(f, did, &self.path, true, false, cx),
-            _ => {
-                for seg in &self.path.segments[..self.path.segments.len() - 1] {
-                    write!(f, "{}::", seg.name)?;
-                }
-                let name = self.path.last();
-                if let hir::def::Res::PrimTy(p) = self.path.res {
-                    primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
-                } else {
-                    f.write_str(name.as_str())?;
-                }
-                Ok(())
+fn print_import_source(import_source: &clean::ImportSource, cx: &Context<'_>) -> impl Display {
+    fmt::from_fn(move |f| match import_source.did {
+        Some(did) => resolved_path(f, did, &import_source.path, true, false, cx),
+        _ => {
+            for seg in &import_source.path.segments[..import_source.path.segments.len() - 1] {
+                write!(f, "{}::", seg.name)?;
             }
-        })
-    }
+            let name = import_source.path.last();
+            if let hir::def::Res::PrimTy(p) = import_source.path.res {
+                primitive_link(f, PrimitiveType::from(p), format_args!("{name}"), cx)?;
+            } else {
+                f.write_str(name.as_str())?;
+            }
+            Ok(())
+        }
+    })
 }
 
-impl clean::AssocItemConstraint {
-    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
-        fmt::from_fn(move |f| {
-            f.write_str(self.assoc.name.as_str())?;
-            self.assoc.args.print(cx).fmt(f)?;
-            match self.kind {
-                clean::AssocItemConstraintKind::Equality { ref term } => {
-                    f.write_str(" = ")?;
-                    term.print(cx).fmt(f)?;
-                }
-                clean::AssocItemConstraintKind::Bound { ref bounds } => {
-                    if !bounds.is_empty() {
-                        f.write_str(": ")?;
-                        print_generic_bounds(bounds, cx).fmt(f)?;
-                    }
+fn print_assoc_item_constraint(
+    assoc_item_constraint: &clean::AssocItemConstraint,
+    cx: &Context<'_>,
+) -> impl Display {
+    fmt::from_fn(move |f| {
+        f.write_str(assoc_item_constraint.assoc.name.as_str())?;
+        print_generic_args(&assoc_item_constraint.assoc.args, cx).fmt(f)?;
+        match assoc_item_constraint.kind {
+            clean::AssocItemConstraintKind::Equality { ref term } => {
+                f.write_str(" = ")?;
+                print_term(term, cx).fmt(f)?;
+            }
+            clean::AssocItemConstraintKind::Bound { ref bounds } => {
+                if !bounds.is_empty() {
+                    f.write_str(": ")?;
+                    print_generic_bounds(bounds, cx).fmt(f)?;
                 }
             }
-            Ok(())
-        })
-    }
+        }
+        Ok(())
+    })
 }
 
 pub(crate) fn print_abi_with_space(abi: ExternAbi) -> impl Display {
@@ -1538,22 +1540,18 @@ pub(crate) fn print_default_space(v: bool) -> &'static str {
     if v { "default " } else { "" }
 }
 
-impl clean::GenericArg {
-    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
-        fmt::from_fn(move |f| match self {
-            clean::GenericArg::Lifetime(lt) => lt.print().fmt(f),
-            clean::GenericArg::Type(ty) => ty.print(cx).fmt(f),
-            clean::GenericArg::Const(ct) => ct.print(cx.tcx()).fmt(f),
-            clean::GenericArg::Infer => Display::fmt("_", f),
-        })
-    }
+fn print_generic_arg(generic_arg: &clean::GenericArg, cx: &Context<'_>) -> impl Display {
+    fmt::from_fn(move |f| match generic_arg {
+        clean::GenericArg::Lifetime(lt) => f.write_str(print_lifetime(lt)),
+        clean::GenericArg::Type(ty) => print_type(ty, cx).fmt(f),
+        clean::GenericArg::Const(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
+        clean::GenericArg::Infer => f.write_char('_'),
+    })
 }
 
-impl clean::Term {
-    pub(crate) fn print(&self, cx: &Context<'_>) -> impl Display {
-        fmt::from_fn(move |f| match self {
-            clean::Term::Type(ty) => ty.print(cx).fmt(f),
-            clean::Term::Constant(ct) => ct.print(cx.tcx()).fmt(f),
-        })
-    }
+fn print_term(term: &clean::Term, cx: &Context<'_>) -> impl Display {
+    fmt::from_fn(move |f| match term {
+        clean::Term::Type(ty) => print_type(ty, cx).fmt(f),
+        clean::Term::Constant(ct) => print_constant_kind(ct, cx.tcx()).fmt(f),
+    })
 }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 5e42bb9861bfa..dbff5c510af29 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -601,52 +601,52 @@ pub(super) fn write_code(
     };
     let mut current_expansion = get_expansion(&mut token_handler, expanded_codes, file_span);
 
-    Classifier::new(
+    classify(
         &src,
-        token_handler.href_context.as_ref().map(|c| c.file_span).unwrap_or(DUMMY_SP),
+        token_handler.href_context.as_ref().map_or(DUMMY_SP, |c| c.file_span),
         decoration_info,
-    )
-    .highlight(&mut |span, highlight| match highlight {
-        Highlight::Token { text, class } => {
-            token_handler.push_token(class, Cow::Borrowed(text));
-
-            if text == "\n" {
-                if current_expansion.is_none() {
-                    current_expansion = get_expansion(&mut token_handler, expanded_codes, span);
-                }
-                if let Some(ref current_expansion) = current_expansion
-                    && current_expansion.span.lo() == span.hi()
-                {
-                    token_handler.add_expanded_code(current_expansion);
-                }
-            } else {
-                let mut need_end = false;
-                if let Some(ref current_expansion) = current_expansion {
-                    if current_expansion.span.lo() == span.hi() {
-                        token_handler.add_expanded_code(current_expansion);
-                    } else if current_expansion.end_line == token_handler.line
-                        && span.hi() >= current_expansion.span.hi()
+        &mut |span, highlight| match highlight {
+            Highlight::Token { text, class } => {
+                token_handler.push_token(class, Cow::Borrowed(text));
+
+                if text == "\n" {
+                    if current_expansion.is_none() {
+                        current_expansion = get_expansion(&mut token_handler, expanded_codes, span);
+                    }
+                    if let Some(ref current_expansion) = current_expansion
+                        && current_expansion.span.lo() == span.hi()
                     {
-                        need_end = true;
+                        token_handler.add_expanded_code(current_expansion);
+                    }
+                } else {
+                    let mut need_end = false;
+                    if let Some(ref current_expansion) = current_expansion {
+                        if current_expansion.span.lo() == span.hi() {
+                            token_handler.add_expanded_code(current_expansion);
+                        } else if current_expansion.end_line == token_handler.line
+                            && span.hi() >= current_expansion.span.hi()
+                        {
+                            need_end = true;
+                        }
+                    }
+                    if need_end {
+                        current_expansion = end_expansion(&mut token_handler, expanded_codes, span);
                     }
-                }
-                if need_end {
-                    current_expansion = end_expansion(&mut token_handler, expanded_codes, span);
                 }
             }
-        }
-        Highlight::EnterSpan { class } => {
-            token_handler.class_stack.enter_elem(
-                token_handler.out,
-                &token_handler.href_context,
-                class,
-                None,
-            );
-        }
-        Highlight::ExitSpan => {
-            token_handler.class_stack.exit_elem();
-        }
-    });
+            Highlight::EnterSpan { class } => {
+                token_handler.class_stack.enter_elem(
+                    token_handler.out,
+                    &token_handler.href_context,
+                    class,
+                    None,
+                );
+            }
+            Highlight::ExitSpan => {
+                token_handler.class_stack.exit_elem();
+            }
+        },
+    );
 }
 
 fn write_footer(playground_button: Option<&str>) -> impl Display {
@@ -770,6 +770,12 @@ struct TokenIter<'a> {
     cursor: Cursor<'a>,
 }
 
+impl<'a> TokenIter<'a> {
+    fn new(src: &'a str) -> Self {
+        Self { src, cursor: Cursor::new(src, FrontmatterAllowed::Yes) }
+    }
+}
+
 impl<'a> Iterator for TokenIter<'a> {
     type Item = (TokenKind, &'a str);
     fn next(&mut self) -> Option<(TokenKind, &'a str)> {
@@ -783,21 +789,6 @@ impl<'a> Iterator for TokenIter<'a> {
     }
 }
 
-/// Classifies into identifier class; returns `None` if this is a non-keyword identifier.
-fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option {
-    let ignore: &[&str] =
-        if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
-    if ignore.contains(&text) {
-        return None;
-    }
-    Some(match text {
-        "ref" | "mut" => Class::RefKeyWord,
-        "false" | "true" => Class::Bool,
-        _ if Symbol::intern(text).is_reserved(|| Edition::Edition2021) => Class::KeyWord,
-        _ => return None,
-    })
-}
-
 /// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than
 /// just the next item by using `peek_next`. The `peek` method always returns the next item after
 /// the current one whereas `peek_next` will return the next item after the last one peeked.
@@ -815,16 +806,16 @@ impl<'a> PeekIter<'a> {
         Self { stored: VecDeque::new(), peek_pos: 0, iter }
     }
     /// Returns the next item after the current one. It doesn't interfere with `peek_next` output.
-    fn peek(&mut self) -> Option<&(TokenKind, &'a str)> {
+    fn peek(&mut self) -> Option<(TokenKind, &'a str)> {
         if self.stored.is_empty()
             && let Some(next) = self.iter.next()
         {
             self.stored.push_back(next);
         }
-        self.stored.front()
+        self.stored.front().copied()
     }
     /// Returns the next item after the last one peeked. It doesn't interfere with `peek` output.
-    fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> {
+    fn peek_next(&mut self) -> Option<(TokenKind, &'a str)> {
         self.peek_pos += 1;
         if self.peek_pos - 1 < self.stored.len() {
             self.stored.get(self.peek_pos - 1)
@@ -834,6 +825,7 @@ impl<'a> PeekIter<'a> {
         } else {
             None
         }
+        .copied()
     }
 
     fn stop_peeking(&mut self) {
@@ -878,6 +870,54 @@ fn new_span(lo: u32, text: &str, file_span: Span) -> Span {
     file_span.with_lo(file_lo + BytePos(lo)).with_hi(file_lo + BytePos(hi))
 }
 
+fn classify<'src>(
+    src: &'src str,
+    file_span: Span,
+    decoration_info: Option<&DecorationInfo>,
+    sink: &mut dyn FnMut(Span, Highlight<'src>),
+) {
+    let offset = rustc_lexer::strip_shebang(src);
+
+    if let Some(offset) = offset {
+        sink(DUMMY_SP, Highlight::Token { text: &src[..offset], class: Some(Class::Comment) });
+    }
+
+    let mut classifier =
+        Classifier::new(src, offset.unwrap_or_default(), file_span, decoration_info);
+
+    loop {
+        if let Some(decs) = classifier.decorations.as_mut() {
+            let byte_pos = classifier.byte_pos;
+            let n_starts = decs.starts.iter().filter(|(i, _)| byte_pos >= *i).count();
+            for (_, kind) in decs.starts.drain(0..n_starts) {
+                sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Decoration(kind) });
+            }
+
+            let n_ends = decs.ends.iter().filter(|i| byte_pos >= **i).count();
+            for _ in decs.ends.drain(0..n_ends) {
+                sink(DUMMY_SP, Highlight::ExitSpan);
+            }
+        }
+
+        if let Some((TokenKind::Colon | TokenKind::Ident, _)) = classifier.tokens.peek() {
+            let tokens = classifier.get_full_ident_path();
+            for &(token, start, end) in &tokens {
+                let text = &classifier.src[start..end];
+                classifier.advance(token, text, sink, start as u32);
+                classifier.byte_pos += text.len() as u32;
+            }
+            if !tokens.is_empty() {
+                continue;
+            }
+        }
+        if let Some((token, text, before)) = classifier.next() {
+            classifier.advance(token, text, sink, before);
+        } else {
+            break;
+        }
+    }
+}
+
 /// Processes program tokens, classifying strings of text by highlighting
 /// category (`Class`).
 struct Classifier<'src> {
@@ -892,21 +932,23 @@ struct Classifier<'src> {
 }
 
 impl<'src> Classifier<'src> {
-    /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code
-    /// file span which will be used later on by the `span_correspondence_map`.
-    fn new(src: &'src str, file_span: Span, decoration_info: Option<&DecorationInfo>) -> Self {
-        let tokens =
-            PeekIter::new(TokenIter { src, cursor: Cursor::new(src, FrontmatterAllowed::Yes) });
-        let decorations = decoration_info.map(Decorations::new);
+    /// Takes as argument the source code to HTML-ify and the source code file span
+    /// which will be used later on by the `span_correspondence_map`.
+    fn new(
+        src: &'src str,
+        byte_pos: usize,
+        file_span: Span,
+        decoration_info: Option<&DecorationInfo>,
+    ) -> Self {
         Classifier {
-            tokens,
+            tokens: PeekIter::new(TokenIter::new(&src[byte_pos..])),
             in_attribute: false,
             in_macro: false,
             in_macro_nonterminal: false,
-            byte_pos: 0,
+            byte_pos: byte_pos as u32,
             file_span,
             src,
-            decorations,
+            decorations: decoration_info.map(Decorations::new),
         }
     }
 
@@ -934,15 +976,10 @@ impl<'src> Classifier<'src> {
                 }
             }
 
-            if let Some((None, text)) = self.tokens.peek().map(|(token, text)| {
-                if *token == TokenKind::Ident {
-                    let class = get_real_ident_class(text, true);
-                    (class, text)
-                } else {
-                    // Doesn't matter which Class we put in here...
-                    (Some(Class::Comment), text)
-                }
-            }) {
+            if let Some((TokenKind::Ident, text)) = self.tokens.peek()
+                && let symbol = Symbol::intern(text)
+                && (symbol.is_path_segment_keyword() || !is_keyword(symbol))
+            {
                 // We only "add" the colon if there is an ident behind.
                 pos += text.len() + nb;
                 has_ident = true;
@@ -973,50 +1010,6 @@ impl<'src> Classifier<'src> {
         }
     }
 
-    /// Exhausts the `Classifier` writing the output into `sink`.
-    ///
-    /// The general structure for this method is to iterate over each token,
-    /// possibly giving it an HTML span with a class specifying what flavor of
-    /// token is used.
-    fn highlight(mut self, sink: &mut dyn FnMut(Span, Highlight<'src>)) {
-        loop {
-            if let Some(decs) = self.decorations.as_mut() {
-                let byte_pos = self.byte_pos;
-                let n_starts = decs.starts.iter().filter(|(i, _)| byte_pos >= *i).count();
-                for (_, kind) in decs.starts.drain(0..n_starts) {
-                    sink(DUMMY_SP, Highlight::EnterSpan { class: Class::Decoration(kind) });
-                }
-
-                let n_ends = decs.ends.iter().filter(|i| byte_pos >= **i).count();
-                for _ in decs.ends.drain(0..n_ends) {
-                    sink(DUMMY_SP, Highlight::ExitSpan);
-                }
-            }
-
-            if self
-                .tokens
-                .peek()
-                .map(|t| matches!(t.0, TokenKind::Colon | TokenKind::Ident))
-                .unwrap_or(false)
-            {
-                let tokens = self.get_full_ident_path();
-                for (token, start, end) in &tokens {
-                    let text = &self.src[*start..*end];
-                    self.advance(*token, text, sink, *start as u32);
-                    self.byte_pos += text.len() as u32;
-                }
-                if !tokens.is_empty() {
-                    continue;
-                }
-            }
-            if let Some((token, text, before)) = self.next() {
-                self.advance(token, text, sink, before);
-            } else {
-                break;
-            }
-        }
-    }
-
     /// Single step of highlighting. This will classify `token`, but maybe also a couple of
     /// following ones as well.
     ///
@@ -1054,6 +1047,7 @@ impl<'src> Classifier<'src> {
                     Class::Comment
                 }
             }
+            TokenKind::Frontmatter { .. } => Class::Comment,
             // Consider this as part of a macro invocation if there was a
             // leading identifier.
             TokenKind::Bang if self.in_macro => {
@@ -1152,7 +1146,6 @@ impl<'src> Classifier<'src> {
             | TokenKind::At
             | TokenKind::Tilde
             | TokenKind::Colon
-            | TokenKind::Frontmatter { .. }
             | TokenKind::Unknown => return no_highlight(sink),
 
             TokenKind::Question => Class::QuestionMark,
@@ -1224,7 +1217,7 @@ impl<'src> Classifier<'src> {
             },
             TokenKind::GuardedStrPrefix => return no_highlight(sink),
             TokenKind::Ident | TokenKind::RawIdent
-                if self.peek_non_whitespace() == Some(TokenKind::Bang) =>
+                if let Some((TokenKind::Bang, _)) = self.peek_non_trivia() =>
             {
                 self.in_macro = true;
                 let span = new_span(before, text, file_span);
@@ -1232,26 +1225,7 @@ impl<'src> Classifier<'src> {
                 sink(span, Highlight::Token { text, class: None });
                 return;
             }
-            TokenKind::Ident => {
-                match get_real_ident_class(text, false) {
-                    None => match text {
-                        "Option" | "Result" => Class::PreludeTy(new_span(before, text, file_span)),
-                        "Some" | "None" | "Ok" | "Err" => {
-                            Class::PreludeVal(new_span(before, text, file_span))
-                        }
-                        // "union" is a weak keyword and is only considered as a keyword when declaring
-                        // a union type.
-                        "union" if self.check_if_is_union_keyword() => Class::KeyWord,
-                        _ if self.in_macro_nonterminal => {
-                            self.in_macro_nonterminal = false;
-                            Class::MacroNonTerminal
-                        }
-                        "self" | "Self" => Class::Self_(new_span(before, text, file_span)),
-                        _ => Class::Ident(new_span(before, text, file_span)),
-                    },
-                    Some(c) => c,
-                }
-            }
+            TokenKind::Ident => self.classify_ident(before, text),
             TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
                 Class::Ident(new_span(before, text, file_span))
             }
@@ -1272,25 +1246,66 @@ impl<'src> Classifier<'src> {
         }
     }
 
+    fn classify_ident(&mut self, before: u32, text: &'src str) -> Class {
+        // Macro non-terminals (meta vars) take precedence.
+        if self.in_macro_nonterminal {
+            self.in_macro_nonterminal = false;
+            return Class::MacroNonTerminal;
+        }
+
+        let file_span = self.file_span;
+        let span = || new_span(before, text, file_span);
+
+        match text {
+            "ref" | "mut" => Class::RefKeyWord,
+            "false" | "true" => Class::Bool,
+            "self" | "Self" => Class::Self_(span()),
+            "Option" | "Result" => Class::PreludeTy(span()),
+            "Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()),
+            _ if self.is_weak_keyword(text) || is_keyword(Symbol::intern(text)) => Class::KeyWord,
+            _ => Class::Ident(span()),
+        }
+    }
+
+    fn is_weak_keyword(&mut self, text: &str) -> bool {
+        // NOTE: `yeet` (`do yeet $expr`), `catch` (`do catch $block`), `default` (specialization),
+        // `contract_{ensures,requires}`, `builtin` (builtin_syntax) & `reuse` (fn_delegation) are
+        // too difficult or annoying to properly detect under this simple scheme.
+
+        let matches = match text {
+            "auto" => |text| text == "trait", // `auto trait Trait {}` (`auto_traits`)
+            "pin" => |text| text == "const" || text == "mut", // `&pin mut Type` (`pin_ergonomics`)
+            "raw" => |text| text == "const" || text == "mut", // `&raw const local`
+            "safe" => |text| text == "fn" || text == "extern", // `unsafe extern { safe fn f(); }`
+            "union" => |_| true,              // `union Untagged { field: () }`
+            _ => return false,
+        };
+        matches!(self.peek_non_trivia(), Some((TokenKind::Ident, text)) if matches(text))
+    }
+
     fn peek(&mut self) -> Option {
-        self.tokens.peek().map(|(token_kind, _text)| *token_kind)
+        self.tokens.peek().map(|(kind, _)| kind)
     }
 
-    fn peek_non_whitespace(&mut self) -> Option {
-        while let Some((token_kind, _)) = self.tokens.peek_next() {
-            if *token_kind != TokenKind::Whitespace {
-                let token_kind = *token_kind;
-                self.tokens.stop_peeking();
-                return Some(token_kind);
+    fn peek_non_trivia(&mut self) -> Option<(TokenKind, &str)> {
+        while let Some(token @ (kind, _)) = self.tokens.peek_next() {
+            if let TokenKind::Whitespace
+            | TokenKind::LineComment { doc_style: None }
+            | TokenKind::BlockComment { doc_style: None, .. } = kind
+            {
+                continue;
             }
+            self.tokens.stop_peeking();
+            return Some(token);
         }
         self.tokens.stop_peeking();
         None
     }
+}
 
-    fn check_if_is_union_keyword(&mut self) -> bool {
-        self.peek_non_whitespace().is_some_and(|kind| kind == TokenKind::Ident)
-    }
+fn is_keyword(symbol: Symbol) -> bool {
+    // FIXME(#148221): Don't hard-code the edition. The classifier should take it as an argument.
+    symbol.is_reserved(|| Edition::Edition2024)
 }
 
 fn generate_link_to_def(
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 13178ee4e9934..36990332b2fc8 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -74,8 +74,9 @@ use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::format::{
-    Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space,
-    print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
+    Ending, HrefError, PrintWithSpace, full_print_fn_decl, href, print_abi_with_space,
+    print_constness_with_space, print_default_space, print_generic_bounds, print_generics,
+    print_impl, print_path, print_type, print_where_clause, visibility_print_with_space,
 };
 use crate::html::markdown::{
     HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
@@ -1044,8 +1045,8 @@ fn assoc_const(
             vis = visibility_print_with_space(it, cx),
             href = assoc_href_attr(it, link, cx).maybe_display(),
             name = it.name.as_ref().unwrap(),
-            generics = generics.print(cx),
-            ty = ty.print(cx),
+            generics = print_generics(generics, cx),
+            ty = print_type(ty, cx),
         )?;
         if let AssocConstValue::TraitDefault(konst) | AssocConstValue::Impl(konst) = value {
             // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
@@ -1083,14 +1084,14 @@ fn assoc_type(
             vis = visibility_print_with_space(it, cx),
             href = assoc_href_attr(it, link, cx).maybe_display(),
             name = it.name.as_ref().unwrap(),
-            generics = generics.print(cx),
+            generics = print_generics(generics, cx),
         )?;
         if !bounds.is_empty() {
             write!(w, ": {}", print_generic_bounds(bounds, cx))?;
         }
         // Render the default before the where-clause which aligns with the new recommended style. See #89122.
         if let Some(default) = default {
-            write!(w, " = {}", default.print(cx))?;
+            write!(w, " = {}", print_type(default, cx))?;
         }
         write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline).maybe_display())
     })
@@ -1128,7 +1129,7 @@ fn assoc_method(
         let href = assoc_href_attr(meth, link, cx).maybe_display();
 
         // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`.
-        let generics_len = format!("{:#}", g.print(cx)).len();
+        let generics_len = format!("{:#}", print_generics(g, cx)).len();
         let mut header_len = "fn ".len()
             + vis.len()
             + defaultness.len()
@@ -1155,8 +1156,8 @@ fn assoc_method(
             "{indent}{vis}{defaultness}{constness}{asyncness}{safety}{abi}fn \
             {name}{generics}{decl}{notable_traits}{where_clause}",
             indent = indent_str,
-            generics = g.print(cx),
-            decl = d.full_print(header_len, indent, cx),
+            generics = print_generics(g, cx),
+            decl = full_print_fn_decl(d, header_len, indent, cx),
             where_clause = print_where_clause(g, cx, indent, end_newline).maybe_display(),
         )
     })
@@ -1441,8 +1442,10 @@ fn render_assoc_items_inner(
                 Cow::Borrowed("implementations-list"),
             ),
             AssocItemRender::DerefFor { trait_, type_, .. } => {
-                let id =
-                    cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx))));
+                let id = cx.derive_id(small_url_encode(format!(
+                    "deref-methods-{:#}",
+                    print_type(type_, cx)
+                )));
                 // the `impls.get` above only looks at the outermost type,
                 // and the Deref impl may only be implemented for certain
                 // values of generic parameters.
@@ -1466,8 +1469,8 @@ fn render_assoc_items_inner(
                                 fmt::from_fn(|f| write!(
                                     f,
                                     "Methods from {trait_}<Target = {type_}>",
-                                    trait_ = trait_.print(cx),
-                                    type_ = type_.print(cx),
+                                    trait_ = print_path(trait_, cx),
+                                    type_ = print_type(type_, cx),
                                 )),
                                 &id,
                             )
@@ -1645,7 +1648,7 @@ fn notable_traits_button(ty: &clean::Type, cx: &Context<'_>) -> Optionⓘ",
-                ty = Escape(&format!("{:#}", ty.print(cx))),
+                ty = Escape(&format!("{:#}", print_type(ty, cx))),
             )
         })
     })
@@ -1683,7 +1686,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
                 f,
                 "

Notable traits for {}

\
",
-                impl_.for_.print(cx)
+                print_type(&impl_.for_, cx),
             )?;
             true
         } else {
@@ -1691,7 +1694,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
         };
 
         for (impl_, trait_did) in notable_impls {
-            write!(f, "
{}
", impl_.print(false, cx))?; + write!(f, "
{}
", print_impl(impl_, false, cx))?; for it in &impl_.items { let clean::AssocTypeItem(tydef, ..) = &it.kind else { continue; @@ -1724,7 +1727,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) { }) .to_string(); - (format!("{:#}", ty.print(cx)), out) + (format!("{:#}", print_type(ty, cx)), out) } fn notable_traits_json<'a>(tys: impl Iterator, cx: &Context<'_>) -> String { @@ -2284,7 +2287,7 @@ fn render_impl_summary( )?; if let Some(use_absolute) = use_absolute { - write!(w, "{}", inner_impl.print(use_absolute, cx))?; + write!(w, "{}", print_impl(inner_impl, use_absolute, cx))?; if show_def_docs { for it in &inner_impl.items { if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind { @@ -2305,7 +2308,7 @@ fn render_impl_summary( } } } else { - write!(w, "{}", inner_impl.print(false, cx))?; + write!(w, "{}", print_impl(inner_impl, false, cx))?; } w.write_str("")?; @@ -2423,7 +2426,10 @@ fn extract_for_impl_name(item: &clean::Item, cx: &Context<'_>) -> Option<(String clean::ItemKind::ImplItem(ref i) if i.trait_.is_some() => { // Alternative format produces no URLs, // so this parameter does nothing. - Some((format!("{:#}", i.for_.print(cx)), get_id_for_impl(cx.tcx(), item.item_id))) + Some(( + format!("{:#}", print_type(&i.for_, cx)), + get_id_for_impl(cx.tcx(), item.item_id), + )) } _ => None, } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 6bb8f2043f4bb..fa7c9e75fdac4 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -31,7 +31,8 @@ use crate::formats::Impl; use crate::formats::item_type::ItemType; use crate::html::escape::{Escape, EscapeBodyTextWithWbr}; use crate::html::format::{ - Ending, PrintWithSpace, print_abi_with_space, print_constness_with_space, print_where_clause, + Ending, PrintWithSpace, full_print_fn_decl, print_abi_with_space, print_constness_with_space, + print_generic_bound, print_generics, print_impl, print_import, print_type, print_where_clause, visibility_print_with_space, }; use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine}; @@ -462,7 +463,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i "{vis}{imp}
{stab_tags}\ ", vis = visibility_print_with_space(myitem, cx), - imp = import.print(cx) + imp = print_import(import, cx), )?; } _ => { @@ -611,7 +612,7 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp let visibility = visibility_print_with_space(it, cx).to_string(); let name = it.name.unwrap(); - let generics_len = format!("{:#}", f.generics.print(cx)).len(); + let generics_len = format!("{:#}", print_generics(&f.generics, cx)).len(); let header_len = "fn ".len() + visibility.len() + constness.len() @@ -635,10 +636,10 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp safety = safety, abi = abi, name = name, - generics = f.generics.print(cx), + generics = print_generics(&f.generics, cx), where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline).maybe_display(), - decl = f.decl.full_print(header_len, 0, cx), + decl = full_print_fn_decl(&f.decl, header_len, 0, cx), ) })?; write!(w, "{}", document(cx, it, None, HeadingOffset::H2)) @@ -673,7 +674,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: safety = t.safety(tcx).print_with_space(), is_auto = if t.is_auto(tcx) { "auto " } else { "" }, name = it.name.unwrap(), - generics = t.generics.print(cx), + generics = print_generics(&t.generics, cx), )?; if !t.generics.where_predicates.is_empty() { @@ -1244,7 +1245,7 @@ fn item_trait_alias( w, "trait {name}{generics} = {bounds}{where_clause};", name = it.name.unwrap(), - generics = t.generics.print(cx), + generics = print_generics(&t.generics, cx), bounds = print_bounds(&t.bounds, true, cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::NoNewline).maybe_display(), @@ -1273,10 +1274,10 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> "{vis}type {name}{generics}{where_clause} = {type_};", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), - generics = t.generics.print(cx), + generics = print_generics(&t.generics, cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline).maybe_display(), - type_ = t.type_.print(cx), + type_ = print_type(&t.type_, cx), ) })?; @@ -1477,7 +1478,7 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> { } fn print_ty(&self, ty: &'a clean::Type) -> impl Display { - ty.print(self.cx) + print_type(ty, self.cx) } // FIXME (GuillaumeGomez): When is implemented, @@ -1523,7 +1524,7 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa .map(|ty| { fmt::from_fn(|f| match ty.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_"), - clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx)), + clean::StructFieldItem(ref ty) => write!(f, "{}", print_type(ty, cx)), _ => unreachable!(), }) }) @@ -1562,7 +1563,7 @@ impl<'clean> DisplayEnum<'clean> { "{}enum {}{}{}", visibility_print_with_space(it, cx), it.name.unwrap(), - self.generics.print(cx), + print_generics(&self.generics, cx), render_enum_fields( cx, Some(self.generics), @@ -1862,7 +1863,7 @@ fn item_variants( {doc}\ ", f = field.name.unwrap(), - t = ty.print(cx), + t = print_type(ty, cx), doc = document(cx, field, Some(variant), HeadingOffset::H5), )?; } @@ -1956,8 +1957,8 @@ fn item_constant( "{vis}const {name}{generics}: {typ}{where_clause}", vis = visibility_print_with_space(it, cx), name = it.name.unwrap(), - generics = generics.print(cx), - typ = ty.print(cx), + generics = print_generics(generics, cx), + typ = print_type(ty, cx), where_clause = print_where_clause(generics, cx, 0, Ending::NoNewline).maybe_display(), )?; @@ -2102,7 +2103,7 @@ fn item_fields( "{field_name}: {ty}\ \ {doc}", - ty = ty.print(cx), + ty = print_type(ty, cx), doc = document(cx, field, Some(it), HeadingOffset::H3), )?; } @@ -2127,7 +2128,7 @@ fn item_static( safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""), mutability = s.mutability.print_with_space(), name = it.name.unwrap(), - typ = s.type_.print(cx) + typ = print_type(&s.type_, cx) ) })?; @@ -2286,7 +2287,7 @@ fn print_bounds( } } - bounds.iter().map(|p| p.print(cx)).joined(inter_str, f) + bounds.iter().map(|p| print_generic_bound(p, cx)).joined(inter_str, f) })) .maybe_display() } @@ -2307,7 +2308,7 @@ struct ImplString(String); impl ImplString { fn new(i: &Impl, cx: &Context<'_>) -> ImplString { - ImplString(format!("{}", i.inner_impl().print(false, cx))) + ImplString(format!("{}", print_impl(i.inner_impl(), false, cx))) } } @@ -2376,7 +2377,7 @@ fn render_union( write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?; let where_displayed = if let Some(generics) = g { - write!(f, "{}", generics.print(cx))?; + write!(f, "{}", print_generics(generics, cx))?; if let Some(where_clause) = print_where_clause(generics, cx, 0, Ending::Newline) { write!(f, "{where_clause}")?; true @@ -2408,7 +2409,7 @@ fn render_union( " {}{}: {},", visibility_print_with_space(field, cx), field.name.unwrap(), - ty.print(cx) + print_type(ty, cx) )?; } } @@ -2442,7 +2443,7 @@ fn render_struct( it.name.unwrap() )?; if let Some(g) = g { - write!(w, "{}", g.print(cx))?; + write!(w, "{}", print_generics(g, cx))?; } write!( w, @@ -2505,7 +2506,7 @@ fn render_struct_fields( "{tab} {vis}{name}: {ty},", vis = visibility_print_with_space(field, cx), name = field.name.unwrap(), - ty = ty.print(cx) + ty = print_type(ty, cx) )?; } } @@ -2548,7 +2549,7 @@ fn render_struct_fields( w, "{}{}", visibility_print_with_space(field, cx), - ty.print(cx) + print_type(ty, cx), )?; } _ => unreachable!(), diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs index b9f5ada417c7f..df9e8631bbdd6 100644 --- a/src/librustdoc/html/render/sidebar.rs +++ b/src/librustdoc/html/render/sidebar.rs @@ -13,6 +13,7 @@ use super::{Context, ItemSection, item_ty_to_section}; use crate::clean; use crate::formats::Impl; use crate::formats::item_type::ItemType; +use crate::html::format::{print_path, print_type}; use crate::html::markdown::{IdMap, MarkdownWithToc}; use crate::html::render::print_item::compare_names; @@ -558,8 +559,8 @@ fn sidebar_deref_methods<'a>( }; let title = format!( "Methods from {:#}", - impl_.inner_impl().trait_.as_ref().unwrap().print(cx), - real_target.print(cx), + print_path(impl_.inner_impl().trait_.as_ref().unwrap(), cx), + print_type(real_target, cx), ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); @@ -690,7 +691,7 @@ fn sidebar_render_assoc_items( ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "", ty::ImplPolarity::Negative => "!", }; - let generated = Link::new(encoded, format!("{prefix}{:#}", trait_.print(cx))); + let generated = Link::new(encoded, format!("{prefix}{:#}", print_path(trait_, cx))); if links.insert(generated.clone()) { Some(generated) } else { None } }) .collect::>>(); diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 3a1db805d01cf..6045b9a77ecae 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -44,6 +44,7 @@ use crate::docfs::PathError; use crate::error::Error; use crate::formats::Impl; use crate::formats::item_type::ItemType; +use crate::html::format::{print_impl, print_path}; use crate::html::layout; use crate::html::render::ordered_json::{EscapedJson, OrderedJson}; use crate::html::render::search_index::{SerializedSearchIndex, build_index}; @@ -605,7 +606,7 @@ impl TypeAliasPart { .inner_impl() .trait_ .as_ref() - .map(|trait_| format!("{:#}", trait_.print(cx))); + .map(|trait_| format!("{:#}", print_path(trait_, cx))); ret = Some(AliasSerializableImpl { text, trait_, @@ -704,7 +705,7 @@ impl TraitAliasPart { None } else { Some(Implementor { - text: imp.inner_impl().print(false, cx).to_string(), + text: print_impl(imp.inner_impl(), false, cx).to_string(), synthetic: imp.inner_impl().kind.is_auto(), types: collect_paths_for_type(&imp.inner_impl().for_, cache), }) diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr index 7f7fc63ac0ddd..b85a23e3c7db1 100644 --- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr +++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr @@ -1,11 +1,15 @@ memory allocation of 4 bytes failed +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace +note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect error: abnormal termination: the program aborted execution --> RUSTLIB/std/src/alloc.rs:LL:CC | -LL | crate::process::abort() - | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here +LL | crate::process::abort() + | ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here | = note: BACKTRACE: + = note: inside closure at RUSTLIB/std/src/alloc.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::alloc::rust_oom::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::_::__rust_alloc_error_handler` at RUSTLIB/std/src/alloc.rs:LL:CC = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC diff --git a/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.rs new file mode 100644 index 0000000000000..54cecc23f9e71 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics, portable_simd)] + +use std::intrinsics::simd::simd_funnel_shl; +use std::simd::*; + +fn main() { + unsafe { + let x = i32x2::from_array([1, 1]); + let y = i32x2::from_array([100, 0]); + simd_funnel_shl(x, x, y); //~ERROR: overflowing shift by 100 in `simd_funnel_shl` in lane 0 + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.stderr new file mode 100644 index 0000000000000..00c7c39a8856f --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shl-too-far.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: overflowing shift by 100 in `simd_funnel_shl` in lane 0 + --> tests/fail/intrinsics/simd-funnel_shl-too-far.rs:LL:CC + | +LL | simd_funnel_shl(x, x, y); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.rs b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.rs new file mode 100644 index 0000000000000..6fb2daa488484 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.rs @@ -0,0 +1,12 @@ +#![feature(core_intrinsics, portable_simd)] + +use std::intrinsics::simd::simd_funnel_shr; +use std::simd::*; + +fn main() { + unsafe { + let x = i32x2::from_array([1, 1]); + let y = i32x2::from_array([20, 40]); + simd_funnel_shr(x, x, y); //~ERROR: overflowing shift by 40 in `simd_funnel_shr` in lane 1 + } +} diff --git a/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.stderr b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.stderr new file mode 100644 index 0000000000000..2f6c6cd71ea6d --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/simd-funnel_shr-too-far.stderr @@ -0,0 +1,13 @@ +error: Undefined Behavior: overflowing shift by 40 in `simd_funnel_shr` in lane 1 + --> tests/fail/intrinsics/simd-funnel_shr-too-far.rs:LL:CC + | +LL | simd_funnel_shr(x, x, y); + | ^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs index 4ecbe167b5253..961a4b82a7e9b 100644 --- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs +++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs @@ -62,7 +62,7 @@ impl PackedSimd { #[rustc_nounwind] pub unsafe fn simd_shuffle_const_generic(x: T, y: T) -> U; -pub fn simd_ops_f16() { +fn simd_ops_f16() { use intrinsics::*; // small hack to make type inference better @@ -273,7 +273,7 @@ fn simd_ops_f64() { assert_eq!(f64x2::from_array([f64::NAN, 0.0]).reduce_min(), 0.0); } -pub fn simd_ops_f128() { +fn simd_ops_f128() { use intrinsics::*; // small hack to make type inference better @@ -454,6 +454,18 @@ fn simd_ops_i32() { 0x3fffffffu32 as i32 ]) ); + + // these values are taken from the doctests of `u32::funnel_shl` and `u32::funnel_shr` + let c = u32x4::splat(0x010000b3); + let d = u32x4::splat(0x2fe78e45); + + unsafe { + assert_eq!(intrinsics::simd_funnel_shl(c, d, u32x4::splat(0)), c); + assert_eq!(intrinsics::simd_funnel_shl(c, d, u32x4::splat(8)), u32x4::splat(0x0000b32f)); + + assert_eq!(intrinsics::simd_funnel_shr(c, d, u32x4::splat(0)), d); + assert_eq!(intrinsics::simd_funnel_shr(c, d, u32x4::splat(8)), u32x4::splat(0xb32fe78e)); + } } fn simd_mask() { diff --git a/tests/codegen-llvm/inline-always-callsite.rs b/tests/codegen-llvm/inline-always-callsite.rs new file mode 100644 index 0000000000000..2821c606aa0ee --- /dev/null +++ b/tests/codegen-llvm/inline-always-callsite.rs @@ -0,0 +1,41 @@ +//@ add-minicore +//@ compile-flags: --target aarch64-unknown-linux-gnu -Zinline-mir=no -C no-prepopulate-passes +//@ needs-llvm-components: aarch64 + +#![crate_type = "lib"] +#![feature(no_core, lang_items, target_feature_inline_always)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[inline(always)] +#[target_feature(enable = "neon")] +#[no_mangle] +pub fn single_target_feature() -> i32 { + 42 +} + +#[inline(always)] +#[target_feature(enable = "neon,i8mm")] +#[no_mangle] +// CHECK: define noundef i32 @multiple_target_features() unnamed_addr #1 { +pub fn multiple_target_features() -> i32 { + // CHECK: %_0 = call noundef i32 @single_target_feature() #3 + single_target_feature() +} + +#[no_mangle] +// CHECK: define noundef i32 @inherits_from_global() unnamed_addr #2 { +pub fn inherits_from_global() -> i32 { + unsafe { + // CHECK: %_0 = call noundef i32 @single_target_feature() #3 + single_target_feature() + } +} + +// Attribute #3 requires the alwaysinline attribute, the alwaysinline attribute is not emitted on a +// function definition when target features are present, rather it will be moved onto the function +// call, if the features match up. +// +// CHECK: attributes #3 = { alwaysinline nounwind } diff --git a/tests/rustdoc/jump-to-def/shebang.rs b/tests/rustdoc/jump-to-def/shebang.rs new file mode 100644 index 0000000000000..a631762554b1a --- /dev/null +++ b/tests/rustdoc/jump-to-def/shebang.rs @@ -0,0 +1,15 @@ +#!/path/to/my/interpreter +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +// Ensure that we can successfully generate links to definitions in the presence of shebang. +// Implementation-wise, shebang is not a token that's emitted by the lexer. Instead, we need +// to offset the actual lexing which is tricky due to all the byte index and span calculations +// in the Classifier. + +fn scope() { +//@ has 'src/shebang/shebang.rs.html' +//@ has - '//a[@href="#15"]' 'function' + function(); +} + +fn function() {} diff --git a/tests/rustdoc/source-code-pages/frontmatter.rs b/tests/rustdoc/source-code-pages/frontmatter.rs new file mode 100644 index 0000000000000..c352504bab2b2 --- /dev/null +++ b/tests/rustdoc/source-code-pages/frontmatter.rs @@ -0,0 +1,10 @@ +--- json +{"edition": "2024"} +--- +#![feature(frontmatter)] + +// Test that we highlight frontmatter as comments on source code pages. + +//@ has 'src/frontmatter/frontmatter.rs.html' +//@ has - '//pre[@class="rust"]//span[@class="comment"]' \ +// '--- json {"edition": "2024"} ---' diff --git a/tests/rustdoc/source-code-pages/shebang.rs b/tests/rustdoc/source-code-pages/shebang.rs new file mode 100644 index 0000000000000..975ca3a31858b --- /dev/null +++ b/tests/rustdoc/source-code-pages/shebang.rs @@ -0,0 +1,6 @@ +#!/path/to/somewhere 0 if false "" + +// Test that we highlight shebang as comments on source code pages. + +//@ has 'src/shebang/shebang.rs.html' +//@ has - '//pre[@class="rust"]//span[@class="comment"]' '#!/path/to/somewhere 0 if false ""' diff --git a/tests/ui/alloc-error/alloc-error-backtrace.rs b/tests/ui/alloc-error/alloc-error-backtrace.rs new file mode 100644 index 0000000000000..4185586ba0448 --- /dev/null +++ b/tests/ui/alloc-error/alloc-error-backtrace.rs @@ -0,0 +1,32 @@ +//@ run-pass +//@ ignore-android FIXME #17520 +//@ needs-subprocess +//@ ignore-openbsd no support for libbacktrace without filename +//@ ignore-fuchsia Backtraces not symbolized +//@ compile-flags:-g +//@ compile-flags:-Cstrip=none + +use std::alloc::{Layout, handle_alloc_error}; +use std::process::Command; +use std::{env, str}; + +fn main() { + if env::args().len() > 1 { + handle_alloc_error(Layout::new::<[u8; 42]>()) + } + + let me = env::current_exe().unwrap(); + let output = Command::new(&me).env("RUST_BACKTRACE", "1").arg("next").output().unwrap(); + assert!(!output.status.success(), "{:?} is a success", output.status); + + let mut stderr = str::from_utf8(&output.stderr).unwrap(); + + // When running inside QEMU user-mode emulation, there will be an extra message printed by QEMU + // in the stderr whenever a core dump happens. Remove it before the check. + stderr = stderr + .strip_suffix("qemu: uncaught target signal 6 (Aborted) - core dumped\n") + .unwrap_or(stderr); + + assert!(stderr.contains("memory allocation of 42 bytes failed"), "{}", stderr); + assert!(stderr.contains("alloc_error_backtrace::main"), "{}", stderr); +} diff --git a/tests/ui/alloc-error/default-alloc-error-hook.rs b/tests/ui/alloc-error/default-alloc-error-hook.rs index 7fbc66ca5f472..3092f5044c080 100644 --- a/tests/ui/alloc-error/default-alloc-error-hook.rs +++ b/tests/ui/alloc-error/default-alloc-error-hook.rs @@ -2,9 +2,8 @@ //@ needs-subprocess use std::alloc::{Layout, handle_alloc_error}; -use std::env; use std::process::Command; -use std::str; +use std::{env, str}; fn main() { if env::args().len() > 1 { @@ -12,7 +11,7 @@ fn main() { } let me = env::current_exe().unwrap(); - let output = Command::new(&me).arg("next").output().unwrap(); + let output = Command::new(&me).env("RUST_BACKTRACE", "0").arg("next").output().unwrap(); assert!(!output.status.success(), "{:?} is a success", output.status); let mut stderr = str::from_utf8(&output.stderr).unwrap(); @@ -23,5 +22,9 @@ fn main() { .strip_suffix("qemu: uncaught target signal 6 (Aborted) - core dumped\n") .unwrap_or(stderr); - assert_eq!(stderr, "memory allocation of 42 bytes failed\n"); + assert_eq!( + stderr, + "memory allocation of 42 bytes failed\n\ + note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n" + ); } diff --git a/tests/ui/async-await/async-closures/ice-async-closure-variance-issue-148488.stderr b/tests/ui/async-await/async-closures/ice-async-closure-variance-issue-148488.stderr index ca98d210579f3..507b1e895898d 100644 --- a/tests/ui/async-await/async-closures/ice-async-closure-variance-issue-148488.stderr +++ b/tests/ui/async-await/async-closures/ice-async-closure-variance-issue-148488.stderr @@ -11,6 +11,12 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures | LL | fn ord() -> _ { | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn ord() -> _ { +LL + fn ord() -> impl AsyncFn() { + | error[E0392]: lifetime parameter `'g` is never used --> $DIR/ice-async-closure-variance-issue-148488.rs:3:10 diff --git a/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.fixed b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.fixed new file mode 100644 index 0000000000000..db5091f6b7f6c --- /dev/null +++ b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.fixed @@ -0,0 +1,64 @@ +#![allow(dead_code)] +//@ run-rustfix +//@ edition: 2021 + +// The suggestion should be `impl AsyncFn()` instead of something like `{async closure@...}` + +fn test1() -> impl AsyncFn() { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() + async || {} +} + +fn test2() -> impl AsyncFn(i32) -> i32 { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn(i32) -> i32 + async |x: i32| x + 1 +} + +fn test3() -> impl AsyncFn(i32, i32) -> i32 { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn(i32, i32) -> i32 + async |x: i32, y: i32| x + y +} + +fn test4() -> impl AsyncFn() { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() + async || -> () { () } +} + +fn test5() -> impl AsyncFn() -> i32 { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() -> i32 + let z = 42; + async move || z +} + +fn test6() -> impl AsyncFnMut() -> i32 { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFnMut() -> i32 + let mut x = 0; + async move || { + x += 1; + x + } +} + +fn test7() -> impl AsyncFnOnce() { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFnOnce() + let s = String::from("hello"); + async move || { + drop(s); + } +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.rs b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.rs new file mode 100644 index 0000000000000..1109904398dab --- /dev/null +++ b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.rs @@ -0,0 +1,64 @@ +#![allow(dead_code)] +//@ run-rustfix +//@ edition: 2021 + +// The suggestion should be `impl AsyncFn()` instead of something like `{async closure@...}` + +fn test1() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() + async || {} +} + +fn test2() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn(i32) -> i32 + async |x: i32| x + 1 +} + +fn test3() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn(i32, i32) -> i32 + async |x: i32, y: i32| x + y +} + +fn test4() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() + async || -> () { () } +} + +fn test5() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFn() -> i32 + let z = 42; + async move || z +} + +fn test6() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFnMut() -> i32 + let mut x = 0; + async move || { + x += 1; + x + } +} + +fn test7() -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + //~| HELP replace with an appropriate return type + //~| SUGGESTION impl AsyncFnOnce() + let s = String::from("hello"); + async move || { + drop(s); + } +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.stderr b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.stderr new file mode 100644 index 0000000000000..abe775cec082b --- /dev/null +++ b/tests/ui/async-await/async-closures/suggest-impl-async-fn-issue-148493.stderr @@ -0,0 +1,87 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:7:15 + | +LL | fn test1() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test1() -> _ { +LL + fn test1() -> impl AsyncFn() { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:14:15 + | +LL | fn test2() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test2() -> _ { +LL + fn test2() -> impl AsyncFn(i32) -> i32 { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:21:15 + | +LL | fn test3() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test3() -> _ { +LL + fn test3() -> impl AsyncFn(i32, i32) -> i32 { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:28:15 + | +LL | fn test4() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test4() -> _ { +LL + fn test4() -> impl AsyncFn() { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:35:15 + | +LL | fn test5() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test5() -> _ { +LL + fn test5() -> impl AsyncFn() -> i32 { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:43:15 + | +LL | fn test6() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test6() -> _ { +LL + fn test6() -> impl AsyncFnMut() -> i32 { + | + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/suggest-impl-async-fn-issue-148493.rs:54:15 + | +LL | fn test7() -> _ { + | ^ not allowed in type signatures + | +help: replace with an appropriate return type + | +LL - fn test7() -> _ { +LL + fn test7() -> impl AsyncFnOnce() { + | + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/macros/macro-hygiene-help-issue-148580.rs b/tests/ui/macros/macro-hygiene-help-issue-148580.rs new file mode 100644 index 0000000000000..8441290b17228 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-148580.rs @@ -0,0 +1,15 @@ +macro_rules! print_it { {} => { println!("{:?}", it); } } +//~^ ERROR cannot find value `it` in this scope + +fn main() { + { + let it = "hello"; + } + { + let it = "world"; + { + let it = (); + print_it!(); + } + } +} diff --git a/tests/ui/macros/macro-hygiene-help-issue-148580.stderr b/tests/ui/macros/macro-hygiene-help-issue-148580.stderr new file mode 100644 index 0000000000000..f6a4ae7dd1c66 --- /dev/null +++ b/tests/ui/macros/macro-hygiene-help-issue-148580.stderr @@ -0,0 +1,19 @@ +error[E0425]: cannot find value `it` in this scope + --> $DIR/macro-hygiene-help-issue-148580.rs:1:50 + | +LL | macro_rules! print_it { {} => { println!("{:?}", it); } } + | ^^ not found in this scope +... +LL | print_it!(); + | ----------- in this macro invocation + | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-help-issue-148580.rs:11:17 + | +LL | let it = (); + | ^^ + = note: this error originates in the macro `print_it` (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 E0425`. diff --git a/tests/ui/macros/macro-hygiene-scope-15167.stderr b/tests/ui/macros/macro-hygiene-scope-15167.stderr index 58112c52df159..332a58467cee1 100644 --- a/tests/ui/macros/macro-hygiene-scope-15167.stderr +++ b/tests/ui/macros/macro-hygiene-scope-15167.stderr @@ -7,6 +7,11 @@ LL | macro_rules! f { () => (n) } LL | println!("{}", f!()); | ---- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-scope-15167.rs:12:9 + | +LL | for n in 0..1 { + | ^ = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `n` in this scope @@ -18,6 +23,11 @@ LL | macro_rules! f { () => (n) } LL | println!("{}", f!()); | ---- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-scope-15167.rs:16:17 + | +LL | if let Some(n) = None { + | ^ = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `n` in this scope @@ -29,6 +39,11 @@ LL | macro_rules! f { () => (n) } LL | println!("{}", f!()); | ---- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-scope-15167.rs:21:24 + | +LL | } else if let Some(n) = None { + | ^ = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `n` in this scope @@ -40,6 +55,11 @@ LL | macro_rules! f { () => (n) } LL | println!("{}", f!()); | ---- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/macro-hygiene-scope-15167.rs:25:20 + | +LL | while let Some(n) = None { + | ^ = note: this error originates in the macro `f` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 4 previous errors diff --git a/tests/ui/macros/metavar-expressions/concat-hygiene.stderr b/tests/ui/macros/metavar-expressions/concat-hygiene.stderr index f3150d385ee70..9520f5182f4a7 100644 --- a/tests/ui/macros/metavar-expressions/concat-hygiene.stderr +++ b/tests/ui/macros/metavar-expressions/concat-hygiene.stderr @@ -7,6 +7,11 @@ LL | ${concat($lhs, $rhs)} LL | let _another = join!(abc, def); | --------------- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/concat-hygiene.rs:11:9 + | +LL | let abcdef = 1; + | ^^^^^^ = note: this error originates in the macro `join` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr index e904b43aaae06..17171ad5c5cc5 100644 --- a/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr +++ b/tests/ui/proc-macro/gen-macro-rules-hygiene.stderr @@ -18,6 +18,11 @@ LL | gen_macro_rules!(); LL | generated!(); | ------------ in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/gen-macro-rules-hygiene.rs:19:13 + | +LL | let local_use = 1; + | ^^^^^^^^^ = note: this error originates in the macro `generated` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope diff --git a/tests/ui/proc-macro/mixed-site-span.stderr b/tests/ui/proc-macro/mixed-site-span.stderr index 2d2d55fe148d5..4bb2508416aaa 100644 --- a/tests/ui/proc-macro/mixed-site-span.stderr +++ b/tests/ui/proc-macro/mixed-site-span.stderr @@ -594,6 +594,11 @@ error[E0425]: cannot find value `local_use` in this scope LL | proc_macro_rules!(); | ^^^^^^^^^^^^^^^^^^^ help: a local variable with a similar name exists: `local_def` | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/mixed-site-span.rs:21:13 + | +LL | let local_use = 1; + | ^^^^^^^^^ = note: this error originates in the macro `proc_macro_rules` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `local_def` in this scope diff --git a/tests/ui/proc-macro/weird-hygiene.stderr b/tests/ui/proc-macro/weird-hygiene.stderr index 0cfac3f89a045..aa3ef9556eb16 100644 --- a/tests/ui/proc-macro/weird-hygiene.stderr +++ b/tests/ui/proc-macro/weird-hygiene.stderr @@ -7,6 +7,11 @@ LL | Value = (stringify!($tokens + hidden_ident), 1).1 LL | other!(50); | ---------- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/weird-hygiene.rs:44:9 + | +LL | let hidden_ident = "Hello1"; + | ^^^^^^^^^^^^ = note: this error originates in the macro `inner` which comes from the expansion of the macro `other` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0425]: cannot find value `hidden_ident` in this scope @@ -18,6 +23,11 @@ LL | hidden_ident LL | invoke_it!(25); | -------------- in this macro invocation | +help: an identifier with the same name exists, but is not accessible due to macro hygiene + --> $DIR/weird-hygiene.rs:44:9 + | +LL | let hidden_ident = "Hello1"; + | ^^^^^^^^^^^^ = note: this error originates in the macro `invoke_it` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors