From c78387192fe6441df078bd00d18d1f8e35161770 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 9 Nov 2025 18:59:28 +1100 Subject: [PATCH 01/21] coverage: Hoist expansion tree creation out of span refinement This is an incremental step towards making the expansion tree central to coverage mapping creation, which will be needed for proper expansion region support. --- .../src/coverage/expansion.rs | 14 +++++++++++--- .../src/coverage/{spans => }/from_mir.rs | 0 .../src/coverage/mappings.rs | 5 ++++- .../rustc_mir_transform/src/coverage/mod.rs | 1 + .../rustc_mir_transform/src/coverage/spans.rs | 17 +++-------------- 5 files changed, 19 insertions(+), 18 deletions(-) rename compiler/rustc_mir_transform/src/coverage/{spans => }/from_mir.rs (100%) diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index 851bbaeed48eb..df27b1ccabe1f 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -1,7 +1,11 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; +use rustc_middle::mir; use rustc_middle::mir::coverage::BasicCoverageBlock; use rustc_span::{ExpnId, ExpnKind, Span}; +use crate::coverage::from_mir; +use crate::coverage::graph::CoverageGraph; + #[derive(Clone, Copy, Debug)] pub(crate) struct SpanWithBcb { pub(crate) span: Span, @@ -92,13 +96,17 @@ impl ExpnNode { } } -/// Given a collection of span/BCB pairs from potentially-different syntax contexts, +/// Extracts raw span/BCB pairs from potentially-different syntax contexts, and /// arranges them into an "expansion tree" based on their expansion call-sites. -pub(crate) fn build_expn_tree(spans: impl IntoIterator) -> ExpnTree { +pub(crate) fn build_expn_tree(mir_body: &mir::Body<'_>, graph: &CoverageGraph) -> ExpnTree { + let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); + let mut nodes = FxIndexMap::default(); let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id); - for span_with_bcb in spans { + for from_mir::RawSpanFromMir { raw_span, bcb } in raw_spans { + let span_with_bcb = SpanWithBcb { span: raw_span, bcb }; + // Create a node for this span's enclosing expansion, and add the span to it. let expn_id = span_with_bcb.span.ctxt().outer_expn(); let node = nodes.entry(expn_id).or_insert_with_key(new_node); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/from_mir.rs similarity index 100% rename from compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs rename to compiler/rustc_mir_transform/src/coverage/from_mir.rs diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index 8dbe564f51749..f632cd02d8326 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -5,6 +5,7 @@ use rustc_middle::mir::coverage::{ use rustc_middle::mir::{self, BasicBlock, StatementKind}; use rustc_middle::ty::TyCtxt; +use crate::coverage::expansion; use crate::coverage::graph::CoverageGraph; use crate::coverage::hir_info::ExtractedHirInfo; use crate::coverage::spans::extract_refined_covspans; @@ -23,10 +24,12 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> ExtractedMappings { + let expn_tree = expansion::build_expn_tree(mir_body, graph); + let mut mappings = vec![]; // Extract ordinary code mappings from MIR statement/terminator spans. - extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut mappings); + extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings); extract_branch_mappings(mir_body, hir_info, graph, &mut mappings); diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 08c7d346009c6..cc9d7c800a96b 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -9,6 +9,7 @@ use crate::coverage::mappings::ExtractedMappings; mod counters; mod expansion; +mod from_mir; mod graph; mod hir_info; mod mappings; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 325935ee84682..ab457401e89d2 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -1,22 +1,19 @@ -use rustc_middle::mir; use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB}; use rustc_middle::ty::TyCtxt; use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span}; use tracing::instrument; -use crate::coverage::expansion::{self, ExpnTree, SpanWithBcb}; +use crate::coverage::expansion::{ExpnTree, SpanWithBcb}; +use crate::coverage::from_mir::Hole; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::hir_info::ExtractedHirInfo; -use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir}; - -mod from_mir; pub(super) fn extract_refined_covspans<'tcx>( tcx: TyCtxt<'tcx>, - mir_body: &mir::Body<'tcx>, hir_info: &ExtractedHirInfo, graph: &CoverageGraph, + expn_tree: &ExpnTree, mappings: &mut Vec, ) { if hir_info.is_async_fn { @@ -32,14 +29,6 @@ pub(super) fn extract_refined_covspans<'tcx>( let &ExtractedHirInfo { body_span, .. } = hir_info; - let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); - // Use the raw spans to build a tree of expansions for this function. - let expn_tree = expansion::build_expn_tree( - raw_spans - .into_iter() - .map(|RawSpanFromMir { raw_span, bcb }| SpanWithBcb { span: raw_span, bcb }), - ); - let mut covspans = vec![]; let mut push_covspan = |covspan: Covspan| { let covspan_span = covspan.span; From 075f9c498490977ef9d0f041677fd286ae76809e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 11 Nov 2025 13:39:19 +1100 Subject: [PATCH 02/21] coverage: Eagerly ensure that span refinement has an expansion tree node This also replaces `push_covspan` with a separate covspan-filtering step, because the relevant code is being reindented anyway. --- .../rustc_mir_transform/src/coverage/spans.rs | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index ab457401e89d2..4e2831b3af3b8 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -29,14 +29,32 @@ pub(super) fn extract_refined_covspans<'tcx>( let &ExtractedHirInfo { body_span, .. } = hir_info; + // If there somehow isn't an expansion tree node corresponding to the + // body span, return now and don't create any mappings. + let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) else { return }; + let mut covspans = vec![]; - let mut push_covspan = |covspan: Covspan| { + + for &SpanWithBcb { span, bcb } in &node.spans { + covspans.push(Covspan { span, bcb }); + } + + // For each expansion with its call-site in the body span, try to + // distill a corresponding covspan. + for &child_expn_id in &node.child_expn_ids { + if let Some(covspan) = single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) + { + covspans.push(covspan); + } + } + + covspans.retain(|covspan: &Covspan| { let covspan_span = covspan.span; // Discard any spans not contained within the function body span. // Also discard any spans that fill the entire body, because they tend // to represent compiler-inserted code, e.g. implicitly returning `()`. if !body_span.contains(covspan_span) || body_span.source_equal(covspan_span) { - return; + return false; } // Each pushed covspan should have the same context as the body span. @@ -46,27 +64,11 @@ pub(super) fn extract_refined_covspans<'tcx>( false, "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" ); - return; - } - - covspans.push(covspan); - }; - - if let Some(node) = expn_tree.get(body_span.ctxt().outer_expn()) { - for &SpanWithBcb { span, bcb } in &node.spans { - push_covspan(Covspan { span, bcb }); + return false; } - // For each expansion with its call-site in the body span, try to - // distill a corresponding covspan. - for &child_expn_id in &node.child_expn_ids { - if let Some(covspan) = - single_covspan_for_child_expn(tcx, graph, &expn_tree, child_expn_id) - { - push_covspan(covspan); - } - } - } + true + }); // Only proceed if we found at least one usable span. if covspans.is_empty() { From 696690ba21b6b4739093bddf811499b8daa845b1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 11 Nov 2025 13:13:33 +1100 Subject: [PATCH 03/21] coverage: Associate hole spans with expansion tree nodes This will make it easier to perform span refinement for child expansions. --- .../src/coverage/expansion.rs | 21 ++++++++++++++- .../src/coverage/from_mir.rs | 16 ----------- .../src/coverage/mappings.rs | 2 +- .../rustc_mir_transform/src/coverage/spans.rs | 27 ++++++++++++------- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/expansion.rs b/compiler/rustc_mir_transform/src/coverage/expansion.rs index df27b1ccabe1f..827df27da52c7 100644 --- a/compiler/rustc_mir_transform/src/coverage/expansion.rs +++ b/compiler/rustc_mir_transform/src/coverage/expansion.rs @@ -5,6 +5,7 @@ use rustc_span::{ExpnId, ExpnKind, Span}; use crate::coverage::from_mir; use crate::coverage::graph::CoverageGraph; +use crate::coverage::hir_info::ExtractedHirInfo; #[derive(Clone, Copy, Debug)] pub(crate) struct SpanWithBcb { @@ -74,6 +75,10 @@ pub(crate) struct ExpnNode { pub(crate) spans: Vec, /// Expansions whose call-site is in this expansion. pub(crate) child_expn_ids: FxIndexSet, + + /// Hole spans belonging to this expansion, to be carved out from the + /// code spans during span refinement. + pub(crate) hole_spans: Vec, } impl ExpnNode { @@ -92,13 +97,19 @@ impl ExpnNode { spans: vec![], child_expn_ids: FxIndexSet::default(), + + hole_spans: vec![], } } } /// Extracts raw span/BCB pairs from potentially-different syntax contexts, and /// arranges them into an "expansion tree" based on their expansion call-sites. -pub(crate) fn build_expn_tree(mir_body: &mir::Body<'_>, graph: &CoverageGraph) -> ExpnTree { +pub(crate) fn build_expn_tree( + mir_body: &mir::Body<'_>, + hir_info: &ExtractedHirInfo, + graph: &CoverageGraph, +) -> ExpnTree { let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph); let mut nodes = FxIndexMap::default(); @@ -131,5 +142,13 @@ pub(crate) fn build_expn_tree(mir_body: &mir::Body<'_>, graph: &CoverageGraph) - } } + // Associate each hole span (extracted from HIR) with its corresponding + // expansion tree node. + for &hole_span in &hir_info.hole_spans { + let expn_id = hole_span.ctxt().outer_expn(); + let Some(node) = nodes.get_mut(&expn_id) else { continue }; + node.hole_spans.push(hole_span); + } + ExpnTree { nodes } } diff --git a/compiler/rustc_mir_transform/src/coverage/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/from_mir.rs index c096f1e2632ce..e1623f590a8eb 100644 --- a/compiler/rustc_mir_transform/src/coverage/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/from_mir.rs @@ -142,19 +142,3 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), } } - -#[derive(Debug)] -pub(crate) struct Hole { - pub(crate) span: Span, -} - -impl Hole { - pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { - if !self.span.overlaps_or_adjacent(other.span) { - return false; - } - - self.span = self.span.to(other.span); - true - } -} diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs index f632cd02d8326..5347e1150e5de 100644 --- a/compiler/rustc_mir_transform/src/coverage/mappings.rs +++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs @@ -24,7 +24,7 @@ pub(crate) fn extract_mappings_from_mir<'tcx>( hir_info: &ExtractedHirInfo, graph: &CoverageGraph, ) -> ExtractedMappings { - let expn_tree = expansion::build_expn_tree(mir_body, graph); + let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph); let mut mappings = vec![]; diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 4e2831b3af3b8..2a06b409e8c2d 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -5,7 +5,6 @@ use rustc_span::{BytePos, DesugaringKind, ExpnId, ExpnKind, MacroKind, Span}; use tracing::instrument; use crate::coverage::expansion::{ExpnTree, SpanWithBcb}; -use crate::coverage::from_mir::Hole; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::hir_info::ExtractedHirInfo; @@ -98,14 +97,8 @@ pub(super) fn extract_refined_covspans<'tcx>( covspans.dedup_by(|b, a| a.span.source_equal(b.span)); // Sort the holes, and merge overlapping/adjacent holes. - let mut holes = hir_info - .hole_spans - .iter() - .copied() - // Discard any holes that aren't directly visible within the body span. - .filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span)) - .map(|span| Hole { span }) - .collect::>(); + let mut holes = node.hole_spans.iter().copied().map(|span| Hole { span }).collect::>(); + holes.sort_by(|a, b| compare_spans(a.span, b.span)); holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); @@ -286,3 +279,19 @@ fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option { }) .ok()? } + +#[derive(Debug)] +struct Hole { + span: Span, +} + +impl Hole { + fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { + if !self.span.overlaps_or_adjacent(other.span) { + return false; + } + + self.span = self.span.to(other.span); + true + } +} From 06e2e7aae1bbf6eda8d8e2a1917396dc3df0f8f8 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Tue, 11 Nov 2025 17:18:28 +0100 Subject: [PATCH 04/21] CStr docs: Fix CStr vs &CStr confusion The CStr docs used to say things about CStr that are only true for &CStr. Also, it's the bytes that are being borrowed, not the reference. One could say that it's the reference that is doing the borrowing, rather than being borrowed. --- library/core/src/ffi/c_str.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 09d9b160700ca..6acabdda69543 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -15,18 +15,18 @@ use crate::{fmt, ops, slice, str}; // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the // links to `CString` and `String` for now until a solution is developed -/// Representation of a borrowed C string. +/// The `&CStr` type represents a borrowed C string. /// -/// This type represents a borrowed reference to a nul-terminated +/// Type `&CStr` represents a reference to a borrowed nul-terminated /// array of bytes. It can be constructed safely from a &[[u8]] /// slice, or unsafely from a raw `*const c_char`. It can be expressed as a /// literal in the form `c"Hello world"`. /// -/// The `CStr` can then be converted to a Rust &[str] by performing +/// The `&CStr` can then be converted to a Rust &[str] by performing /// UTF-8 validation, or into an owned `CString`. /// /// `&CStr` is to `CString` as &[str] is to `String`: the former -/// in each pair are borrowed references; the latter are owned +/// in each pair are borrowing references; the latter are owned /// strings. /// /// Note that this structure does **not** have a guaranteed layout (the `repr(transparent)` From a56fc9c21366b8430a779975638477fdb00b2715 Mon Sep 17 00:00:00 2001 From: Bart Jacobs Date: Tue, 11 Nov 2025 19:56:48 +0100 Subject: [PATCH 05/21] Some tweaks --- library/core/src/ffi/c_str.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 6acabdda69543..9a35ed07b89ac 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -15,9 +15,9 @@ use crate::{fmt, ops, slice, str}; // actually reference libstd or liballoc in intra-doc links. so, the best we can do is remove the // links to `CString` and `String` for now until a solution is developed -/// The `&CStr` type represents a borrowed C string. +/// A dynamically-sized view of a C string. /// -/// Type `&CStr` represents a reference to a borrowed nul-terminated +/// The type `&CStr` represents a reference to a borrowed nul-terminated /// array of bytes. It can be constructed safely from a &[[u8]] /// slice, or unsafely from a raw `*const c_char`. It can be expressed as a /// literal in the form `c"Hello world"`. From 65b5d765bc08141c0a01b29611a8660490ef2d14 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 11 Nov 2025 16:18:50 -0800 Subject: [PATCH 06/21] Implement `Read::read_array` Tracking issue: https://github.com/rust-lang/rust/issues/148848 --- library/std/src/io/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++- library/std/src/lib.rs | 1 + 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 25a4661a0bc9c..499b094735247 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -330,7 +330,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, util::{Empty, Repeat, Sink, empty, repeat, sink}, }; -use crate::mem::take; +use crate::mem::{MaybeUninit, take}; use crate::ops::{Deref, DerefMut}; use crate::{cmp, fmt, slice, str, sys}; @@ -1242,6 +1242,44 @@ pub trait Read { { Take { inner: self, len: limit, limit } } + + /// Read and return a fixed array of bytes from this source. + /// + /// This function uses an array sized based on a const generic size known at compile time. You + /// can specify the size with turbofish (`reader.read_array::<8>()`), or let type inference + /// determine the number of bytes needed based on how the return value gets used. For instance, + /// this function works well with functions like [`u64::from_le_bytes`] to turn an array of + /// bytes into an integer of the same size. + /// + /// Like `read_exact`, if this function encounters an "end of file" before reading the desired + /// number of bytes, it returns an error of the kind [`ErrorKind::UnexpectedEof`]. + /// + /// ``` + /// #![feature(read_array)] + /// use std::io::Cursor; + /// use std::io::prelude::*; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buf = Cursor::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2]); + /// let x = u64::from_le_bytes(buf.read_array()?); + /// let y = u32::from_be_bytes(buf.read_array()?); + /// let z = u16::from_be_bytes(buf.read_array()?); + /// assert_eq!(x, 0x807060504030201); + /// assert_eq!(y, 0x9080706); + /// assert_eq!(z, 0x504); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "read_array", issue = "148848")] + fn read_array(&mut self) -> Result<[u8; N]> + where + Self: Sized, + { + let mut buf = [MaybeUninit::uninit(); N]; + let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); + self.read_buf_exact(borrowed_buf.unfilled())?; + Ok(unsafe { MaybeUninit::array_assume_init(buf) }) + } } /// Reads all bytes from a [reader][Read] into a new [`String`]. diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7b6cfbfe0f259..5374e9e61783f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -348,6 +348,7 @@ #![feature(int_from_ascii)] #![feature(ip)] #![feature(lazy_get)] +#![feature(maybe_uninit_array_assume_init)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] #![feature(panic_can_unwind)] From a0fe930898e21320771c66141b1c771b690088a8 Mon Sep 17 00:00:00 2001 From: edwloef Date: Wed, 12 Nov 2025 15:06:54 +0100 Subject: [PATCH 07/21] Refactor `Box::take` --- library/alloc/src/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index fde6b80a62fe6..2b767ffe02bee 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -725,9 +725,9 @@ impl Box { #[unstable(feature = "box_take", issue = "147212")] pub fn take(boxed: Self) -> (T, Box, A>) { unsafe { - let (raw, alloc) = Box::into_raw_with_allocator(boxed); + let (raw, alloc) = Box::into_non_null_with_allocator(boxed); let value = raw.read(); - let uninit = Box::from_raw_in(raw.cast::>(), alloc); + let uninit = Box::from_non_null_in(raw.cast_uninit(), alloc); (value, uninit) } } From c7e50d0f377b2cbc5a9f5706992121301ce44533 Mon Sep 17 00:00:00 2001 From: Quinn Okabayashi Date: Wed, 12 Nov 2025 15:46:08 +0000 Subject: [PATCH 08/21] Remove unused LLVMModuleRef argument --- compiler/rustc_codegen_llvm/src/back/write.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 2 +- compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index c55fe3fad008a..fde7dd6ef7a85 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -683,7 +683,7 @@ pub(crate) unsafe fn llvm_optimize( // Here we map the old arguments to the new arguments, with an offset of 1 to make sure // that we don't use the newly added `%dyn_ptr`. unsafe { - llvm::LLVMRustOffloadMapper(cx.llmod(), old_fn, new_fn); + llvm::LLVMRustOffloadMapper(old_fn, new_fn); } llvm::set_linkage(new_fn, llvm::get_linkage(old_fn)); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 16549f9aab81a..ca64d96c2a33c 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2025,7 +2025,7 @@ unsafe extern "C" { ) -> &Attribute; // Operations on functions - pub(crate) fn LLVMRustOffloadMapper<'a>(M: &'a Module, Fn: &'a Value, Fn: &'a Value); + pub(crate) fn LLVMRustOffloadMapper<'a>(Fn: &'a Value, Fn: &'a Value); pub(crate) fn LLVMRustGetOrInsertFunction<'a>( M: &'a Module, Name: *const c_char, diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index eab65f92e2935..8823c83922822 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -144,9 +144,7 @@ extern "C" void LLVMRustPrintStatistics(RustStringRef OutBuf) { llvm::PrintStatistics(OS); } -extern "C" void LLVMRustOffloadMapper(LLVMModuleRef M, LLVMValueRef OldFn, - LLVMValueRef NewFn) { - llvm::Module *module = llvm::unwrap(M); +extern "C" void LLVMRustOffloadMapper(LLVMValueRef OldFn, LLVMValueRef NewFn) { llvm::Function *oldFn = llvm::unwrap(OldFn); llvm::Function *newFn = llvm::unwrap(NewFn); From 258a446c898dbcba98b95a0759e019aa366cb8b6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 11 Nov 2025 11:07:01 +1100 Subject: [PATCH 09/21] Simplify `Resolver::resolve_macro_path`. There are only two call sites, and three of the arguments are identical at both call sites. This commit removes those arguments and renames the method accordingly. --- compiler/rustc_resolve/src/ident.rs | 5 +---- compiler/rustc_resolve/src/macros.rs | 16 +++++----------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index f3f0a74d03bc0..8ecae07dea67d 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -458,14 +458,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut result = Err(Determinacy::Determined); for derive in parent_scope.derives { let parent_scope = &ParentScope { derives: &[], ..*parent_scope }; - match this.reborrow().resolve_macro_path( + match this.reborrow().resolve_derive_macro_path( derive, - MacroKind::Derive, parent_scope, - true, force, ignore_import, - None, ) { Ok((Some(ext), _)) => { if ext.helper_attrs.contains(&ident.name) { diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 4a5894c9ffa86..d40752d21c1ef 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -395,14 +395,11 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { for (i, resolution) in entry.resolutions.iter_mut().enumerate() { if resolution.exts.is_none() { resolution.exts = Some( - match self.cm().resolve_macro_path( + match self.cm().resolve_derive_macro_path( &resolution.path, - MacroKind::Derive, &parent_scope, - true, force, None, - None, ) { Ok((Some(ext), _)) => { if !ext.helper_attrs.is_empty() { @@ -706,26 +703,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Ok((ext, res)) } - pub(crate) fn resolve_macro_path<'r>( + pub(crate) fn resolve_derive_macro_path<'r>( self: CmResolver<'r, 'ra, 'tcx>, path: &ast::Path, - kind: MacroKind, parent_scope: &ParentScope<'ra>, - trace: bool, force: bool, ignore_import: Option>, - suggestion_span: Option, ) -> Result<(Option>, Res), Determinacy> { self.resolve_macro_or_delegation_path( path, - kind, + MacroKind::Derive, parent_scope, - trace, + true, force, None, None, ignore_import, - suggestion_span, + None, ) } From 8ece93912c493990e5533eca4c3874db8ece9466 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 11 Nov 2025 11:13:50 +1100 Subject: [PATCH 10/21] Remove `trace` argument from `resolve_macro_or_delegation_path`. It's `true` at all call sites. --- compiler/rustc_resolve/src/macros.rs | 37 +++++++++++----------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d40752d21c1ef..4fed69071e58f 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -561,7 +561,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path, kind, parent_scope, - true, force, deleg_impl, invoc_in_mod_inert_attr.map(|def_id| (def_id, node_id)), @@ -714,7 +713,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { path, MacroKind::Derive, parent_scope, - true, force, None, None, @@ -728,7 +726,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ast_path: &ast::Path, kind: MacroKind, parent_scope: &ParentScope<'ra>, - trace: bool, force: bool, deleg_impl: Option, invoc_in_mod_inert_attr: Option<(LocalDefId, NodeId)>, @@ -767,16 +764,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { PathResult::Module(..) => unreachable!(), }; - if trace { - self.multi_segment_macro_resolutions.borrow_mut(&self).push(( - path, - path_span, - kind, - *parent_scope, - res.ok(), - ns, - )); - } + self.multi_segment_macro_resolutions.borrow_mut(&self).push(( + path, + path_span, + kind, + *parent_scope, + res.ok(), + ns, + )); self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span); res @@ -794,15 +789,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { return Err(Determinacy::Undetermined); } - if trace { - self.single_segment_macro_resolutions.borrow_mut(&self).push(( - path[0].ident, - kind, - *parent_scope, - binding.ok(), - suggestion_span, - )); - } + self.single_segment_macro_resolutions.borrow_mut(&self).push(( + path[0].ident, + kind, + *parent_scope, + binding.ok(), + suggestion_span, + )); let res = binding.map(|binding| binding.res()); self.prohibit_imported_non_macro_attrs(binding.ok(), res.ok(), path_span); From b728064935ac5a6a534ffc1f92ac8f83f134ac4b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 13 Nov 2025 09:10:15 +1100 Subject: [PATCH 11/21] Add a helpful comment to `DeriveResolution::exts`. --- compiler/rustc_expand/src/base.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 810a5a21a055b..946f17943fe3d 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1099,6 +1099,9 @@ pub struct Indeterminate; pub struct DeriveResolution { pub path: ast::Path, pub item: Annotatable, + // FIXME: currently this field is only used in `is_none`/`is_some` conditions. However, the + // `Arc` will be used if the FIXME in `MacroExpander::fully_expand_fragment` + // is completed. pub exts: Option>, pub is_const: bool, } From 199f30844688baf711cddd16edc7e45802893346 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 12 Nov 2025 23:12:37 -0800 Subject: [PATCH 12/21] Guard against incorrect `read_buf_exact` implementations --- library/std/src/io/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 499b094735247..b7756befa11e9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1278,6 +1278,8 @@ pub trait Read { let mut buf = [MaybeUninit::uninit(); N]; let mut borrowed_buf = BorrowedBuf::from(buf.as_mut_slice()); self.read_buf_exact(borrowed_buf.unfilled())?; + // Guard against incorrect `read_buf_exact` implementations. + assert_eq!(borrowed_buf.len(), N); Ok(unsafe { MaybeUninit::array_assume_init(buf) }) } } From a40c3e5b4b63bd7edc9a9f0a3b24bfe9452c5869 Mon Sep 17 00:00:00 2001 From: Sinan Nalkaya Date: Thu, 13 Nov 2025 11:53:58 +0100 Subject: [PATCH 13/21] Disable rustdoc-test-builder test partially for SGX target. --- tests/run-make/rustdoc-test-builder/rmake.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/run-make/rustdoc-test-builder/rmake.rs b/tests/run-make/rustdoc-test-builder/rmake.rs index d10a3c92cae42..17d40c68fd922 100644 --- a/tests/run-make/rustdoc-test-builder/rmake.rs +++ b/tests/run-make/rustdoc-test-builder/rmake.rs @@ -25,7 +25,10 @@ fn main() { // Some targets (for example wasm) cannot execute doctests directly even with a runner, // so only exercise the success path when the target can run on the host. - if target().contains("wasm") || std::env::var_os("REMOTE_TEST_CLIENT").is_some() { + if target().contains("wasm") + || target().contains("sgx") + || std::env::var_os("REMOTE_TEST_CLIENT").is_some() + { return; } From 776405c058851a9d5937c44509174d2f0198b987 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 13 Nov 2025 12:46:34 +0100 Subject: [PATCH 14/21] add missing s390x target feature to std detect test --- library/std/tests/run-time-detect.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/run-time-detect.rs b/library/std/tests/run-time-detect.rs index b2c3d0d3f9f8b..be2980f73268f 100644 --- a/library/std/tests/run-time-detect.rs +++ b/library/std/tests/run-time-detect.rs @@ -16,6 +16,7 @@ all(target_arch = "powerpc64", target_os = "linux"), feature(stdarch_powerpc_feature_detection) )] +#![cfg_attr(all(target_arch = "s390x", target_os = "linux"), feature(s390x_target_feature))] #[test] #[cfg(all(target_arch = "arm", any(target_os = "linux", target_os = "android")))] From 5d70446e5051f098248e253b1abb288857f3f2f3 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 17 Oct 2025 00:42:21 +0800 Subject: [PATCH 15/21] Emit error when using path-segment keyword as cfg pred --- .../rustc_attr_parsing/src/attributes/cfg.rs | 5 +- .../src/attributes/cfg_old.rs | 5 +- compiler/rustc_errors/src/emitter.rs | 22 +- compiler/rustc_interface/src/interface.rs | 70 ++- compiler/rustc_parse/src/parser/mod.rs | 2 +- compiler/rustc_session/src/parse.rs | 12 +- ...e.32bit.stderr => cast_size.r32bit.stderr} | 0 ...e.64bit.stderr => cast_size.r64bit.stderr} | 0 src/tools/clippy/tests/ui/cast_size.rs | 8 +- ...tderr => fn_to_numeric_cast.r32bit.stderr} | 0 ...tderr => fn_to_numeric_cast.r64bit.stderr} | 0 .../clippy/tests/ui/fn_to_numeric_cast.rs | 26 +- ...tderr => large_enum_variant.r32bit.stderr} | 0 ...tderr => large_enum_variant.r64bit.stderr} | 0 .../clippy/tests/ui/large_enum_variant.rs | 10 +- tests/codegen-llvm/cf-protection.rs | 10 +- tests/ui/cfg/cmdline-false.rs | 2 +- ...th-kw-as-cfg-pred-cli-cfg.cfg_crate.stderr | 2 + ...ath-kw-as-cfg-pred-cli-cfg.cfg_priv.stderr | 2 + ...w-as-cfg-pred-cli-cfg.cfg_raw_crate.stderr | 6 + ...cfg-pred-cli-cfg.cfg_raw_self_lower.stderr | 6 + ...cfg-pred-cli-cfg.cfg_raw_self_upper.stderr | 6 + ...w-as-cfg-pred-cli-cfg.cfg_raw_super.stderr | 6 + ...cfg-pred-cli-cfg.cfg_raw_underscore.stderr | 6 + ...-as-cfg-pred-cli-cfg.cfg_self_lower.stderr | 2 + ...-as-cfg-pred-cli-cfg.cfg_self_upper.stderr | 2 + ...h-kw-as-cfg-pred-cli-cfg.cfg_struct.stderr | 2 + ...th-kw-as-cfg-pred-cli-cfg.cfg_super.stderr | 2 + ...-as-cfg-pred-cli-cfg.cfg_underscore.stderr | 2 + tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.rs | 24 + ...-pred-cli-check-cfg.check_cfg_crate.stderr | 5 + ...g-pred-cli-check-cfg.check_cfg_priv.stderr | 5 + ...d-cli-check-cfg.check_cfg_raw_crate.stderr | 9 + ...-check-cfg.check_cfg_raw_self_lower.stderr | 9 + ...-check-cfg.check_cfg_raw_self_upper.stderr | 9 + ...d-cli-check-cfg.check_cfg_raw_super.stderr | 9 + ...-check-cfg.check_cfg_raw_underscore.stderr | 9 + ...-cli-check-cfg.check_cfg_self_lower.stderr | 5 + ...-cli-check-cfg.check_cfg_self_upper.stderr | 5 + ...pred-cli-check-cfg.check_cfg_struct.stderr | 5 + ...-pred-cli-check-cfg.check_cfg_super.stderr | 5 + ...-cli-check-cfg.check_cfg_underscore.stderr | 5 + .../cfg/path-kw-as-cfg-pred-cli-check-cfg.rs | 25 + .../cfg/path-kw-as-cfg-pred-cli-raw-allow.rs | 6 + tests/ui/cfg/path-kw-as-cfg-pred.rs | 129 ++++ tests/ui/cfg/path-kw-as-cfg-pred.stderr | 573 ++++++++++++++++++ tests/ui/cfg/raw-true-false.rs | 6 +- tests/ui/check-cfg/raw-keywords.rs | 2 +- .../cfg-arg-invalid-7.rs | 2 +- .../cfg-arg-invalid-7.stderr | 2 +- ...tderr => ctfe-id-unlimited.return_.stderr} | 12 +- .../explicit-tail-calls/ctfe-id-unlimited.rs | 10 +- 52 files changed, 988 insertions(+), 99 deletions(-) rename src/tools/clippy/tests/ui/{cast_size.32bit.stderr => cast_size.r32bit.stderr} (100%) rename src/tools/clippy/tests/ui/{cast_size.64bit.stderr => cast_size.r64bit.stderr} (100%) rename src/tools/clippy/tests/ui/{fn_to_numeric_cast.32bit.stderr => fn_to_numeric_cast.r32bit.stderr} (100%) rename src/tools/clippy/tests/ui/{fn_to_numeric_cast.64bit.stderr => fn_to_numeric_cast.r64bit.stderr} (100%) rename src/tools/clippy/tests/ui/{large_enum_variant.32bit.stderr => large_enum_variant.r32bit.stderr} (100%) rename src/tools/clippy/tests/ui/{large_enum_variant.64bit.stderr => large_enum_variant.r64bit.stderr} (100%) create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_crate.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_priv.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_crate.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_lower.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_upper.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_super.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_underscore.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_lower.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_upper.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_struct.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_super.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_underscore.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.rs create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_crate.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_priv.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_crate.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_lower.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_upper.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_super.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_underscore.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_lower.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_upper.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_struct.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_super.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_underscore.stderr create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.rs create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred-cli-raw-allow.rs create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred.rs create mode 100644 tests/ui/cfg/path-kw-as-cfg-pred.stderr rename tests/ui/explicit-tail-calls/{ctfe-id-unlimited.return.stderr => ctfe-id-unlimited.return_.stderr} (59%) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 631c2f1be4fd4..9680e5492c561 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -82,7 +82,8 @@ pub fn parse_cfg_entry( } }, a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { - let Some(name) = meta.path().word_sym() else { + let Some(name) = meta.path().word_sym().filter(|s| !s.is_path_segment_keyword()) + else { return Err(cx.expected_identifier(meta.path().span())); }; parse_name_value(name, meta.path().span(), a.name_value(), meta.span(), cx)? @@ -158,7 +159,7 @@ fn parse_cfg_entry_target( }; // Then, parse it as a name-value item - let Some(name) = sub_item.path().word_sym() else { + let Some(name) = sub_item.path().word_sym().filter(|s| !s.is_path_segment_keyword()) else { return Err(cx.expected_identifier(sub_item.path().span())); }; let name = Symbol::intern(&format!("target_{name}")); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs index 3257d898eccbc..70228d1e15101 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_old.rs @@ -220,7 +220,10 @@ pub fn eval_condition( } } } - MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + MetaItemKind::Word | MetaItemKind::NameValue(..) + if cfg.path.segments.len() != 1 + || cfg.path.segments[0].ident.is_path_segment_keyword() => + { dcx.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span }); true } diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index e6fa1fa5ce50a..0ef08c69a998c 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -532,30 +532,24 @@ impl Emitter for HumanEmitter { } } -/// An emitter that does nothing when emitting a non-fatal diagnostic. -/// Fatal diagnostics are forwarded to `fatal_emitter` to avoid silent -/// failures of rustc, as witnessed e.g. in issue #89358. -pub struct FatalOnlyEmitter { - pub fatal_emitter: Box, - pub fatal_note: Option, +/// An emitter that adds a note to each diagnostic. +pub struct EmitterWithNote { + pub emitter: Box, + pub note: String, } -impl Emitter for FatalOnlyEmitter { +impl Emitter for EmitterWithNote { fn source_map(&self) -> Option<&SourceMap> { None } fn emit_diagnostic(&mut self, mut diag: DiagInner, registry: &Registry) { - if diag.level == Level::Fatal { - if let Some(fatal_note) = &self.fatal_note { - diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); - } - self.fatal_emitter.emit_diagnostic(diag, registry); - } + diag.sub(Level::Note, self.note.clone(), MultiSpan::new()); + self.emitter.emit_diagnostic(diag, registry); } fn translator(&self) -> &Translator { - self.fatal_emitter.translator() + self.emitter.translator() } } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index bc4e4127c8eda..af1c35774a67a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -15,6 +15,7 @@ use rustc_middle::ty::CurrentGcx; use rustc_middle::util::Providers; use rustc_parse::lexer::StripTokens; use rustc_parse::new_parser_from_source_str; +use rustc_parse::parser::Recovery; use rustc_parse::parser::attr::AllowLeadingUnsafe; use rustc_query_impl::QueryCtxt; use rustc_query_system::query::print_query_stack; @@ -52,9 +53,9 @@ pub struct Compiler { pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { - let psess = ParseSess::with_fatal_emitter( + let psess = ParseSess::emitter_with_note( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], - format!("this error occurred on the command line: `--cfg={s}`"), + format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -62,36 +63,46 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { ($reason: expr) => { #[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::diagnostic_outside_of_impl)] - dcx.fatal(format!( - concat!("invalid `--cfg` argument: `{}` (", $reason, ")"), - s - )); + dcx.fatal(format!("invalid `--cfg` argument: `{s}` ({})", $reason)); }; } match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) { - Ok(mut parser) => match parser.parse_meta_item(AllowLeadingUnsafe::No) { - Ok(meta_item) if parser.token == token::Eof => { - if meta_item.path.segments.len() != 1 { - error!("argument key must be an identifier"); - } - match &meta_item.kind { - MetaItemKind::List(..) => {} - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - error!("argument value must be a string"); + Ok(mut parser) => { + parser = parser.recovery(Recovery::Forbidden); + match parser.parse_meta_item(AllowLeadingUnsafe::No) { + Ok(meta_item) + if parser.token == token::Eof + && parser.dcx().has_errors().is_none() => + { + if meta_item.path.segments.len() != 1 { + error!("argument key must be an identifier"); } - MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let ident = meta_item.ident().expect("multi-segment cfg key"); - return (ident.name, meta_item.value_str()); + match &meta_item.kind { + MetaItemKind::List(..) => {} + MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { + error!("argument value must be a string"); + } + MetaItemKind::NameValue(..) | MetaItemKind::Word => { + let ident = meta_item.ident().expect("multi-segment cfg key"); + + if ident.is_path_segment_keyword() { + error!( + "malformed `cfg` input, expected a valid identifier" + ); + } + + return (ident.name, meta_item.value_str()); + } } } + Ok(..) => {} + Err(err) => err.cancel(), } - Ok(..) => {} - Err(err) => err.cancel(), - }, + } Err(errs) => errs.into_iter().for_each(|err| err.cancel()), - } + }; // If the user tried to use a key="value" flag, but is missing the quotes, provide // a hint about how to resolve this. @@ -116,9 +127,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() }; for s in specs { - let psess = ParseSess::with_fatal_emitter( + let psess = ParseSess::emitter_with_note( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], - format!("this error occurred on the command line: `--check-cfg={s}`"), + format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -171,7 +182,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch let mut parser = match new_parser_from_source_str(&psess, filename, s.to_string(), StripTokens::Nothing) { - Ok(parser) => parser, + Ok(parser) => parser.recovery(Recovery::Forbidden), Err(errs) => { errs.into_iter().for_each(|err| err.cancel()); expected_error(); @@ -179,7 +190,9 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch }; let meta_item = match parser.parse_meta_item(AllowLeadingUnsafe::No) { - Ok(meta_item) if parser.token == token::Eof => meta_item, + Ok(meta_item) if parser.token == token::Eof && parser.dcx().has_errors().is_none() => { + meta_item + } Ok(..) => expected_error(), Err(err) => { err.cancel(); @@ -209,6 +222,11 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch if values_specified { error!("`cfg()` names cannot be after values"); } + + if ident.is_path_segment_keyword() { + error!("malformed `cfg` input, expected a valid identifier"); + } + names.push(ident); } else if let Some(boolean) = arg.boolean_literal() { if values_specified { diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 203a93b52012b..14a738fb9d247 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -466,7 +466,7 @@ impl<'a> Parser<'a> { // Public for rustfmt usage. pub fn parse_ident(&mut self) -> PResult<'a, Ident> { - self.parse_ident_common(true) + self.parse_ident_common(self.may_recover()) } fn parse_ident_common(&mut self, recover: bool) -> PResult<'a, Ident> { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 9048c51d5b42d..8c70e18fb66dd 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -8,7 +8,7 @@ use rustc_ast::attr::AttrIdGenerator; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{AppendOnlyVec, Lock}; -use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination}; +use rustc_errors::emitter::{EmitterWithNote, HumanEmitter, stderr_destination}; use rustc_errors::translation::Translator; use rustc_errors::{ BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, @@ -315,16 +315,12 @@ impl ParseSess { } } - pub fn with_fatal_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self { + pub fn emitter_with_note(locale_resources: Vec<&'static str>, note: String) -> Self { let translator = Translator::with_fallback_bundle(locale_resources, false); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let fatal_emitter = + let emitter = Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)); - let dcx = DiagCtxt::new(Box::new(FatalOnlyEmitter { - fatal_emitter, - fatal_note: Some(fatal_note), - })) - .disable_warnings(); + let dcx = DiagCtxt::new(Box::new(EmitterWithNote { emitter, note })); ParseSess::with_dcx(dcx, sm) } diff --git a/src/tools/clippy/tests/ui/cast_size.32bit.stderr b/src/tools/clippy/tests/ui/cast_size.r32bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/cast_size.32bit.stderr rename to src/tools/clippy/tests/ui/cast_size.r32bit.stderr diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.r64bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/cast_size.64bit.stderr rename to src/tools/clippy/tests/ui/cast_size.r64bit.stderr diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs index ecc5866941918..cefab253bbeed 100644 --- a/src/tools/clippy/tests/ui/cast_size.rs +++ b/src/tools/clippy/tests/ui/cast_size.rs @@ -1,6 +1,6 @@ -//@revisions: 32bit 64bit -//@[32bit]ignore-bitwidth: 64 -//@[64bit]ignore-bitwidth: 32 +//@revisions: r32bit r64bit +//@[r32bit]ignore-bitwidth: 64 +//@[r64bit]ignore-bitwidth: 32 //@no-rustfix: only some diagnostics have suggestions #![warn( @@ -62,7 +62,7 @@ fn main() { //~^ cast_precision_loss 9_999_999_999_999_999usize as f64; //~^ cast_precision_loss - //~[32bit]^^ ERROR: literal out of range for `usize` + //~[r32bit]^^ ERROR: literal out of range for `usize` // 999_999_999_999_999_999_999_999_999_999u128 as f128; } diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.r32bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/fn_to_numeric_cast.32bit.stderr rename to src/tools/clippy/tests/ui/fn_to_numeric_cast.r32bit.stderr diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr b/src/tools/clippy/tests/ui/fn_to_numeric_cast.r64bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/fn_to_numeric_cast.64bit.stderr rename to src/tools/clippy/tests/ui/fn_to_numeric_cast.r64bit.stderr diff --git a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs index 0a07aeff366eb..3659ca24278f6 100644 --- a/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs +++ b/src/tools/clippy/tests/ui/fn_to_numeric_cast.rs @@ -1,6 +1,6 @@ -//@revisions: 32bit 64bit -//@[32bit]ignore-bitwidth: 64 -//@[64bit]ignore-bitwidth: 32 +//@revisions: r32bit r64bit +//@[r32bit]ignore-bitwidth: 64 +//@[r64bit]ignore-bitwidth: 32 //@no-rustfix #![warn(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] #![allow(function_casts_as_integer)] @@ -15,8 +15,8 @@ fn test_function_to_numeric_cast() { let _ = foo as i16; //~^ fn_to_numeric_cast_with_truncation let _ = foo as i32; - //~[64bit]^ fn_to_numeric_cast_with_truncation - //~[32bit]^^ fn_to_numeric_cast + //~[r64bit]^ fn_to_numeric_cast_with_truncation + //~[r32bit]^^ fn_to_numeric_cast let _ = foo as i64; //~^ fn_to_numeric_cast let _ = foo as i128; @@ -29,8 +29,8 @@ fn test_function_to_numeric_cast() { let _ = foo as u16; //~^ fn_to_numeric_cast_with_truncation let _ = foo as u32; - //~[64bit]^ fn_to_numeric_cast_with_truncation - //~[32bit]^^ fn_to_numeric_cast + //~[r64bit]^ fn_to_numeric_cast_with_truncation + //~[r32bit]^^ fn_to_numeric_cast let _ = foo as u64; //~^ fn_to_numeric_cast let _ = foo as u128; @@ -52,8 +52,8 @@ fn test_function_var_to_numeric_cast() { let _ = abc as i16; //~^ fn_to_numeric_cast_with_truncation let _ = abc as i32; - //~[64bit]^ fn_to_numeric_cast_with_truncation - //~[32bit]^^ fn_to_numeric_cast + //~[r64bit]^ fn_to_numeric_cast_with_truncation + //~[r32bit]^^ fn_to_numeric_cast let _ = abc as i64; //~^ fn_to_numeric_cast let _ = abc as i128; @@ -66,8 +66,8 @@ fn test_function_var_to_numeric_cast() { let _ = abc as u16; //~^ fn_to_numeric_cast_with_truncation let _ = abc as u32; - //~[64bit]^ fn_to_numeric_cast_with_truncation - //~[32bit]^^ fn_to_numeric_cast + //~[r64bit]^ fn_to_numeric_cast_with_truncation + //~[r32bit]^^ fn_to_numeric_cast let _ = abc as u64; //~^ fn_to_numeric_cast let _ = abc as u128; @@ -79,8 +79,8 @@ fn test_function_var_to_numeric_cast() { fn fn_with_fn_args(f: fn(i32) -> i32) -> i32 { f as i32 - //~[64bit]^ fn_to_numeric_cast_with_truncation - //~[32bit]^^ fn_to_numeric_cast + //~[r64bit]^ fn_to_numeric_cast_with_truncation + //~[r32bit]^^ fn_to_numeric_cast } fn main() {} diff --git a/src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr b/src/tools/clippy/tests/ui/large_enum_variant.r32bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/large_enum_variant.32bit.stderr rename to src/tools/clippy/tests/ui/large_enum_variant.r32bit.stderr diff --git a/src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr b/src/tools/clippy/tests/ui/large_enum_variant.r64bit.stderr similarity index 100% rename from src/tools/clippy/tests/ui/large_enum_variant.64bit.stderr rename to src/tools/clippy/tests/ui/large_enum_variant.r64bit.stderr diff --git a/src/tools/clippy/tests/ui/large_enum_variant.rs b/src/tools/clippy/tests/ui/large_enum_variant.rs index 9cf6318ca342b..43c19d6486269 100644 --- a/src/tools/clippy/tests/ui/large_enum_variant.rs +++ b/src/tools/clippy/tests/ui/large_enum_variant.rs @@ -1,8 +1,8 @@ -//@revisions: 32bit 64bit +//@revisions: r32bit r64bit //@aux-build:proc_macros.rs //@no-rustfix -//@[32bit]ignore-bitwidth: 64 -//@[64bit]ignore-bitwidth: 32 +//@[r32bit]ignore-bitwidth: 64 +//@[r64bit]ignore-bitwidth: 32 #![allow(dead_code)] #![allow(unused_variables)] #![warn(clippy::large_enum_variant)] @@ -221,13 +221,13 @@ mod issue11915 { } enum NoWarnings { - //~[64bit]^ large_enum_variant + //~[r64bit]^ large_enum_variant BigBoi(PublishWithBytes), _SmallBoi(u8), } enum MakesClippyAngry { - //~[64bit]^ large_enum_variant + //~[r64bit]^ large_enum_variant BigBoi(PublishWithVec), _SmallBoi(u8), } diff --git a/tests/codegen-llvm/cf-protection.rs b/tests/codegen-llvm/cf-protection.rs index c9e9d3433f1f7..a1c902755afbc 100644 --- a/tests/codegen-llvm/cf-protection.rs +++ b/tests/codegen-llvm/cf-protection.rs @@ -1,12 +1,12 @@ // Test that the correct module flags are emitted with different control-flow protection flags. //@ add-minicore -//@ revisions: undefined none branch return full +//@ revisions: undefined none branch return_ full //@ needs-llvm-components: x86 // [undefined] no extra compile-flags //@ [none] compile-flags: -Z cf-protection=none //@ [branch] compile-flags: -Z cf-protection=branch -//@ [return] compile-flags: -Z cf-protection=return +//@ [return_] compile-flags: -Z cf-protection=return //@ [full] compile-flags: -Z cf-protection=full //@ compile-flags: --target x86_64-unknown-linux-gnu @@ -30,9 +30,9 @@ pub fn test() {} // branch: !"cf-protection-branch", i32 1 // branch-NOT: !"cf-protection-return" -// return-NOT: !"cf-protection-branch" -// return: !"cf-protection-return", i32 1 -// return-NOT: !"cf-protection-branch" +// return_-NOT: !"cf-protection-branch" +// return_: !"cf-protection-return", i32 1 +// return_-NOT: !"cf-protection-branch" // full: !"cf-protection-branch", i32 1 // full: !"cf-protection-return", i32 1 diff --git a/tests/ui/cfg/cmdline-false.rs b/tests/ui/cfg/cmdline-false.rs index d4b7d3bbfdca2..917679e2e424b 100644 --- a/tests/ui/cfg/cmdline-false.rs +++ b/tests/ui/cfg/cmdline-false.rs @@ -1,5 +1,5 @@ /// Test that `--cfg false` doesn't cause `cfg(false)` to evaluate to `true` -//@ compile-flags: --cfg false +//@ compile-flags: --cfg r#false #[cfg(false)] fn foo() {} diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_crate.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_crate.stderr new file mode 100644 index 0000000000000..daaddd3074160 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_crate.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `crate` (malformed `cfg` input, expected a valid identifier) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_priv.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_priv.stderr new file mode 100644 index 0000000000000..5fd341485cd99 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_priv.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `priv` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_crate.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_crate.stderr new file mode 100644 index 0000000000000..a5f8b25fe4272 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_crate.stderr @@ -0,0 +1,6 @@ +error: `crate` cannot be a raw identifier + | + = note: this occurred on the command line: `--cfg=r#crate` + +error: invalid `--cfg` argument: `r#crate` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_lower.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_lower.stderr new file mode 100644 index 0000000000000..25dbaa36dbec0 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_lower.stderr @@ -0,0 +1,6 @@ +error: `self` cannot be a raw identifier + | + = note: this occurred on the command line: `--cfg=r#self` + +error: invalid `--cfg` argument: `r#self` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_upper.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_upper.stderr new file mode 100644 index 0000000000000..04341926143a5 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_self_upper.stderr @@ -0,0 +1,6 @@ +error: `Self` cannot be a raw identifier + | + = note: this occurred on the command line: `--cfg=r#Self` + +error: invalid `--cfg` argument: `r#Self` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_super.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_super.stderr new file mode 100644 index 0000000000000..9c70442394cac --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_super.stderr @@ -0,0 +1,6 @@ +error: `super` cannot be a raw identifier + | + = note: this occurred on the command line: `--cfg=r#super` + +error: invalid `--cfg` argument: `r#super` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_underscore.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_underscore.stderr new file mode 100644 index 0000000000000..144f6a1349896 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_raw_underscore.stderr @@ -0,0 +1,6 @@ +error: `_` cannot be a raw identifier + | + = note: this occurred on the command line: `--cfg=r#_` + +error: invalid `--cfg` argument: `r#_` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_lower.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_lower.stderr new file mode 100644 index 0000000000000..9d7897c48ef46 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_lower.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `self` (malformed `cfg` input, expected a valid identifier) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_upper.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_upper.stderr new file mode 100644 index 0000000000000..16c91198eea79 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_self_upper.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `Self` (malformed `cfg` input, expected a valid identifier) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_struct.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_struct.stderr new file mode 100644 index 0000000000000..5ee3f35ae0cfe --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_struct.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `struct` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_super.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_super.stderr new file mode 100644 index 0000000000000..08513ac466cb7 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_super.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `super` (malformed `cfg` input, expected a valid identifier) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_underscore.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_underscore.stderr new file mode 100644 index 0000000000000..4dfb3f8276469 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.cfg_underscore.stderr @@ -0,0 +1,2 @@ +error: invalid `--cfg` argument: `_` (expected `key` or `key="value"`) + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.rs b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.rs new file mode 100644 index 0000000000000..b50b8216ca47b --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-cfg.rs @@ -0,0 +1,24 @@ +//@ edition: 2024 + +//@ revisions: cfg_crate cfg_super cfg_self_lower cfg_self_upper +//@ revisions: cfg_raw_crate cfg_raw_super cfg_raw_self_lower cfg_raw_self_upper +//@ revisions: cfg_struct cfg_priv cfg_underscore cfg_raw_underscore + +//@ [cfg_crate]compile-flags: --cfg crate +//@ [cfg_super]compile-flags: --cfg super +//@ [cfg_self_lower]compile-flags: --cfg self +//@ [cfg_self_upper]compile-flags: --cfg Self + +//@ [cfg_raw_crate]compile-flags: --cfg r#crate +//@ [cfg_raw_super]compile-flags: --cfg r#super +//@ [cfg_raw_self_lower]compile-flags: --cfg r#self +//@ [cfg_raw_self_upper]compile-flags: --cfg r#Self + +//@ [cfg_struct]compile-flags: --cfg struct +//@ [cfg_priv]compile-flags: --cfg priv +//@ [cfg_underscore]compile-flags: --cfg _ +//@ [cfg_raw_underscore]compile-flags: --cfg r#_ + +fn main() {} + +//~? ERROR invalid `--cfg` argument diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_crate.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_crate.stderr new file mode 100644 index 0000000000000..69f06cf5e216c --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_crate.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(crate)` + | + = note: malformed `cfg` input, expected a valid identifier + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_priv.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_priv.stderr new file mode 100644 index 0000000000000..2165d37500a91 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_priv.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(priv)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_crate.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_crate.stderr new file mode 100644 index 0000000000000..f440c07779da0 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_crate.stderr @@ -0,0 +1,9 @@ +error: `crate` cannot be a raw identifier + | + = note: this occurred on the command line: `--check-cfg=cfg(r#crate)` + +error: invalid `--check-cfg` argument: `cfg(r#crate)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_lower.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_lower.stderr new file mode 100644 index 0000000000000..bccc694084e3b --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_lower.stderr @@ -0,0 +1,9 @@ +error: `self` cannot be a raw identifier + | + = note: this occurred on the command line: `--check-cfg=cfg(r#self)` + +error: invalid `--check-cfg` argument: `cfg(r#self)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_upper.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_upper.stderr new file mode 100644 index 0000000000000..68c149a01fb50 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_self_upper.stderr @@ -0,0 +1,9 @@ +error: `Self` cannot be a raw identifier + | + = note: this occurred on the command line: `--check-cfg=cfg(r#Self)` + +error: invalid `--check-cfg` argument: `cfg(r#Self)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_super.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_super.stderr new file mode 100644 index 0000000000000..b3ef744226207 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_super.stderr @@ -0,0 +1,9 @@ +error: `super` cannot be a raw identifier + | + = note: this occurred on the command line: `--check-cfg=cfg(r#super)` + +error: invalid `--check-cfg` argument: `cfg(r#super)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_underscore.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_underscore.stderr new file mode 100644 index 0000000000000..c05536b4185e1 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_raw_underscore.stderr @@ -0,0 +1,9 @@ +error: `_` cannot be a raw identifier + | + = note: this occurred on the command line: `--check-cfg=cfg(r#_)` + +error: invalid `--check-cfg` argument: `cfg(r#_)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_lower.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_lower.stderr new file mode 100644 index 0000000000000..b109650d116d4 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_lower.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(self)` + | + = note: malformed `cfg` input, expected a valid identifier + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_upper.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_upper.stderr new file mode 100644 index 0000000000000..649cde33e159a --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_self_upper.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(Self)` + | + = note: malformed `cfg` input, expected a valid identifier + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_struct.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_struct.stderr new file mode 100644 index 0000000000000..40942a955bf92 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_struct.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(struct)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_super.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_super.stderr new file mode 100644 index 0000000000000..488d2982e7b80 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_super.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(super)` + | + = note: malformed `cfg` input, expected a valid identifier + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_underscore.stderr b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_underscore.stderr new file mode 100644 index 0000000000000..98a645f039a51 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.check_cfg_underscore.stderr @@ -0,0 +1,5 @@ +error: invalid `--check-cfg` argument: `cfg(_)` + | + = note: expected `cfg(name, values("value1", "value2", ... "valueN"))` + = note: visit for more details + diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.rs b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.rs new file mode 100644 index 0000000000000..57ec3f3d54bd4 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-check-cfg.rs @@ -0,0 +1,25 @@ +//@ edition: 2021 + +//@ revisions: check_cfg_crate check_cfg_super check_cfg_self_lower check_cfg_self_upper +//@ revisions: check_cfg_raw_crate check_cfg_raw_super check_cfg_raw_self_lower +//@ revisions: check_cfg_raw_self_upper +//@ revisions: check_cfg_struct check_cfg_priv check_cfg_underscore check_cfg_raw_underscore + +//@ [check_cfg_crate]compile-flags: --check-cfg 'cfg(crate)' +//@ [check_cfg_super]compile-flags: --check-cfg 'cfg(super)' +//@ [check_cfg_self_lower]compile-flags: --check-cfg 'cfg(self)' +//@ [check_cfg_self_upper]compile-flags: --check-cfg 'cfg(Self)' + +//@ [check_cfg_raw_crate]compile-flags: --check-cfg 'cfg(r#crate)' +//@ [check_cfg_raw_super]compile-flags: --check-cfg 'cfg(r#super)' +//@ [check_cfg_raw_self_lower]compile-flags: --check-cfg 'cfg(r#self)' +//@ [check_cfg_raw_self_upper]compile-flags: --check-cfg 'cfg(r#Self)' + +//@ [check_cfg_struct]compile-flags: --check-cfg 'cfg(struct)' +//@ [check_cfg_priv]compile-flags: --check-cfg 'cfg(priv)' +//@ [check_cfg_underscore]compile-flags: --check-cfg 'cfg(_)' +//@ [check_cfg_raw_underscore]compile-flags: --check-cfg 'cfg(r#_)' + +fn main() {} + +//~? ERROR invalid `--check-cfg` argument diff --git a/tests/ui/cfg/path-kw-as-cfg-pred-cli-raw-allow.rs b/tests/ui/cfg/path-kw-as-cfg-pred-cli-raw-allow.rs new file mode 100644 index 0000000000000..2f603baf28bd3 --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred-cli-raw-allow.rs @@ -0,0 +1,6 @@ +//@ edition: 2024 +//@ check-pass +//@ compile-flags: --cfg r#struct --cfg r#priv +//@ compile-flags: --check-cfg 'cfg(r#struct)' --check-cfg 'cfg(r#priv)' + +fn main() {} diff --git a/tests/ui/cfg/path-kw-as-cfg-pred.rs b/tests/ui/cfg/path-kw-as-cfg-pred.rs new file mode 100644 index 0000000000000..d3b419175163f --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred.rs @@ -0,0 +1,129 @@ +//@ edition: 2024 + +#![allow(unexpected_cfgs)] + +macro_rules! foo { + () => { + #[cfg($crate)] //~ ERROR malformed `cfg` attribute input + mod _cfg_dollar_crate {} + #[cfg_attr($crate, path = "foo")] //~ ERROR malformed `cfg_attr` attribute input + mod _cfg_attr_dollar_crate {} + #[cfg_attr(true, cfg($crate))] //~ ERROR malformed `cfg` attribute input + mod _cfg_attr_true_cfg_crate {} + + cfg!($crate); //~ ERROR malformed `cfg` macro input + }; +} + +#[cfg(crate)] //~ ERROR malformed `cfg` attribute input +mod _cfg_crate {} +#[cfg(super)] //~ ERROR malformed `cfg` attribute input +mod _cfg_super {} +#[cfg(self)] //~ ERROR malformed `cfg` attribute input +mod _cfg_self_lower {} +#[cfg(Self)] //~ ERROR malformed `cfg` attribute input +mod _cfg_self_upper {} +#[cfg_attr(crate, path = "foo")] //~ ERROR malformed `cfg_attr` attribute input +mod _cfg_attr_crate {} +#[cfg_attr(super, path = "foo")] //~ ERROR malformed `cfg_attr` attribute input +mod _cfg_attr_super {} +#[cfg_attr(self, path = "foo")] //~ ERROR malformed `cfg_attr` attribute input +mod _cfg_attr_self_lower {} +#[cfg_attr(Self, path = "foo")] //~ ERROR malformed `cfg_attr` attribute input +mod _cfg_attr_self_upper {} +#[cfg_attr(true, cfg(crate))] //~ ERROR malformed `cfg` attribute input +mod _cfg_attr_true_cfg_crate {} +#[cfg_attr(true, cfg(super))] //~ ERROR malformed `cfg` attribute input +mod _cfg_attr_true_cfg_super {} +#[cfg_attr(true, cfg(self))] //~ ERROR malformed `cfg` attribute input +mod _cfg_attr_true_cfg_self_lower {} +#[cfg_attr(true, cfg(Self))] //~ ERROR malformed `cfg` attribute input +mod _cfg_attr_true_cfg_self_upper {} + +#[cfg(struct)] //~ ERROR expected identifier, found keyword +mod _cfg_struct {} +#[cfg(priv)] //~ ERROR expected identifier, found reserved keyword `priv` +mod _cfg_priv {} +#[cfg(_)] //~ ERROR expected identifier, found reserved identifier `_` +mod _cfg_underscore {} +#[cfg_attr(struct, path = "foo")] //~ ERROR expected identifier, found keyword +mod _cfg_attr_struct {} +#[cfg_attr(priv, path = "foo")] //~ ERROR expected identifier, found reserved keyword `priv` +mod _cfg_attr_priv {} +#[cfg_attr(_, path = "foo")] //~ ERROR expected identifier, found reserved identifier `_` +mod _cfg_attr_underscore {} +#[cfg_attr(true, cfg(struct))] //~ ERROR expected identifier, found keyword +mod _cfg_attr_true_cfg_struct {} +#[cfg_attr(true, cfg(priv))] //~ ERROR expected identifier, found reserved keyword `priv` +mod _cfg_attr_true_cfg_priv {} +#[cfg_attr(true, cfg(_))] //~ ERROR expected identifier, found reserved identifier `_` +mod _cfg_attr_true_cfg_underscore {} + +fn main() { + foo!(); + + cfg!(crate); //~ ERROR malformed `cfg` macro input + cfg!(super); //~ ERROR malformed `cfg` macro input + cfg!(self); //~ ERROR malformed `cfg` macro input + cfg!(Self); //~ ERROR malformed `cfg` macro input + + cfg!(r#crate); //~ ERROR `crate` cannot be a raw identifier + //~^ ERROR malformed `cfg` macro input + cfg!(r#super); //~ ERROR `super` cannot be a raw identifier + //~^ ERROR malformed `cfg` macro input + cfg!(r#self); //~ ERROR `self` cannot be a raw identifier + //~^ ERROR malformed `cfg` macro input + cfg!(r#Self); //~ ERROR `Self` cannot be a raw identifier + //~^ ERROR malformed `cfg` macro input + + cfg!(struct); //~ ERROR expected identifier, found keyword + cfg!(priv); //~ ERROR expected identifier, found reserved keyword `priv` + cfg!(_); //~ ERROR expected identifier, found reserved identifier `_` + + cfg!(r#struct); // Ok + cfg!(r#priv); // Ok + cfg!(r#_); //~ ERROR `_` cannot be a raw identifier +} + +#[cfg(r#crate)] //~ ERROR malformed `cfg` attribute input +//~^ ERROR `crate` cannot be a raw identifier +mod _cfg_r_crate {} +#[cfg(r#super)] //~ ERROR malformed `cfg` attribute input +//~^ ERROR `super` cannot be a raw identifier +mod _cfg_r_super {} +#[cfg(r#self)] //~ ERROR malformed `cfg` attribute input +//~^ ERROR `self` cannot be a raw identifier +mod _cfg_r_self_lower {} +#[cfg(r#Self)] //~ ERROR malformed `cfg` attribute input +//~^ ERROR `Self` cannot be a raw identifier +mod _cfg_r_self_upper {} +#[cfg_attr(r#crate, cfg(r#crate))] //~ ERROR malformed `cfg_attr` attribute input +//~^ ERROR `crate` cannot be a raw identifier +//~^^ ERROR `crate` cannot be a raw identifier +mod _cfg_attr_r_crate {} +#[cfg_attr(r#super, cfg(r#super))] //~ ERROR malformed `cfg_attr` attribute input +//~^ ERROR `super` cannot be a raw identifier +//~^^ ERROR `super` cannot be a raw identifier +mod _cfg_attr_r_super {} +#[cfg_attr(r#self, cfg(r#self))] //~ ERROR malformed `cfg_attr` attribute input +//~^ ERROR `self` cannot be a raw identifier +//~^^ ERROR `self` cannot be a raw identifier +mod _cfg_attr_r_self_lower {} +#[cfg_attr(r#Self, cfg(r#Self))] //~ ERROR malformed `cfg_attr` attribute input +//~^ ERROR `Self` cannot be a raw identifier +//~^^ ERROR `Self` cannot be a raw identifier +mod _cfg_attr_r_self_upper {} + +#[cfg(r#struct)] // Ok +mod _cfg_r_struct {} +#[cfg(r#priv)] // Ok +mod _cfg_r_priv {} +#[cfg(r#_)] //~ ERROR `_` cannot be a raw identifier +mod _cfg_r_underscore {} +#[cfg_attr(r#struct, cfg(r#struct))] // Ok +mod _cfg_attr_r_struct {} +#[cfg_attr(r#priv, cfg(r#priv))] // Ok +mod _cfg_attr_r_priv {} +#[cfg_attr(r#_, cfg(r#_))] //~ ERROR `_` cannot be a raw identifier +//~^ ERROR `_` cannot be a raw identifier +mod _cfg_attr_r_underscore {} diff --git a/tests/ui/cfg/path-kw-as-cfg-pred.stderr b/tests/ui/cfg/path-kw-as-cfg-pred.stderr new file mode 100644 index 0000000000000..7f3628befda2e --- /dev/null +++ b/tests/ui/cfg/path-kw-as-cfg-pred.stderr @@ -0,0 +1,573 @@ +error: `crate` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:70:10 + | +LL | cfg!(r#crate); + | ^^^^^^^ + +error: `super` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:72:10 + | +LL | cfg!(r#super); + | ^^^^^^^ + +error: `self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:74:10 + | +LL | cfg!(r#self); + | ^^^^^^ + +error: `Self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:76:10 + | +LL | cfg!(r#Self); + | ^^^^^^ + +error: `_` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:85:10 + | +LL | cfg!(r#_); + | ^^^ + +error: `crate` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:88:7 + | +LL | #[cfg(r#crate)] + | ^^^^^^^ + +error: `super` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:91:7 + | +LL | #[cfg(r#super)] + | ^^^^^^^ + +error: `self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:94:7 + | +LL | #[cfg(r#self)] + | ^^^^^^ + +error: `Self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:97:7 + | +LL | #[cfg(r#Self)] + | ^^^^^^ + +error: `crate` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:100:12 + | +LL | #[cfg_attr(r#crate, cfg(r#crate))] + | ^^^^^^^ + +error: `crate` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:100:25 + | +LL | #[cfg_attr(r#crate, cfg(r#crate))] + | ^^^^^^^ + +error: `super` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:104:12 + | +LL | #[cfg_attr(r#super, cfg(r#super))] + | ^^^^^^^ + +error: `super` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:104:25 + | +LL | #[cfg_attr(r#super, cfg(r#super))] + | ^^^^^^^ + +error: `self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:108:12 + | +LL | #[cfg_attr(r#self, cfg(r#self))] + | ^^^^^^ + +error: `self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:108:24 + | +LL | #[cfg_attr(r#self, cfg(r#self))] + | ^^^^^^ + +error: `Self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:112:12 + | +LL | #[cfg_attr(r#Self, cfg(r#Self))] + | ^^^^^^ + +error: `Self` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:112:24 + | +LL | #[cfg_attr(r#Self, cfg(r#Self))] + | ^^^^^^ + +error: `_` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:121:7 + | +LL | #[cfg(r#_)] + | ^^^ + +error: `_` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:127:12 + | +LL | #[cfg_attr(r#_, cfg(r#_))] + | ^^^ + +error: `_` cannot be a raw identifier + --> $DIR/path-kw-as-cfg-pred.rs:127:21 + | +LL | #[cfg_attr(r#_, cfg(r#_))] + | ^^^ + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:18:1 + | +LL | #[cfg(crate)] + | ^^^^^^-----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:20:1 + | +LL | #[cfg(super)] + | ^^^^^^-----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:22:1 + | +LL | #[cfg(self)] + | ^^^^^^----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:24:1 + | +LL | #[cfg(Self)] + | ^^^^^^----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:26:1 + | +LL | #[cfg_attr(crate, path = "foo")] + | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:28:1 + | +LL | #[cfg_attr(super, path = "foo")] + | ^^^^^^^^^^^-----^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:30:1 + | +LL | #[cfg_attr(self, path = "foo")] + | ^^^^^^^^^^^----^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:32:1 + | +LL | #[cfg_attr(Self, path = "foo")] + | ^^^^^^^^^^^----^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:34:18 + | +LL | #[cfg_attr(true, cfg(crate))] + | ^^^^-----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:36:18 + | +LL | #[cfg_attr(true, cfg(super))] + | ^^^^-----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:38:18 + | +LL | #[cfg_attr(true, cfg(self))] + | ^^^^----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:40:18 + | +LL | #[cfg_attr(true, cfg(Self))] + | ^^^^----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error: expected identifier, found keyword `struct` + --> $DIR/path-kw-as-cfg-pred.rs:43:7 + | +LL | #[cfg(struct)] + | ^^^^^^ expected identifier, found keyword + +error: expected identifier, found reserved keyword `priv` + --> $DIR/path-kw-as-cfg-pred.rs:45:7 + | +LL | #[cfg(priv)] + | ^^^^ expected identifier, found reserved keyword + +error: expected identifier, found reserved identifier `_` + --> $DIR/path-kw-as-cfg-pred.rs:47:7 + | +LL | #[cfg(_)] + | ^ expected identifier, found reserved identifier + +error: expected identifier, found keyword `struct` + --> $DIR/path-kw-as-cfg-pred.rs:49:12 + | +LL | #[cfg_attr(struct, path = "foo")] + | ^^^^^^ expected identifier, found keyword + | +help: escape `struct` to use it as an identifier + | +LL | #[cfg_attr(r#struct, path = "foo")] + | ++ + +error: expected identifier, found reserved keyword `priv` + --> $DIR/path-kw-as-cfg-pred.rs:51:12 + | +LL | #[cfg_attr(priv, path = "foo")] + | ^^^^ expected identifier, found reserved keyword + | +help: escape `priv` to use it as an identifier + | +LL | #[cfg_attr(r#priv, path = "foo")] + | ++ + +error: expected identifier, found reserved identifier `_` + --> $DIR/path-kw-as-cfg-pred.rs:53:12 + | +LL | #[cfg_attr(_, path = "foo")] + | ^ expected identifier, found reserved identifier + +error: expected identifier, found keyword `struct` + --> $DIR/path-kw-as-cfg-pred.rs:55:22 + | +LL | #[cfg_attr(true, cfg(struct))] + | ^^^^^^ expected identifier, found keyword + +error: expected identifier, found reserved keyword `priv` + --> $DIR/path-kw-as-cfg-pred.rs:57:22 + | +LL | #[cfg_attr(true, cfg(priv))] + | ^^^^ expected identifier, found reserved keyword + +error: expected identifier, found reserved identifier `_` + --> $DIR/path-kw-as-cfg-pred.rs:59:22 + | +LL | #[cfg_attr(true, cfg(_))] + | ^ expected identifier, found reserved identifier + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:88:1 + | +LL | #[cfg(r#crate)] + | ^^^^^^-------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:91:1 + | +LL | #[cfg(r#super)] + | ^^^^^^-------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:94:1 + | +LL | #[cfg(r#self)] + | ^^^^^^------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:97:1 + | +LL | #[cfg(r#Self)] + | ^^^^^^------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:100:1 + | +LL | #[cfg_attr(r#crate, cfg(r#crate))] + | ^^^^^^^^^^^-------^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:104:1 + | +LL | #[cfg_attr(r#super, cfg(r#super))] + | ^^^^^^^^^^^-------^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:108:1 + | +LL | #[cfg_attr(r#self, cfg(r#self))] + | ^^^^^^^^^^^------^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:112:1 + | +LL | #[cfg_attr(r#Self, cfg(r#Self))] + | ^^^^^^^^^^^------^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:7:9 + | +LL | #[cfg($crate)] + | ^^^^^^------^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg(predicate)]` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0539]: malformed `cfg_attr` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:9:9 + | +LL | #[cfg_attr($crate, path = "foo")] + | ^^^^^^^^^^^------^^^^^^^^^^^^^^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0539]: malformed `cfg` attribute input + --> $DIR/path-kw-as-cfg-pred.rs:11:26 + | +LL | #[cfg_attr(true, cfg($crate))] + | ^^^^------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:14:9 + | +LL | cfg!($crate); + | ^^^^^------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `cfg` which comes from the expansion of the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:65:5 + | +LL | cfg!(crate); + | ^^^^^-----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:66:5 + | +LL | cfg!(super); + | ^^^^^-----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:67:5 + | +LL | cfg!(self); + | ^^^^^----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:68:5 + | +LL | cfg!(Self); + | ^^^^^----^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:70:5 + | +LL | cfg!(r#crate); + | ^^^^^-------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:72:5 + | +LL | cfg!(r#super); + | ^^^^^-------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:74:5 + | +LL | cfg!(r#self); + | ^^^^^------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` macro input + --> $DIR/path-kw-as-cfg-pred.rs:76:5 + | +LL | cfg!(r#Self); + | ^^^^^------^ + | | | + | | expected a valid identifier here + | help: must be of the form: `cfg(predicate)` + | + = note: for more information, visit + +error: expected identifier, found keyword `struct` + --> $DIR/path-kw-as-cfg-pred.rs:79:10 + | +LL | cfg!(struct); + | ^^^^^^ expected identifier, found keyword + +error: expected identifier, found reserved keyword `priv` + --> $DIR/path-kw-as-cfg-pred.rs:80:10 + | +LL | cfg!(priv); + | ^^^^ expected identifier, found reserved keyword + +error: expected identifier, found reserved identifier `_` + --> $DIR/path-kw-as-cfg-pred.rs:81:10 + | +LL | cfg!(_); + | ^ expected identifier, found reserved identifier + +error: aborting due to 64 previous errors + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/cfg/raw-true-false.rs b/tests/ui/cfg/raw-true-false.rs index c92672fc144e3..9f4c6c1fcd42f 100644 --- a/tests/ui/cfg/raw-true-false.rs +++ b/tests/ui/cfg/raw-true-false.rs @@ -1,9 +1,5 @@ //@ check-pass -//@ revisions: r0x0 r0x1 r1x0 r1x1 -//@[r0x0] compile-flags: --cfg false --check-cfg=cfg(false) -//@[r0x1] compile-flags: --cfg false --check-cfg=cfg(r#false) -//@[r1x0] compile-flags: --cfg r#false --check-cfg=cfg(false) -//@[r1x1] compile-flags: --cfg r#false --check-cfg=cfg(r#false) +//@ compile-flags: --cfg r#false --check-cfg=cfg(r#false) #![deny(unexpected_cfgs)] fn main() { #[cfg(not(r#false))] diff --git a/tests/ui/check-cfg/raw-keywords.rs b/tests/ui/check-cfg/raw-keywords.rs index b82eb5a64e9a1..07daf564f6d8d 100644 --- a/tests/ui/check-cfg/raw-keywords.rs +++ b/tests/ui/check-cfg/raw-keywords.rs @@ -3,7 +3,7 @@ // //@ check-pass //@ no-auto-check-cfg -//@ compile-flags: --cfg=true --cfg=async --check-cfg=cfg(r#true,r#async,edition2015,edition2021) +//@ compile-flags: --cfg=r#true --cfg=r#async --check-cfg=cfg(r#true,r#async,edition2015,edition2021) // //@ revisions: edition2015 edition2021 //@ [edition2015] edition: 2015 diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs b/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs index f05adc7bf7aad..d90075da3e63c 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-7.rs @@ -3,4 +3,4 @@ //@ compile-flags: --cfg a" //~? RAW unterminated double quote string -//~? RAW this error occurred on the command line +//~? RAW this occurred on the command line diff --git a/tests/ui/conditional-compilation/cfg-arg-invalid-7.stderr b/tests/ui/conditional-compilation/cfg-arg-invalid-7.stderr index 919709c847019..833d24a907dc2 100644 --- a/tests/ui/conditional-compilation/cfg-arg-invalid-7.stderr +++ b/tests/ui/conditional-compilation/cfg-arg-invalid-7.stderr @@ -1,4 +1,4 @@ error[E0765]: unterminated double quote string | - = note: this error occurred on the command line: `--cfg=a"` + = note: this occurred on the command line: `--cfg=a"` diff --git a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return_.stderr similarity index 59% rename from tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr rename to tests/ui/explicit-tail-calls/ctfe-id-unlimited.return_.stderr index 25e30397c8322..5102f278f0273 100644 --- a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr +++ b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return_.stderr @@ -10,15 +10,15 @@ note: inside `rec_id` LL | inner(0, n) | ^^^^^^^^^^^ note: [... 125 additional calls inside `inner` ...] - --> $DIR/ctfe-id-unlimited.rs:17:42 + --> $DIR/ctfe-id-unlimited.rs:17:41 | -LL | #[cfg(r#return)] _ => return inner(acc + 1, n - 1), - | ^^^^^^^^^^^^^^^^^^^^^ +LL | #[cfg(return_)] _ => return inner(acc + 1, n - 1), + | ^^^^^^^^^^^^^^^^^^^^^ note: inside `inner` - --> $DIR/ctfe-id-unlimited.rs:17:42 + --> $DIR/ctfe-id-unlimited.rs:17:41 | -LL | #[cfg(r#return)] _ => return inner(acc + 1, n - 1), - | ^^^^^^^^^^^^^^^^^^^^^ the failure occurred here +LL | #[cfg(return_)] _ => return inner(acc + 1, n - 1), + | ^^^^^^^^^^^^^^^^^^^^^ the failure occurred here error: aborting due to 1 previous error diff --git a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs index 53cccb38e2b88..0efd8967f8bd9 100644 --- a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs +++ b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs @@ -1,5 +1,5 @@ -//@ revisions: become return -//@ [become] run-pass +//@ revisions: become_ return_ +//@ [become_] run-pass #![expect(incomplete_features)] #![feature(explicit_tail_calls)] @@ -13,8 +13,8 @@ const fn rec_id(n: u32) -> u32 { const fn inner(acc: u32, n: u32) -> u32 { match n { 0 => acc, - #[cfg(r#become)] _ => become inner(acc + 1, n - 1), - #[cfg(r#return)] _ => return inner(acc + 1, n - 1), + #[cfg(become_)] _ => become inner(acc + 1, n - 1), + #[cfg(return_)] _ => return inner(acc + 1, n - 1), } } @@ -25,7 +25,7 @@ const fn rec_id(n: u32) -> u32 { const ORIGINAL: u32 = 12345; // Original number, but with identity function applied // (this is the same, but requires execution of the recursion) -const ID_ED: u32 = rec_id(ORIGINAL); //[return]~ ERROR: reached the configured maximum number of stack frames +const ID_ED: u32 = rec_id(ORIGINAL); //[return_]~ ERROR: reached the configured maximum number of stack frames // Assert to make absolutely sure the computation actually happens const ASSERT: () = assert!(ORIGINAL == ID_ED); From abaccaed216a9aaeb58cd4d5bd2b8d38e79dd8c6 Mon Sep 17 00:00:00 2001 From: Waffle Lapkin Date: Thu, 13 Nov 2025 14:35:06 +0100 Subject: [PATCH 16/21] waffle: stop watching codegen ssa --- triagebot.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index adc73c42c428c..0bf310e8fd7ef 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -922,9 +922,6 @@ cc = ["@davidtwco", "@wesleywiser"] [mentions."compiler/rustc_codegen_cranelift"] cc = ["@bjorn3"] -[mentions."compiler/rustc_codegen_ssa"] -cc = ["@WaffleLapkin"] - [mentions."compiler/rustc_codegen_gcc"] cc = ["@antoyo", "@GuillaumeGomez"] From 78beefed8483cc7b33cf678b6408927dcf5ffb8b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Wed, 12 Nov 2025 20:04:38 +0100 Subject: [PATCH 17/21] error when ABI does not support guaranteed tail calls --- compiler/rustc_abi/src/extern_abi.rs | 45 +++++++++++++++++++ .../rustc_mir_build/src/check_tail_calls.rs | 14 ++++++ .../unsupported-abi/cmse-nonsecure-call.rs | 38 ++++++++++++++++ .../cmse-nonsecure-call.stderr | 34 ++++++++++++++ .../unsupported-abi/cmse-nonsecure-entry.rs | 22 +++++++++ .../cmse-nonsecure-entry.stderr | 10 +++++ 6 files changed, 163 insertions(+) create mode 100644 tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.rs create mode 100644 tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.stderr create mode 100644 tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.rs create mode 100644 tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.stderr diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs index e3b2b1eff72d4..0625759359ea3 100644 --- a/compiler/rustc_abi/src/extern_abi.rs +++ b/compiler/rustc_abi/src/extern_abi.rs @@ -276,6 +276,51 @@ impl ExternAbi { _ => CVariadicStatus::NotSupported, } } + + /// Returns whether the ABI supports guaranteed tail calls. + #[cfg(feature = "nightly")] + pub fn supports_guaranteed_tail_call(self) -> bool { + match self { + Self::CmseNonSecureCall | Self::CmseNonSecureEntry => { + // See https://godbolt.org/z/9jhdeqErv. The CMSE calling conventions clear registers + // before returning, and hence cannot guarantee a tail call. + false + } + Self::AvrInterrupt + | Self::AvrNonBlockingInterrupt + | Self::Msp430Interrupt + | Self::RiscvInterruptM + | Self::RiscvInterruptS + | Self::X86Interrupt => { + // See https://godbolt.org/z/Edfjnxxcq. Interrupts cannot be called directly. + false + } + Self::GpuKernel | Self::PtxKernel => { + // See https://godbolt.org/z/jq5TE5jK1. + false + } + Self::Custom => { + // This ABI does not support calls at all (except via assembly). + false + } + Self::C { .. } + | Self::System { .. } + | Self::Rust + | Self::RustCall + | Self::RustCold + | Self::RustInvalid + | Self::Unadjusted + | Self::EfiApi + | Self::Aapcs { .. } + | Self::Cdecl { .. } + | Self::Stdcall { .. } + | Self::Fastcall { .. } + | Self::Thiscall { .. } + | Self::Vectorcall { .. } + | Self::SysV64 { .. } + | Self::Win64 { .. } => true, + } + } } pub fn all_names() -> Vec<&'static str> { diff --git a/compiler/rustc_mir_build/src/check_tail_calls.rs b/compiler/rustc_mir_build/src/check_tail_calls.rs index 9115c17f37525..b8547e288027a 100644 --- a/compiler/rustc_mir_build/src/check_tail_calls.rs +++ b/compiler/rustc_mir_build/src/check_tail_calls.rs @@ -135,6 +135,10 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { self.report_abi_mismatch(expr.span, caller_sig.abi, callee_sig.abi); } + if !callee_sig.abi.supports_guaranteed_tail_call() { + self.report_unsupported_abi(expr.span, callee_sig.abi); + } + // FIXME(explicit_tail_calls): this currently fails for cases where opaques are used. // e.g. // ``` @@ -358,6 +362,16 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> { self.found_errors = Err(err); } + fn report_unsupported_abi(&mut self, sp: Span, callee_abi: ExternAbi) { + let err = self + .tcx + .dcx() + .struct_span_err(sp, "ABI does not support guaranteed tail calls") + .with_note(format!("`become` is not supported for `extern {callee_abi}` functions")) + .emit(); + self.found_errors = Err(err); + } + fn report_signature_mismatch( &mut self, sp: Span, diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.rs b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.rs new file mode 100644 index 0000000000000..028716a14c7b4 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.rs @@ -0,0 +1,38 @@ +//@ add-minicore +//@ ignore-backends: gcc +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![expect(incomplete_features)] +#![feature(no_core, explicit_tail_calls, abi_cmse_nonsecure_call)] +#![no_core] + +extern crate minicore; +use minicore::*; + +unsafe extern "C" { + safe fn magic() -> extern "cmse-nonsecure-call" fn(u32, u32) -> u32; +} + +// The `cmse-nonsecure-call` ABI can only occur on function pointers: +// +// - a `cmse-nonsecure-call` definition throws an error +// - a `cmse-nonsecure-call` become in a definition with any other ABI is an ABI mismatch +#[no_mangle] +extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 { + //~^ ERROR the `"cmse-nonsecure-call"` ABI is only allowed on function pointers + unsafe { + let f = magic(); + become f(1, 2) + //~^ ERROR ABI does not support guaranteed tail calls + } +} + +#[no_mangle] +extern "C" fn become_nonsecure_call_2(x: u32, y: u32) -> u32 { + unsafe { + let f = magic(); + become f(1, 2) + //~^ ERROR mismatched function ABIs + //~| ERROR ABI does not support guaranteed tail calls + } +} diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.stderr b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.stderr new file mode 100644 index 0000000000000..08f11a4bd4b71 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-call.stderr @@ -0,0 +1,34 @@ +error[E0781]: the `"cmse-nonsecure-call"` ABI is only allowed on function pointers + --> $DIR/cmse-nonsecure-call.rs:21:1 + | +LL | extern "cmse-nonsecure-call" fn become_nonsecure_call_1(x: u32, y: u32) -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: ABI does not support guaranteed tail calls + --> $DIR/cmse-nonsecure-call.rs:25:9 + | +LL | become f(1, 2) + | ^^^^^^^^^^^^^^ + | + = note: `become` is not supported for `extern "cmse-nonsecure-call"` functions + +error: mismatched function ABIs + --> $DIR/cmse-nonsecure-call.rs:34:9 + | +LL | become f(1, 2) + | ^^^^^^^^^^^^^^ + | + = note: `become` requires caller and callee to have the same ABI + = note: caller ABI is `"C"`, while callee ABI is `"cmse-nonsecure-call"` + +error: ABI does not support guaranteed tail calls + --> $DIR/cmse-nonsecure-call.rs:34:9 + | +LL | become f(1, 2) + | ^^^^^^^^^^^^^^ + | + = note: `become` is not supported for `extern "cmse-nonsecure-call"` functions + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0781`. diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.rs b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.rs new file mode 100644 index 0000000000000..006fa538c4891 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.rs @@ -0,0 +1,22 @@ +//@ add-minicore +//@ ignore-backends: gcc +//@ compile-flags: --target thumbv8m.main-none-eabi --crate-type lib +//@ needs-llvm-components: arm +#![expect(incomplete_features)] +#![feature(no_core, explicit_tail_calls, cmse_nonsecure_entry)] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[inline(never)] +extern "cmse-nonsecure-entry" fn entry(c: bool, x: u32, y: u32) -> u32 { + if c { x } else { y } +} + +// A `cmse-nonsecure-entry` clears registers before returning, so a tail call cannot be guaranteed. +#[unsafe(no_mangle)] +extern "cmse-nonsecure-entry" fn become_nonsecure_entry(c: bool, x: u32, y: u32) -> u32 { + become entry(c, x, y) + //~^ ERROR ABI does not support guaranteed tail calls +} diff --git a/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.stderr b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.stderr new file mode 100644 index 0000000000000..3acbe8c5bfaa5 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsupported-abi/cmse-nonsecure-entry.stderr @@ -0,0 +1,10 @@ +error: ABI does not support guaranteed tail calls + --> $DIR/cmse-nonsecure-entry.rs:20:5 + | +LL | become entry(c, x, y) + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `become` is not supported for `extern "cmse-nonsecure-entry"` functions + +error: aborting due to 1 previous error + From 101ef2bf81ef8715fa123bdde4d139f158efe578 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Nov 2025 17:50:21 +0100 Subject: [PATCH 18/21] Correctly link to associated trait items in reexports --- src/librustdoc/html/format.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 4843c20c758e6..b7f6d84ea36c2 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -838,9 +838,23 @@ fn print_higher_ranked_params_with_space( pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl Display { fmt::from_fn(move |f| { if let Ok(HrefInfo { url, kind, rust_path }) = href(did, cx) { + let tcx = cx.tcx(); + let def_kind = tcx.def_kind(did); + let anchor = if matches!( + def_kind, + DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant + ) { + let parent_def_id = tcx.parent(did); + let item_type = + ItemType::from_def_kind(def_kind, Some(tcx.def_kind(parent_def_id))); + format!("#{}.{}", item_type.as_str(), tcx.item_name(did)) + } else { + String::new() + }; + write!( f, - r#"{text}"#, + r#"{text}"#, path = join_path_syms(rust_path), text = EscapeBodyText(text.as_str()), ) From 044245c6c8891ae480a4f30bd0a9331966987cf2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Nov 2025 17:51:27 +0100 Subject: [PATCH 19/21] Add regression test for #148008 --- .../import_trait_associated_functions.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/rustdoc/import_trait_associated_functions.rs diff --git a/tests/rustdoc/import_trait_associated_functions.rs b/tests/rustdoc/import_trait_associated_functions.rs new file mode 100644 index 0000000000000..84f6f8009701d --- /dev/null +++ b/tests/rustdoc/import_trait_associated_functions.rs @@ -0,0 +1,19 @@ +// This test ensures that reexports of associated items links to the associated items. +// Regression test for . + +#![feature(import_trait_associated_functions)] + +#![crate_name = "foo"] + +//@ has 'foo/index.html' + +pub trait Test { + fn method(); + const CONST: u8; + type Type; +} + +//@ has - '//*[@id="reexport.method"]//a[@href="trait.Test.html#tymethod.method"]' 'method' +//@ has - '//*[@id="reexport.CONST"]//a[@href="trait.Test.html#associatedconstant.CONST"]' 'CONST' +//@ has - '//*[@id="reexport.Type"]//a[@href="trait.Test.html#associatedtype.Type"]' 'Type' +pub use self::Test::{method, CONST, Type}; From ad1789a5f0a09f4d1c5a29cc6df9960bce97797a Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 13 Nov 2025 15:57:51 +0100 Subject: [PATCH 20/21] Expose fmt::Arguments::from_str as unstable. --- library/alloc/src/lib.rs | 1 + library/core/src/fmt/mod.rs | 22 ++++++++++++++-------- library/core/src/macros/mod.rs | 6 +++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 666ae27fb8634..dc58b30afbce3 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -116,6 +116,7 @@ #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extend_one_unchecked)] +#![feature(fmt_arguments_from_str)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(formatting_options)] diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index e00e48bcfeb70..7ca33d363cd8f 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -734,7 +734,21 @@ impl<'a> Arguments<'a> { unsafe { Arguments { template: mem::transmute(template), args: mem::transmute(args) } } } + // Same as `from_str`, but not const. + // Used by format_args!() expansion when arguments are inlined, + // e.g. format_args!("{}", 123), which is not allowed in const. #[inline] + pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { + Arguments::from_str(s) + } +} + +impl<'a> Arguments<'a> { + /// Create a `fmt::Arguments` object for a single static string. + /// + /// Formatting this `fmt::Arguments` will just produce the string as-is. + #[inline] + #[unstable(feature = "fmt_arguments_from_str", issue = "148905")] pub const fn from_str(s: &'static str) -> Arguments<'a> { // SAFETY: This is the "static str" representation of fmt::Arguments; see above. unsafe { @@ -744,14 +758,6 @@ impl<'a> Arguments<'a> { } } } - - // Same as `from_str`, but not const. - // Used by format_args!() expansion when arguments are inlined, - // e.g. format_args!("{}", 123), which is not allowed in const. - #[inline] - pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> { - Arguments::from_str(s) - } } #[doc(hidden)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index df24dd43b82eb..6156525b2f599 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -991,7 +991,7 @@ pub(crate) mod builtin { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "format_args_macro"] #[allow_internal_unsafe] - #[allow_internal_unstable(fmt_internals)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[macro_export] macro_rules! format_args { @@ -1005,7 +1005,7 @@ pub(crate) mod builtin { /// /// This macro will be removed once `format_args` is allowed in const contexts. #[unstable(feature = "const_format_args", issue = "none")] - #[allow_internal_unstable(fmt_internals, const_fmt_arguments_new)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[macro_export] macro_rules! const_format_args { @@ -1020,7 +1020,7 @@ pub(crate) mod builtin { reason = "`format_args_nl` is only for internal \ language use and is subject to change" )] - #[allow_internal_unstable(fmt_internals)] + #[allow_internal_unstable(fmt_internals, fmt_arguments_from_str)] #[rustc_builtin_macro] #[doc(hidden)] #[macro_export] From ddebb6269f53d16465f1b49484527a8cd638abbd Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 13 Nov 2025 16:05:09 +0100 Subject: [PATCH 21/21] add assembly test for infinite recursion with `become` --- .../tail-call-infinite-recursion.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/assembly-llvm/tail-call-infinite-recursion.rs diff --git a/tests/assembly-llvm/tail-call-infinite-recursion.rs b/tests/assembly-llvm/tail-call-infinite-recursion.rs new file mode 100644 index 0000000000000..f788f0d590b6b --- /dev/null +++ b/tests/assembly-llvm/tail-call-infinite-recursion.rs @@ -0,0 +1,21 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: --target x86_64-unknown-linux-gnu -Copt-level=0 -Cllvm-args=-x86-asm-syntax=intel +//@ needs-llvm-components: x86 +#![expect(incomplete_features)] +#![feature(no_core, explicit_tail_calls)] +#![crate_type = "lib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +// Test that an infinite loop via guaranteed tail calls does not blow the stack. + +// CHECK-LABEL: inf +// CHECK: mov rax, qword ptr [rip + inf@GOTPCREL] +// CHECK: jmp rax +#[unsafe(no_mangle)] +fn inf() -> ! { + become inf() +}