diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index e3865345cae84..f48fbe6324834 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -8,9 +8,9 @@ use rustc_span::edition::Edition::Edition2024; use super::prelude::*; use crate::attributes::AttributeSafety; use crate::session_diagnostics::{ - EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, - NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral, - SanitizeInvalidStatic, TargetFeatureOnLangItem, + EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport, + NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral, + ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem, }; use crate::target_checking::Policy::AllowSilent; @@ -795,7 +795,8 @@ pub(crate) struct PatchableFunctionEntryParser; impl SingleAttributeParser for PatchableFunctionEntryParser { const PATH: &[Symbol] = &[sym::patchable_function_entry]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]); - const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]); + const TEMPLATE: AttributeTemplate = + template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]); const STABILITY: AttributeStability = unstable!(patchable_function_entry); fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option { @@ -803,74 +804,85 @@ impl SingleAttributeParser for PatchableFunctionEntryParser { let mut prefix = None; let mut entry = None; + let mut section = None; if meta_item_list.len() == 0 { cx.adcx().expected_at_least_one_argument(meta_item_list.span); return None; } - let mut errored = false; - for item in meta_item_list.mixed() { let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else { - continue; + return None; }; let attrib_to_write = match ident.name { sym::prefix_nops => { // Duplicate prefixes are not allowed if prefix.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::prefix_nops); - continue; + return None; } &mut prefix } sym::entry_nops => { // Duplicate entries are not allowed if entry.is_some() { - errored = true; cx.adcx().duplicate_key(ident.span, sym::entry_nops); - continue; + return None; } &mut entry } + sym::section => { + // Duplicate entries are not allowed + if section.is_some() { + cx.adcx().duplicate_key(ident.span, sym::section); + return None; + } + // Only a string type value is allowed. + let Some(value_str) = value.value_as_str() else { + cx.adcx().expect_string_literal(value); + return None; + }; + // The section name does not allow null characters. + if value_str.as_str().contains('\0') { + cx.emit_err(NullOnSection { span: value.value_span }); + } + // The section name is not allowed to be empty, LLVM does + // not allow them. + if value_str.is_empty() { + cx.emit_err(EmptySection { span: value.value_span }); + } + section = Some(value_str); + // Integer parsing is not needed, process next item. + continue; + } _ => { - errored = true; cx.adcx().expected_specific_argument( ident.span, &[sym::prefix_nops, sym::entry_nops], ); - continue; + return None; } }; let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else { - errored = true; cx.adcx().expected_integer_literal(value.value_span); - continue; + return None; }; let Ok(val) = val.get().try_into() else { - errored = true; cx.adcx().expected_integer_literal_in_range( value.value_span, u8::MIN as isize, u8::MAX as isize, ); - continue; + return None; }; *attrib_to_write = Some(val); } - if errored { - None - } else { - Some(AttributeKind::PatchableFunctionEntry { - prefix: prefix.unwrap_or(0), - entry: entry.unwrap_or(0), - }) - } + Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 63e33e0ae4882..9e5d7bb2cf375 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -292,6 +292,13 @@ pub(crate) struct EmptyExportName { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not be empty")] +pub(crate) struct EmptySection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { @@ -327,6 +334,13 @@ pub(crate) struct NullOnObjcSelector { pub span: Span, } +#[derive(Diagnostic)] +#[diag("`section` may not contain null characters", code = E0648)] +pub(crate) struct NullOnSection { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index fe36a9865485d..20e712174e0e9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -86,11 +86,26 @@ fn patchable_function_entry_attrs<'ll>( attr: Option, ) -> SmallVec<[&'ll Attribute; 2]> { let mut attrs = SmallVec::new(); - let patchable_spec = attr.unwrap_or_else(|| { - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry) - }); - let entry = patchable_spec.entry(); - let prefix = patchable_spec.prefix(); + + let mut entry = sess.opts.unstable_opts.patchable_function_entry.entry(); + let mut prefix = sess.opts.unstable_opts.patchable_function_entry.prefix(); + let mut section = sess.opts.unstable_opts.patchable_function_entry.section(); + let section_sym; + + // Apply attribute specified overrides, if any. + if let Some(patchable_spec) = attr { + if let Some(sym) = patchable_spec.section() { + section_sym = sym; + section = Some(section_sym.as_str()); + } + // Override the nop counts if either is present. If only one is present, the + // other count is implied to be 0. + if patchable_spec.entry().is_some() || patchable_spec.prefix().is_some() { + entry = patchable_spec.entry().unwrap_or(0); + prefix = patchable_spec.prefix().unwrap_or(0); + } + } + if entry > 0 { attrs.push(llvm::CreateAttrStringValue( cx.llcx, @@ -105,6 +120,13 @@ fn patchable_function_entry_attrs<'ll>( &format!("{}", prefix), )); } + if let Some(section) = section { + attrs.push(llvm::CreateAttrStringValue( + cx.llcx, + "patchable-function-entry-section", + section, + )); + } attrs } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 6198a98e5f7ae..80f13a7a35255 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -14,7 +14,6 @@ use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::PatchableFunctionEntry; use rustc_middle::mono::CodegenUnit; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, @@ -343,14 +342,13 @@ pub(crate) unsafe fn create_module<'ll>( // Add "kcfi-offset" module flag with -Z patchable-function-entry (See // https://reviews.llvm.org/D141172). - let pfe = - PatchableFunctionEntry::from_config(sess.opts.unstable_opts.patchable_function_entry); - if pfe.prefix() > 0 { + let patchable_prefix_nops = sess.opts.unstable_opts.patchable_function_entry.prefix(); + if patchable_prefix_nops > 0 { llvm::add_module_flag_u32( llmod, llvm::ModuleFlagMergeBehavior::Override, "kcfi-offset", - pfe.prefix().into(), + patchable_prefix_nops.into(), ); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 4d271447746c1..46bb1182c298c 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -290,9 +290,11 @@ fn process_builtin_attrs( AttributeKind::RustcOffloadKernel => { codegen_fn_attrs.flags |= CodegenFnAttrFlags::OFFLOAD_KERNEL } - AttributeKind::PatchableFunctionEntry { prefix, entry } => { + AttributeKind::PatchableFunctionEntry { prefix, entry, section } => { codegen_fn_attrs.patchable_function_entry = - Some(PatchableFunctionEntry::from_prefix_and_entry(*prefix, *entry)); + Some(PatchableFunctionEntry::from_prefix_entry_and_section( + *prefix, *entry, *section, + )); } AttributeKind::InstrumentFn(instrument_fn) => { codegen_fn_attrs.instrument_fn = match instrument_fn { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 41e550b8d6a3a..65af690611919 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -490,9 +490,11 @@ impl server::Server for Rustc<'_, '_> { fn literal_from_str(&mut self, s: &str) -> Result, String> { let name = FileName::proc_macro_source_code(s); - let mut parser = + let mut parser = rustc_errors::catch_fatal_errors(|| { new_parser_from_source_str(self.psess(), name, s.to_owned(), StripTokens::Nothing) - .map_err(cancel_diags_into_string)?; + }) + .map_err(|_| String::from("failed to parse to literal"))? + .map_err(cancel_diags_into_string)?; let first_span = parser.token.span.data(); let minus_present = parser.eat(exp!(Minus)); @@ -569,12 +571,15 @@ impl server::Server for Rustc<'_, '_> { } fn ts_from_str(&mut self, src: &str) -> Result { - source_str_to_stream( - self.psess(), - FileName::proc_macro_source_code(src), - src.to_string(), - Some(self.call_site), - ) + rustc_errors::catch_fatal_errors(|| { + source_str_to_stream( + self.psess(), + FileName::proc_macro_source_code(src), + src.to_string(), + Some(self.call_site), + ) + }) + .map_err(|_| String::from("failed to parse to tokenstream"))? .map_err(cancel_diags_into_string) } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index acae158acb2b4..17d00863d99d5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1271,8 +1271,9 @@ pub enum AttributeKind { /// Represents `#[patchable_function_entry]` PatchableFunctionEntry { - prefix: u8, - entry: u8, + prefix: Option, + entry: Option, + section: Option, }, /// Represents `#[path]` diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5933d0fd4b356..a3264d3cc3311 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -866,7 +866,7 @@ fn test_unstable_options_tracking_hash() { tracked!(panic_in_drop, PanicStrategy::Abort); tracked!( patchable_function_entry, - PatchableFunctionEntry::from_total_and_prefix_nops(10, 5) + PatchableFunctionEntry::from_parts(10, 5, None) .expect("total must be greater than or equal to prefix") ); tracked!(plt, Some(true)); diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index dd2cb505cb1bc..5d1b82b0bb48c 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -338,7 +338,7 @@ trait UnusedDelimLint { && !snip.starts_with(' ') { " " - } else if let Ok(snip) = sm.span_to_prev_source(value_span) + } else if let Ok(snip) = sm.span_to_next_source(value_span) && snip.starts_with(|c: char| c.is_alphanumeric()) { " " diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 713c6597c1966..b6ae4a98a34e3 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -114,7 +114,7 @@ pub struct CodegenFnAttrs { // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity pub alignment: Option, /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around - /// the function entry. + /// the function entry, or override default section to record entry location. pub patchable_function_entry: Option, /// The `#[rustc_objc_class = "..."]` attribute. pub objc_class: Option, @@ -162,24 +162,33 @@ pub struct TargetFeature { #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, StableHash)] pub struct PatchableFunctionEntry { /// Nops to prepend to the function - prefix: u8, + prefix: Option, /// Nops after entry, but before body - entry: u8, + entry: Option, + /// Optional, specific section to record entry location in + section: Option, } impl PatchableFunctionEntry { - pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self { - Self { prefix: config.prefix(), entry: config.entry() } + pub fn from_prefix_entry_and_section( + prefix: Option, + entry: Option, + section: Option, + ) -> Self { + Self { prefix, entry, section } } pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self { - Self { prefix, entry } + Self { prefix: Some(prefix), entry: Some(entry), section: None } } - pub fn prefix(&self) -> u8 { + pub fn prefix(&self) -> Option { self.prefix } - pub fn entry(&self) -> u8 { + pub fn entry(&self) -> Option { self.entry } + pub fn section(&self) -> Option { + self.section + } } #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, StableHash)] diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index c7da2d9a1fc38..4492cdf2d2f8a 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -3342,23 +3342,29 @@ impl DumpMonoStatsFormat { /// `-Z patchable-function-entry` representation - how many nops to put before and after function /// entry. -#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)] +#[derive(Clone, PartialEq, Hash, Debug, Default)] pub struct PatchableFunctionEntry { /// Nops before the entry prefix: u8, /// Nops after the entry entry: u8, + /// An optional section name to record the entry location + section: Option, } impl PatchableFunctionEntry { - pub fn from_total_and_prefix_nops( + pub fn from_parts( total_nops: u8, prefix_nops: u8, + section: Option, ) -> Option { if total_nops < prefix_nops { None + // Section name cannot contain null characters. + } else if section.as_ref().map(|x| x.contains('\0') || x.is_empty()).unwrap_or(false) { + None } else { - Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops }) + Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops, section }) } } pub fn prefix(&self) -> u8 { @@ -3367,6 +3373,9 @@ impl PatchableFunctionEntry { pub fn entry(&self) -> u8 { self.entry } + pub fn section(&self) -> Option<&str> { + self.section.as_ref().map(|x| x.as_str()) + } } /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy, diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index efc8a70f4feb2..b7668bd47d8ec 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -784,7 +784,7 @@ mod desc { pub(crate) const parse_passes: &str = "a space-separated list of passes, or `all`"; pub(crate) const parse_panic_strategy: &str = "either `unwind`, `abort`, or `immediate-abort`"; pub(crate) const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`"; - pub(crate) const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)"; + pub(crate) const parse_patchable_function_entry: &str = "a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops"; pub(crate) const parse_opt_panic_strategy: &str = parse_panic_strategy; pub(crate) const parse_relro_level: &str = "one of: `full`, `partial`, or `off`"; pub(crate) const parse_sanitizers: &str = "comma separated list of sanitizers: `address`, `cfi`, `dataflow`, `hwaddress`, `kcfi`, `kernel-address`, `kernel-hwaddress`, `leak`, `memory`, `memtag`, `safestack`, `shadow-call-stack`, `thread`, or 'realtime'"; @@ -1206,20 +1206,24 @@ pub mod parse { ) -> bool { let mut total_nops = 0; let mut prefix_nops = 0; + let mut section = None; if !parse_number(&mut total_nops, v) { - let parts = v.and_then(|v| v.split_once(',')).unzip(); - if !parse_number(&mut total_nops, parts.0) { + let parts: Vec<_> = v.unwrap_or("").split(',').collect(); + if parts.len() < 2 || parts.len() > 3 { return false; } - if !parse_number(&mut prefix_nops, parts.1) { + + if !parse_number(&mut total_nops, Some(parts[0])) { + return false; + } + if !parse_number(&mut prefix_nops, Some(parts[1])) { return false; } + section = parts.get(2).map(|x| x.to_string()); } - if let Some(pfe) = - PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops) - { + if let Some(pfe) = PatchableFunctionEntry::from_parts(total_nops, prefix_nops, section) { *slot = pfe; return true; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c75da250efd5f..4bada9712d2a9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1874,6 +1874,7 @@ symbols! { saturating_sub, sdylib, search_unbox, + section, select_unpredictable, self_in_typedefs, self_struct_ctor, diff --git a/library/std/src/attribute_docs.rs b/library/core/src/attribute_docs.rs similarity index 100% rename from library/std/src/attribute_docs.rs rename to library/core/src/attribute_docs.rs diff --git a/library/std/src/keyword_docs.rs b/library/core/src/keyword_docs.rs similarity index 98% rename from library/std/src/keyword_docs.rs rename to library/core/src/keyword_docs.rs index 5f94a13dad22a..596765be5e2dd 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/core/src/keyword_docs.rs @@ -195,7 +195,7 @@ mod break_keyword {} /// to be most things that would be reasonable to have in a constant (barring `const fn`s). For /// example, you can't have a [`File`] as a `const`. /// -/// [`File`]: crate::fs::File +/// [`File`]: ../std/fs/struct.File.html /// /// The only lifetime allowed in a constant is `'static`, which is the lifetime that encompasses /// all others in a Rust program. For example, if you wanted to define a constant string, it would @@ -484,7 +484,7 @@ mod extern_keyword {} #[doc(keyword = "false")] // -/// A value of type [`bool`] representing logical **false**. +/// A value of type [`prim@bool`] representing logical **false**. /// /// `false` is the logical opposite of [`true`]. /// @@ -1060,7 +1060,8 @@ mod mod_keyword {} /// /// `move` is often used when [threads] are involved. /// -/// ```rust +#[cfg_attr(target_os = "wasi", doc = "```rust,ignore (thread::spawn not supported)")] +#[cfg_attr(not(target_os = "wasi"), doc = "```rust")] /// let data = vec![1, 2, 3]; /// /// std::thread::spawn(move || { @@ -1235,31 +1236,18 @@ mod ref_keyword {} /// `return` returns from the function immediately (an "early return"): /// /// ```no_run -/// use std::fs::File; -/// use std::io::{Error, ErrorKind, Read, Result}; -/// -/// fn main() -> Result<()> { -/// let mut file = match File::open("foo.txt") { -/// Ok(f) => f, -/// Err(e) => return Err(e), -/// }; +/// fn main() -> Result<(), &'static str> { +/// let contents = "Hello, world!"; /// -/// let mut contents = String::new(); -/// let size = match file.read_to_string(&mut contents) { -/// Ok(s) => s, -/// Err(e) => return Err(e), -/// }; +/// if contents.contains("impossible!") { +/// return Err("oh no!"); +/// } /// -/// if contents.contains("impossible!") { -/// return Err(Error::new(ErrorKind::Other, "oh no!")); -/// } +/// if contents.len() > 9000 { +/// return Err("over 9000!"); +/// } /// -/// if size > 9000 { -/// return Err(Error::new(ErrorKind::Other, "over 9000!")); -/// } -/// -/// assert_eq!(contents, "Hello, world!"); -/// Ok(()) +/// Ok(()) /// } /// ``` /// @@ -1306,7 +1294,8 @@ mod return_keyword {} /// manner to computed goto). /// /// Example of using `become` to implement functional-style `fold`: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1360,7 +1349,8 @@ mod return_keyword {} /// (unless it's coerced to a function pointer) /// /// It is possible to tail-call a function pointer: -/// ``` +/// +/// ```ignore-wasm (tail-call target feature not enabled by default on wasm) /// #![feature(explicit_tail_calls)] /// #![expect(incomplete_features)] /// @@ -1631,8 +1621,8 @@ mod self_upper_keyword {} /// [`extern`]: keyword.extern.html /// [`mut`]: keyword.mut.html /// [`unsafe`]: keyword.unsafe.html -/// [`Mutex`]: sync::Mutex -/// [`OnceLock`]: sync::OnceLock +/// [`Mutex`]: ../std/sync/struct.Mutex.html +/// [`OnceLock`]: ../std/sync/struct.OnceLock.html /// [`RefCell`]: cell::RefCell /// [atomic]: sync::atomic /// [Reference]: ../reference/items/static-items.html @@ -1959,7 +1949,7 @@ mod trait_keyword {} #[doc(keyword = "true")] // -/// A value of type [`bool`] representing logical **true**. +/// A value of type [`prim@bool`] representing logical **true**. /// /// Logically `true` is not equal to [`false`]. /// @@ -2312,6 +2302,7 @@ mod type_keyword {} /// [`static`]: keyword.static.html /// [`union`]: keyword.union.html /// [`impl`]: keyword.impl.html +/// [`Vec::set_len`]: ../std/vec/struct.Vec.html#method.set_len /// [raw pointers]: ../reference/types/pointer.html /// [memory safety]: ../book/ch19-01-unsafe-rust.html /// [Rustonomicon]: ../nomicon/index.html @@ -2502,7 +2493,7 @@ mod use_keyword {} /// ``` /// /// `where` is available anywhere generic and lifetime parameters are available, -/// as can be seen with the [`Cow`](crate::borrow::Cow) type from the standard +/// as can be seen with the [`Cow`](../std/borrow/enum.Cow.html) type from the standard /// library: /// /// ```rust diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 6fa1aa3b1f172..8e310cc7d2155 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -386,4 +386,17 @@ pub mod simd { pub use crate::core_simd::simd::*; } +// Include private modules that exist solely to provide rustdoc +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("attribute_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for the existing keywords. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("keyword_docs.rs"); + +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. include!("primitive_docs.rs"); diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 78e984db6cba5..afa30c8c00f27 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -772,20 +772,23 @@ pub mod from { pub use core::from::From; } -// Include a number of private modules that exist solely to provide -// the rustdoc documentation for primitive types. Using `include!` -// because rustdoc only looks for these modules at the crate level. -include!("../../core/src/primitive_docs.rs"); +// We include the following files here *again* (they are already included in libcore) +// so that they show up in search results for the std crate, and to avoid breaking +// existing links: + +// documentation for built-in attributes. Using `include!` because rustdoc +// only looks for these modules at the crate level. +include!("../../core/src/attribute_docs.rs"); // Include a number of private modules that exist solely to provide // the rustdoc documentation for the existing keywords. Using `include!` // because rustdoc only looks for these modules at the crate level. -include!("keyword_docs.rs"); +include!("../../core/src/keyword_docs.rs"); -// Include private modules that exist solely to provide rustdoc -// documentation for built-in attributes. Using `include!` because rustdoc -// only looks for these modules at the crate level. -include!("attribute_docs.rs"); +// Include a number of private modules that exist solely to provide +// the rustdoc documentation for primitive types. Using `include!` +// because rustdoc only looks for these modules at the crate level. +include!("../../core/src/primitive_docs.rs"); // This is required to avoid an unstable error when `restricted-std` is not // enabled. The use of #![feature(restricted_std)] in rustc-std-workspace-std diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index e7fe8bd1f3858..72094373b5719 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -14,8 +14,11 @@ use crate::{ RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, }; -/// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler -/// later. +/// Represents flag values in `String` form with a `\x1f` delimiter to pass to the compiler later. +/// +/// Flags are emitted via `CARGO_ENCODED_RUSTFLAGS` / `CARGO_ENCODED_RUSTDOCFLAGS`, +/// which use `\x1f` (ASCII Unit Separator) as the delimiter and therefore allow spaces +/// within individual flag values (e.g. paths from `llvm-config --libdir`). /// /// `-Z crate-attr` flags will be applied recursively on the target code using the /// `rustc_parse::parser::Parser`. See `rustc_builtin_macros::cmdline_attrs::inject` for more @@ -51,11 +54,16 @@ impl Rustflags { } fn arg(&mut self, arg: &str) -> &mut Self { - assert_eq!(arg.split(' ').count(), 1); - if !self.0.is_empty() { - self.0.push(' '); + assert!( + !arg.contains('\x1f'), + "rustflag must not contain the ASCII unit separator (\\x1f): {arg:?}" + ); + if !arg.is_empty() { + if !self.0.is_empty() { + self.0.push('\x1f'); + } + self.0.push_str(arg); } - self.0.push_str(arg); self } @@ -457,14 +465,21 @@ impl From for BootstrapCommand { cargo.command.args(cargo.args); + // Always unset the plain RUSTFLAGS/RUSTDOCFLAGS so that downstream + // tools (e.g. build.rs scripts) see only the encoded form. Any flags + // from the caller's environment have already been folded into the + // Rustflags struct via `propagate_cargo_env`. + cargo.command.env_remove("RUSTFLAGS"); + cargo.command.env_remove("RUSTDOCFLAGS"); + let rustflags = &cargo.rustflags.0; if !rustflags.is_empty() { - cargo.command.env("RUSTFLAGS", rustflags); + cargo.command.env("CARGO_ENCODED_RUSTFLAGS", rustflags); } let rustdocflags = &cargo.rustdocflags.0; if !rustdocflags.is_empty() { - cargo.command.env("RUSTDOCFLAGS", rustdocflags); + cargo.command.env("CARGO_ENCODED_RUSTDOCFLAGS", rustdocflags); } let encoded_hostflags = cargo.hostflags.encode(); @@ -918,7 +933,10 @@ impl Builder<'_> { } let rustdoc_path = match cmd_kind { - Kind::Doc | Kind::Test | Kind::MiriTest => self.rustdoc_for_compiler(compiler), + Kind::Doc => self.rustdoc_for_compiler(compiler), + Kind::Test | Kind::MiriTest if self.test_target.runs_doctests() => { + self.rustdoc_for_compiler(compiler) + } _ => PathBuf::from("/path/to/nowhere/rustdoc/not/required"), }; @@ -1183,8 +1201,9 @@ impl Builder<'_> { if (mode == Mode::ToolRustcPrivate || mode == Mode::Codegen) && let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = + let llvm_libdir_raw = command(llvm_config).cached().arg("--libdir").run_capture_stdout(self).stdout(); + let llvm_libdir = llvm_libdir_raw.trim(); if target.is_msvc() { rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}")); } else { diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 048fcc5c80145..66e14dae9b9ec 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1150,6 +1150,7 @@ impl<'a> Builder<'a> { /// compiler will run on, *not* the target it will build code for). Explicitly does not take /// `Compiler` since all `Compiler` instances are meant to be obtained through this function, /// since it ensures that they are valid (i.e., built and assembled). + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1183,6 +1184,7 @@ impl<'a> Builder<'a> { /// /// However, without this optimization, we would also build stage 2 rustc for **target1**, /// which is completely wasteful. + #[track_caller] pub fn compiler_for_std(&self, stage: u32) -> Compiler { if compile::Std::should_be_uplifted_from_stage_1(self, stage) { self.compiler(1, self.host_target) @@ -1202,6 +1204,7 @@ impl<'a> Builder<'a> { /// sysroot. /// /// See `force_use_stage1` and `force_use_stage2` for documentation on what each argument is. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1249,6 +1252,7 @@ impl<'a> Builder<'a> { /// Prefer using this method rather than manually invoking `Std::new`. /// /// Returns an optional build stamp, if libstd was indeed built. + #[track_caller] #[cfg_attr( feature = "tracing", instrument( @@ -1297,17 +1301,20 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler } } + #[track_caller] pub fn sysroot(&self, compiler: Compiler) -> PathBuf { self.ensure(compile::Sysroot::new(compiler)) } /// Returns the bindir for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_bindir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("bin") } /// Returns the libdir where the standard library and other artifacts are /// found for a compiler's sysroot. + #[track_caller] pub fn sysroot_target_libdir(&self, compiler: Compiler, target: TargetSelection) -> PathBuf { self.ensure(Libdir { compiler, target }).join(target).join("lib") } @@ -1416,6 +1423,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Returns a path to `Rustdoc` that "belongs" to the `target_compiler`. /// It can be either a stage0 rustdoc or a locally built rustdoc that *links* to /// `target_compiler`. + #[track_caller] pub fn rustdoc_for_compiler(&self, target_compiler: Compiler) -> PathBuf { self.ensure(tool::Rustdoc { target_compiler }) } @@ -1532,6 +1540,7 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler /// Ensure that a given step is built, returning its output. This will /// cache the step, so it is safe (and good!) to call this as often as /// needed to ensure that all dependencies are built. + #[track_caller] pub fn ensure(&'a self, step: S) -> S::Output { { let mut stack = self.stack.borrow_mut(); @@ -1589,7 +1598,8 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler // in the step_name field. "step", step_name = pretty_step_name::(), - args = step_debug_args(&step) + args = step_debug_args(&step), + location = crate::utils::tracing::format_location(*std::panic::Location::caller()) ); span.entered() }; diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index 1a59e54a95cbf..176a61b15c988 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -2423,6 +2423,19 @@ mod snapshot { } } + #[test] + fn test_library_tests_only_does_not_build_rustdoc() { + let ctx = TestCtx::new(); + let host = TargetSelection::from_user(&host_target()); + insta::assert_snapshot!( + ctx.config("test").args(&["--tests", "library/core"]).render_steps(), + @r" + [build] llvm + [build] rustc 0 -> rustc 1 + [build] rustc 1 -> std 1 + "); + } + #[test] fn test_cargo_stage_1() { let ctx = TestCtx::new(); diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index cffe0dbb3e3a5..d6a2d89482ee4 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,10 +1,12 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fmt::Debug; use std::io::BufWriter; +use std::panic::Location; use std::path::Path; use crate::core::builder::{AnyDebug, Step, pretty_step_name}; use crate::t; +use crate::utils::tracing::format_location; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -20,6 +22,7 @@ pub struct StepGraph { } impl StepGraph { + #[track_caller] pub fn register_step_execution( &mut self, step: &S, @@ -57,6 +60,7 @@ impl StepGraph { } } + #[track_caller] pub fn register_cached_step( &mut self, step: &S, @@ -97,12 +101,18 @@ struct Node { struct NodeHandle(usize); /// Represents a dependency between two bootstrap steps. -#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)] -struct Edge { - src: NodeHandle, - dst: NodeHandle, +#[derive(Default)] +struct EdgeData { // Was the corresponding execution of a step cached, or was the step actually executed? cached: bool, + // Locations from where the step was called + locations: Vec>, +} + +#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Clone)] +struct EdgeKey { + src: NodeHandle, + dst: NodeHandle, } // We could use a library for this, but they either: @@ -114,8 +124,8 @@ struct Edge { #[derive(Default)] struct DotGraph { nodes: Vec, + edges: HashMap, /// The `NodeHandle` represents an index within `self.nodes` - edges: HashSet, key_to_index: HashMap, } @@ -127,16 +137,19 @@ impl DotGraph { handle } + #[track_caller] fn add_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - self.edges.insert(Edge { src, dst, cached: false }); + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.locations.push(*Location::caller()); } + #[track_caller] fn add_cached_edge(&mut self, src: NodeHandle, dst: NodeHandle) { - // There's no point in rendering both cached and uncached edge - let uncached = Edge { src, dst, cached: false }; - if !self.edges.contains(&uncached) { - self.edges.insert(Edge { src, dst, cached: true }); - } + let key = EdgeKey { src, dst }; + let edge = self.edges.entry(key).or_default(); + edge.cached = true; + edge.locations.push(*Location::caller()); } fn get_handle_by_key(&self, key: &str) -> Option { @@ -157,11 +170,23 @@ impl DotGraph { )?; } - let mut edges: Vec<&Edge> = self.edges.iter().collect(); - edges.sort(); - for edge in edges { - let style = if edge.cached { "dashed" } else { "solid" }; - writeln!(file, r#"{} -> {} [style="{style}"]"#, edge.src.0, edge.dst.0)?; + let mut edges: Vec<(&EdgeKey, &EdgeData)> = self.edges.iter().collect(); + edges.sort_by_key(|(key, _)| **key); + for (key, data) in edges { + let style = if data.cached { "dashed" } else { "solid" }; + let mut locations = data + .locations + .iter() + .map(|location| format_location(*location)) + .collect::>(); + locations.sort(); + locations.dedup(); + let locations = locations.join(", "); + writeln!( + file, + r#"{} -> {} [style="{style}", tooltip="{locations}"]"#, + key.src.0, key.dst.0, + )?; } writeln!(file, "}}") diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index b1226ed7de7be..4e75347f4cbae 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -351,7 +351,7 @@ mod inner { let field = &values.fields[0]; write!(writer, " {{{}}}", field.1)?; } - write_location(writer, span.metadata())?; + write_with_location(writer)?; } // Executed command COMMAND_SPAN_TARGET => { diff --git a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md index 4a9bf47a29011..80f0e54907872 100644 --- a/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md +++ b/src/doc/unstable-book/src/compiler-flags/patchable-function-entry.md @@ -2,10 +2,13 @@ -------------------- -The `-Z patchable-function-entry=total_nops,prefix_nops` or `-Z patchable-function-entry=total_nops` +The `-Z patchable-function-entry=total_nops,prefix_nops,record_section`, + `-Z patchable-function-entry=total_nops,prefix_nops`, or + `-Z patchable-function-entry=total_nops` compiler flag enables nop padding of function entries with 'total_nops' nops, with -an offset for the entry of the function at 'prefix_nops' nops. In the second form, -'prefix_nops' defaults to 0. +an offset for the entry of the function at 'prefix_nops' nops. In the third form, +'prefix_nops' defaults to 0. record\_section can specify a specific linker section +to place entry record in, the default is `__patchable_function_entries`. As an illustrative example, `-Z patchable-function-entry=3,2` would produce: diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 633452f6052d7..3e46ee17c8a65 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -182,8 +182,9 @@ fn miri_config( .map(Into::into) .collect(), envs: vec![ - // Reset `RUSTFLAGS` to work around . + // Reset `RUSTFLAGS`/`CARGO_ENCODED_RUSTFLAGS` to work around . ("RUSTFLAGS".into(), None), + ("CARGO_ENCODED_RUSTFLAGS".into(), None), // Reset `MIRIFLAGS` because it caused trouble in the past and should not be needed. ("MIRIFLAGS".into(), None), // Allow `cargo miri build`. diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs index 72204c78a4906..45aa46d24fe94 100644 --- a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-both-flags.rs @@ -39,6 +39,26 @@ pub fn fun5() {} #[patchable_function_entry(prefix_nops = 4)] pub fn fun6() {} +// The attribute should override patchable-function-prefix to 4 +// and patchable-function-entry to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(prefix_nops = 4, section = "foo")] +pub fn fun7() {} + +// The attribute should override patchable-function-entry-section, +// while passing through the commandline options. +#[no_mangle] +#[patchable_function_entry(section = "bar")] +pub fn fun8() {} + +// The attribute should override patchable-function-entry to 5 +// and patchable-function-prefix to the default of 0, clearing it entirely, +// while setting patchable-function-entry-section. +#[no_mangle] +#[patchable_function_entry(entry_nops = 5, section = "baz")] +pub fn fun9() {} + // CHECK: @fun0() unnamed_addr #0 // CHECK: @fun1() unnamed_addr #1 // CHECK: @fun2() unnamed_addr #2 @@ -46,6 +66,9 @@ pub fn fun6() {} // CHECK: @fun4() unnamed_addr #4 // CHECK: @fun5() unnamed_addr #5 // CHECK: @fun6() unnamed_addr #6 +// CHECK: @fun7() unnamed_addr #7 +// CHECK: @fun8() unnamed_addr #8 +// CHECK: @fun9() unnamed_addr #9 // CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-prefix"="10" {{.*}} } // CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="2"{{.*}}"patchable-function-prefix"="1" {{.*}} } @@ -62,3 +85,12 @@ pub fn fun6() {} // CHECK: attributes #6 = { {{.*}}"patchable-function-prefix"="4"{{.*}} } // CHECK-NOT: attributes #6 = { {{.*}}patchable-function-entry{{.*}} } +// +// CHECK: attributes #7 = { {{.*}}"patchable-function-entry-section"="foo"{{.*}}"patchable-function-prefix"="4" {{.*}} } +// CHECK-NOT: attributes #7 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #8 = { {{.*}}"patchable-function-entry-section"="bar"{{.*}} } +// CHECK-NOT: attributes #8 = { {{.*}}"patchable-function-entry"{{.*}} } +// +// CHECK: attributes #9 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="baz" {{.*}} } +// CHECK-NOT: attributes #9 = { {{.*}}"patchable-function-prefix{{.*}} } diff --git a/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs new file mode 100644 index 0000000000000..9ffff5ca7537c --- /dev/null +++ b/tests/codegen-llvm/patchable-function-entry/patchable-function-entry-section.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Z patchable-function-entry=15,10,default_foo_section +// + +#![feature(patchable_function_entry)] +#![crate_type = "lib"] + +// This should have the default, as set by the compile flags +#[no_mangle] +pub fn fun0() {} + +// This should override the default section name +#[no_mangle] +#[patchable_function_entry(section = "bar_section")] +pub fn fun1() {} + +// CHECK: @fun0() unnamed_addr #0 +// CHECK: @fun1() unnamed_addr #1 + +// CHECK: attributes #0 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="default_foo_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } +// CHECK: attributes #1 = { {{.*}}"patchable-function-entry"="5"{{.*}}"patchable-function-entry-section"="bar_section"{{.*}}"patchable-function-prefix"="10" {{.*}} } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7d8bd3700d4bb..96012d8df936b 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -550,8 +550,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0565]: malformed `coroutine` attribute input --> $DIR/malformed-attrs.rs:118:5 diff --git a/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs b/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs new file mode 100644 index 0000000000000..d3c70f8769a9c --- /dev/null +++ b/tests/ui/lint/unused-parens-trailing-space-issue-158583.rs @@ -0,0 +1,5 @@ +fn main() { + #[deny(unused_parens)] + let _x = (3 + 6); + //~^ ERROR unnecessary parentheses around assigned value +} diff --git a/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr b/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr new file mode 100644 index 0000000000000..200379a5151eb --- /dev/null +++ b/tests/ui/lint/unused-parens-trailing-space-issue-158583.stderr @@ -0,0 +1,19 @@ +error: unnecessary parentheses around assigned value + --> $DIR/unused-parens-trailing-space-issue-158583.rs:3:14 + | +LL | let _x = (3 + 6); + | ^ ^ + | +note: the lint level is defined here + --> $DIR/unused-parens-trailing-space-issue-158583.rs:2:12 + | +LL | #[deny(unused_parens)] + | ^^^^^^^^^^^^^ +help: remove these parentheses + | +LL - let _x = (3 + 6); +LL + let _x = 3 + 6; + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs index 5cbeccf1b0e4f..1223c0c5b05f9 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.rs @@ -24,3 +24,19 @@ pub fn no_parameters_given() {} #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] //~^ ERROR malformed pub fn duplicate_parameter() {} + +#[patchable_function_entry(section = 255)] +//~^ ERROR malformed +pub fn invalid_section_parameter() {} + +#[patchable_function_entry(section = "foo", section = "bar")] +//~^ ERROR malformed +pub fn duplicate_section_parameter() {} + +#[patchable_function_entry(section = "fo\0o")] +//~^ ERROR null characters +pub fn nul_in_section_parameter() {} + +#[patchable_function_entry(section = "")] +//~^ ERROR empty +pub fn empty_section_parameter() {} diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr index 6c32a76834c69..c3f09ff12384a 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-attribute.stderr @@ -9,7 +9,7 @@ LL | #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 256, entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -23,7 +23,7 @@ LL | #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = "stringvalue", entry_nops = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -34,8 +34,8 @@ LL | #[patchable_function_entry] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:16:1 @@ -48,7 +48,7 @@ LL | #[patchable_function_entry(prefix_nops = 10, something = 0)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 10, something = 0)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | error[E0539]: malformed `patchable_function_entry` attribute input @@ -61,8 +61,8 @@ LL | #[patchable_function_entry()] | help: must be of the form | -LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n)] - | +++++++++++++++++++++++++++++++ +LL | #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | ++++++++++++++++++++++++++++++++++++++++++++++++++++ error[E0538]: malformed `patchable_function_entry` attribute input --> $DIR/patchable-function-entry-attribute.rs:24:1 @@ -75,10 +75,50 @@ LL | #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] help: must be of the form | LL - #[patchable_function_entry(prefix_nops = 255, prefix_nops = 255)] -LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] | -error: aborting due to 6 previous errors +error[E0539]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:28:1 + | +LL | #[patchable_function_entry(section = 255)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^ + | | + | expected a string literal here + | +help: must be of the form + | +LL - #[patchable_function_entry(section = 255)] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0538]: malformed `patchable_function_entry` attribute input + --> $DIR/patchable-function-entry-attribute.rs:32:1 + | +LL | #[patchable_function_entry(section = "foo", section = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^ + | | + | found `section` used as a key more than once + | +help: must be of the form + | +LL - #[patchable_function_entry(section = "foo", section = "bar")] +LL + #[patchable_function_entry(prefix_nops = m, entry_nops = n, section = "section")] + | + +error[E0648]: `section` may not contain null characters + --> $DIR/patchable-function-entry-attribute.rs:36:38 + | +LL | #[patchable_function_entry(section = "fo\0o")] + | ^^^^^^^ + +error: `section` may not be empty + --> $DIR/patchable-function-entry-attribute.rs:40:38 + | +LL | #[patchable_function_entry(section = "")] + | ^^ + +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0538, E0539. +Some errors have detailed explanations: E0538, E0539, E0648. For more information about an error, try `rustc --explain E0538`. diff --git a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr index b09af94a61541..bde7f4aa9dff6 100644 --- a/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr +++ b/tests/ui/patchable-function-entry/patchable-function-entry-flags.stderr @@ -1,2 +1,2 @@ -error: incorrect value `1,2` for unstable option `patchable-function-entry` - either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops) was expected +error: incorrect value `1,2` for unstable option `patchable-function-entry` - a comma separated list of (prefix_nops,total_nops,section_name), (prefix_nops,total_nops), or (total_nops). Where prefix_nops <= total_nops where 0 < total_nops <= 255 and prefix_nops <= total_nops was expected diff --git a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs index 258f77067ce9c..b29c40770b0e7 100644 --- a/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs +++ b/tests/ui/proc-macro/auxiliary/nonfatal-parsing-body.rs @@ -7,12 +7,14 @@ use proc_macro::*; use self::Mode::*; // FIXME: all cases should become `NormalOk` or `NormalErr` +// +// And .stderr should be empty (no diagnostics should get emitted from fallible parsing in the proc +// macro). #[derive(PartialEq, Clone, Copy)] enum Mode { NormalOk, NormalErr, OtherError, - OtherWithPanic, } fn print_unspanned(s: &str) -> Result @@ -43,12 +45,11 @@ where assert!(t.is_err()); } OtherError => { - print_unspanned::(s); - } - OtherWithPanic => { - if catch_unwind(|| print_unspanned::(s)).is_ok() { - eprintln!("{s} did not panic"); - } + let t = print_unspanned::(s); + // For now we assert OK here, but in the future this should all move to NormalErr. + // Any case that's failing this should go to NormalErr, probably after verifying that a + // diagnostic did get emitted. + assert!(t.is_ok()); } } } @@ -136,9 +137,9 @@ pub fn run() { // FIXME: all of the cases below should return an Err and emit no diagnostics, but don't yet. // emits diagnostics and returns LexError - lit("r'r'", OtherError); - lit("c'r'", OtherError); - lit("\u{2000}", OtherError); + lit("r'r'", NormalErr); + lit("c'r'", NormalErr); + lit("\u{2000}", NormalErr); // emits diagnostic and returns a seemingly valid tokenstream stream("r'r'", OtherError); @@ -146,8 +147,8 @@ pub fn run() { stream("\u{2000}", OtherError); for parse in [stream as fn(&str, Mode), lit] { - // emits diagnostic(s), then panics - parse("r#", OtherWithPanic); + // emits diagnostic(s), then returns LexError + parse("r#", NormalErr); // emits diagnostic(s), then returns Ok(Literal { kind: ErrWithGuar, .. }) parse("0b2", OtherError); @@ -158,9 +159,10 @@ pub fn run() { "' '", OtherError, ); - parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), OtherWithPanic); - - // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar - parse("/*a*/ 0b2 //", OtherError); + parse(&format!("r{0}\"a\"{0}", "#".repeat(256)), NormalErr); } + + // emits diagnostic, then, when parsing as a lit, returns LexError, otherwise ErrWithGuar + lit("/*a*/ 0b2 //", NormalErr); + stream("/*a*/ 0b2 //", OtherError); } diff --git a/tests/ui/proc-macro/nonfatal-parsing.stderr b/tests/ui/proc-macro/nonfatal-parsing.stderr index cb6e243eebc9b..f015794e2ce67 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stderr +++ b/tests/ui/proc-macro/nonfatal-parsing.stderr @@ -130,15 +130,6 @@ LL | nonfatal_parsing::run!(); | = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) -error: invalid digit for a base 2 literal - --> $DIR/nonfatal-parsing.rs:15:5 - | -LL | nonfatal_parsing::run!(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) - error: found invalid character; only `#` is allowed in raw string delimitation: \u{0} --> :1:1 | @@ -199,6 +190,15 @@ error: invalid digit for a base 2 literal LL | /*a*/ 0b2 // | ^ +error: invalid digit for a base 2 literal + --> $DIR/nonfatal-parsing.rs:15:5 + | +LL | nonfatal_parsing::run!(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `nonfatal_parsing::run` (in Nightly builds, run with -Z macro-backtrace for more info) + error: aborting due to 22 previous errors For more information about this error, try `rustc --explain E0768`. diff --git a/tests/ui/proc-macro/nonfatal-parsing.stdout b/tests/ui/proc-macro/nonfatal-parsing.stdout index a46ef66f0f9d0..fcefde22541ba 100644 --- a/tests/ui/proc-macro/nonfatal-parsing.stdout +++ b/tests/ui/proc-macro/nonfatal-parsing.stdout @@ -52,15 +52,19 @@ Err(LexError("not a literal")) Ok(TokenStream [Ident { ident: "r", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream [Ident { ident: "c", span: Span }, Literal { kind: Char, symbol: "r", suffix: None, span: Span }]) Ok(TokenStream []) +Err(LexError("failed to parse to tokenstream")) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }]) Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }]) -Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }]) +Err(LexError("failed to parse to tokenstream")) +Err(LexError("failed to parse to literal")) Ok(Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "0b0.0", suffix: Some("f32"), span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'''", suffix: None, span: Span }) Ok(Literal { kind: ErrWithGuar, symbol: "'\n'", suffix: None, span: Span }) +Err(LexError("failed to parse to literal")) Err(LexError("comment or whitespace around literal")) +Ok(TokenStream [Literal { kind: ErrWithGuar, symbol: "0b2", suffix: None, span: Span }])