From 9529e2dbf36535e0dab59e2663fbdc10393b28e6 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Fri, 28 Aug 2026 21:33:40 +0200 Subject: [PATCH 1/8] rework handling of doc attributes on macro calls --- Cargo.lock | 1 - compiler/rustc_ast_lowering/src/lib.rs | 3 +- compiler/rustc_ast_passes/src/feature_gate.rs | 34 +----- .../rustc_attr_parsing/src/attributes/doc.rs | 102 +++++++++++------- compiler/rustc_attr_parsing/src/context.rs | 6 -- compiler/rustc_attr_parsing/src/interface.rs | 31 +++--- compiler/rustc_attr_parsing/src/lib.rs | 2 +- compiler/rustc_attr_parsing/src/stability.rs | 16 ++- compiler/rustc_expand/Cargo.toml | 1 - compiler/rustc_expand/src/expand.rs | 4 +- compiler/rustc_resolve/src/def_collector.rs | 3 +- tests/rustdoc-ui/feature-gate-doc_cfg.stderr | 36 +++---- tests/ui/attributes/attr-on-mac-call.rs | 11 ++ tests/ui/attributes/attr-on-mac-call.stderr | 8 +- tests/ui/feature-gates/doc-rust-logo.rs | 4 +- tests/ui/feature-gates/doc-rust-logo.stderr | 14 ++- .../feature-gates/feature-gate-doc_cfg.stderr | 6 +- .../feature-gates/feature-gate-doc_masked.rs | 8 +- .../feature-gate-doc_masked.stderr | 18 +++- .../feature-gate-doc_notable_trait.rs | 8 +- .../feature-gate-doc_notable_trait.stderr | 18 +++- .../feature-gate-rustdoc_internals.rs | 22 +++- .../feature-gate-rustdoc_internals.stderr | 56 +++++++--- .../unused/unused-doc-comments-for-macros.rs | 3 +- 24 files changed, 255 insertions(+), 160 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 696b797e612f9..3d8c246270130 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4026,7 +4026,6 @@ name = "rustc_expand" version = "0.0.0" dependencies = [ "rustc_ast", - "rustc_ast_passes", "rustc_ast_pretty", "rustc_attr_ir", "rustc_attr_parsing", diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 1265bae778601..def0934214ef2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -45,7 +45,7 @@ use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::node_id::NodeMap; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{self as ast, *}; -use rustc_attr_parsing::{AttributeParser, OmitDoc, Recovery, ShouldEmit}; +use rustc_attr_parsing::{AttributeParser, Recovery, ShouldEmit}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::{StableHash, StableHasher}; @@ -1231,7 +1231,6 @@ impl<'hir> LoweringContext<'_, 'hir> { attrs, target_span, target, - OmitDoc::Lower, |s| l.lower(s), |lint_id, span, kind| { self.delayed_lints.push(DelayedLint { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index daa663b8d1b6a..15d94530eecae 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -46,10 +46,6 @@ macro_rules! gate_multi { }}; } -pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) { - PostExpansionVisitor { sess, features }.visit_attribute(attr) -} - struct PostExpansionVisitor<'a> { sess: &'a Session, @@ -152,33 +148,9 @@ impl<'a> PostExpansionVisitor<'a> { } impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { - fn visit_attribute(&mut self, attr: &ast::Attribute) { - // Check unstable flavors of the `#[doc]` attribute. - if attr.has_name(sym::doc) { - for meta_item_inner in attr.meta_item_list().unwrap_or_default() { - macro_rules! gate_doc { ($($s:literal { $($name:ident => $feature:ident)* })*) => { - $($(if meta_item_inner.has_name(sym::$name) { - let msg = concat!("`#[doc(", stringify!($name), ")]` is ", $s); - gate!(self, $feature, attr.span, msg); - })*)* - }} - - gate_doc!( - "experimental" { - cfg => doc_cfg - auto_cfg => doc_cfg - masked => doc_masked - notable_trait => doc_notable_trait - } - "meant for internal use only" { - attribute => rustdoc_internals - keyword => rustdoc_internals - fake_variadic => rustdoc_internals - search_unbox => rustdoc_internals - } - ); - } - } + fn visit_attribute(&mut self, attr: &'a ast::Attribute) { + // Checked in attribute parsers, do NOT add checks here + visit::walk_attribute(self, attr) } fn visit_item(&mut self, i: &'a ast::Item) { diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 7b0df693debf5..e315d6abea395 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -5,10 +5,9 @@ use rustc_attr_ir::{ DocInline, HideOrShow, }; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, IndexEntry}; -use rustc_errors::{Applicability, msg}; +use rustc_errors::Applicability; use rustc_feature::AttributeStability; use rustc_lint_defs::builtin::{INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES}; -use rustc_session::diagnostics::feature_err; use rustc_span::{Span, Symbol, edition, sym}; use super::prelude::{ALL_TARGETS, AllowedTargets}; @@ -526,19 +525,15 @@ impl DocParser { } macro_rules! no_args_and_crate_level { ($ident: ident) => {{ - no_args_and_crate_level!($ident, |span| {}); - }}; - ($ident: ident, |$span:ident| $extra_validation:block) => {{ if let Err(span) = args.as_no_args() { expected_no_args(cx, span); return; } - let $span = path.span(); - if !check_attr_crate_level(cx, $span) { + let span = path.span(); + if !check_attr_crate_level(cx, span) { return; } - $extra_validation - self.attribute.$ident = Some($span); + self.attribute.$ident = Some(span); }}; } macro_rules! string_arg_and_crate_level { @@ -569,6 +564,12 @@ impl DocParser { self.attribute.$ident = Some((s, path.span())); }}; } + macro_rules! gated { + ($feature:ident $(,$notes:expr)*) => { + let stability = $crate::unstable!($feature $(, $notes)*); + cx.shared.cx.check_attribute_stability(&cx.attr_path, path.span(), stability); + }; + } match path.word_sym() { Some(sym::alias) => self.parse_alias(cx, path, args), @@ -583,37 +584,60 @@ impl DocParser { } Some(sym::inline) => self.parse_inline(cx, path, args, DocInline::Inline), Some(sym::no_inline) => self.parse_inline(cx, path, args, DocInline::NoInline), - Some(sym::masked) => no_args!(masked), - Some(sym::cfg) => self.parse_cfg(cx, args), - Some(sym::notable_trait) => no_args!(notable_trait), - Some(sym::keyword) => parse_keyword_and_attribute( - cx, - path, - args, - &mut self.attribute.keyword, - sym::keyword, - ), - Some(sym::attribute) => parse_keyword_and_attribute( - cx, - path, - args, - &mut self.attribute.attribute, - sym::attribute, - ), - Some(sym::fake_variadic) => no_args_and_not_crate_level!(fake_variadic), - Some(sym::search_unbox) => no_args_and_not_crate_level!(search_unbox), - Some(sym::rust_logo) => no_args_and_crate_level!(rust_logo, |span| { - if !cx.features().rustdoc_internals() { - feature_err( - cx.sess(), - sym::rustdoc_internals, - span, - msg!("the `#[doc(rust_logo)]` attribute is used for Rust branding"), - ) - .emit(); + Some(sym::masked) => { + gated!(doc_masked); + no_args!(masked) + } + Some(sym::cfg) => { + gated!(doc_cfg); + self.parse_cfg(cx, args) + } + Some(sym::notable_trait) => { + gated!(doc_notable_trait); + no_args!(notable_trait) + } + Some(sym::keyword) => { + gated!(rustdoc_internals); + parse_keyword_and_attribute( + cx, + path, + args, + &mut self.attribute.keyword, + sym::keyword, + ) + } + Some(sym::attribute) => { + gated!(rustdoc_internals); + parse_keyword_and_attribute( + cx, + path, + args, + &mut self.attribute.attribute, + sym::attribute, + ) + } + Some(sym::fake_variadic) => { + gated!(rustdoc_internals); + no_args_and_not_crate_level!(fake_variadic) + } + Some(sym::search_unbox) => { + gated!(rustdoc_internals); + no_args_and_not_crate_level!(search_unbox) + } + Some(sym::rust_logo) => { + // FIXME: Only feature gated at the crate level (!!) + if cx.target == Target::Crate { + gated!( + rustdoc_internals, + "the `#[doc(rust_logo)]` attribute is used for Rust branding" + ); } - }), - Some(sym::auto_cfg) => self.parse_auto_cfg(cx, path, args), + no_args_and_crate_level!(rust_logo) + } + Some(sym::auto_cfg) => { + gated!(doc_cfg); + self.parse_auto_cfg(cx, path, args) + } Some(sym::test) => { let Some(list) = args.as_list() else { cx.emit_lint( diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index dfda3dc722e1b..006219624e4fa 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -864,12 +864,6 @@ impl<'p, 'sess: 'p> DerefMut for SharedContext<'p, 'sess> { } } -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum OmitDoc { - Lower, - Skip, -} - #[derive(Copy, Clone, Debug)] pub enum ShouldEmit { /// The operations will emit errors, and lints, and errors are fatal. diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 602fa6f707055..aef7dd48ec664 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -23,7 +23,7 @@ use crate::context::{ use crate::diagnostics::ParsedDescription; use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::synthetic::SyntheticAttrState; -use crate::{AttributeTemplate, OmitDoc, ShouldEmit}; +use crate::{AttributeTemplate, ShouldEmit}; pub struct EmitAttribute( pub Box< @@ -161,7 +161,6 @@ impl<'sess> AttributeParser<'sess> { attrs, target_span, target, - OmitDoc::Skip, std::convert::identity, |lint_id, span, kind| { sess.psess.dyn_buffer_lint_sess(lint_id.lint, span, target_node_id, kind.0) @@ -310,14 +309,12 @@ impl<'sess> AttributeParser<'sess> { /// Parse a list of attributes. /// - /// `target_span` is the span of the thing this list of attributes is applied to, - /// and when `omit_doc` is set, doc attributes are filtered out. + /// `target_span` is the span of the thing this list of attributes is applied to. pub fn parse_attribute_list( &mut self, attrs: &[ast::Attribute], target_span: Span, target: Target, - omit_doc: OmitDoc, lower_span: impl Copy + Fn(Span) -> Span, mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute), ) -> Vec { @@ -335,23 +332,23 @@ impl<'sess> AttributeParser<'sess> { } } - // Sometimes, for example for `#![doc = include_str!("readme.md")]`, - // doc still contains a non-literal. You might say, when we're lowering attributes - // that's expanded right? But no, sometimes, when parsing attributes on macros, - // we already use the lowering logic and these are still there. So, when `omit_doc` - // is set we *also* want to ignore these. - let is_doc_attribute = attr.has_name(sym::doc); - if omit_doc == OmitDoc::Skip && is_doc_attribute { + fn is_doc_non_lit_expr(attr: &ast::Attribute) -> bool { + if !attr.has_name(sym::doc) { + return false; + } + let ast::AttrKind::Normal(n) = &attr.kind else { return false }; + let ast::AttrArgs::Eq { expr, .. } = &n.item.args else { return false }; + !matches!(expr.kind, ast::ExprKind::Lit(_)) + } + + // FIXME accidentally allowed on Stable Rust + if target == Target::MacroCall && is_doc_non_lit_expr(attr) { continue; } let attr_span = lower_span(attr.span); match &attr.kind { ast::AttrKind::DocComment(comment_kind, symbol) => { - if omit_doc == OmitDoc::Skip { - continue; - } - attributes.push(Attribute::Parsed(AttributeKind::DocComment { style: attr.style, kind: DocFragmentKind::Sugared(*comment_kind), @@ -407,7 +404,7 @@ impl<'sess> AttributeParser<'sess> { // bla // blob // a - if is_doc_attribute + if attr.has_name(sym::doc) && let ArgParser::NameValue(nv) = &args // If not a string key/value, it should emit an error, but to make // things simpler, it's handled in `DocParser` because it's simpler to diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 4b386f06a005c..3360791cdbc5f 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -116,7 +116,7 @@ pub use attributes::cfg::{ }; pub use attributes::cfg_select::*; pub use attributes::util::{is_builtin_attr, parse_version}; -pub use context::{OmitDoc, ShouldEmit}; +pub use context::ShouldEmit; pub use diagnostics::ParsedDescription; pub use interface::{AttributeParser, EmitAttribute}; pub use rustc_parse::parser::Recovery; diff --git a/compiler/rustc_attr_parsing/src/stability.rs b/compiler/rustc_attr_parsing/src/stability.rs index b0ab2649c4078..9ae8287812542 100644 --- a/compiler/rustc_attr_parsing/src/stability.rs +++ b/compiler/rustc_attr_parsing/src/stability.rs @@ -53,10 +53,24 @@ impl<'sess> AttributeParser<'sess> { sym::prelude_import => ("the `prelude_import` attribute is for use by rustc only".to_string(), &[]), sym::profiler_runtime => ("the `profiler_runtime` attribute is used to identify the `profiler_builtins` crate which contains the profiler runtime and will never be stable".to_string(), &[]), sym::thread_local => ("the `thread_local` attribute is an experimental feature, and does not currently handle destructors".to_string(), &[]), + sym::rustdoc_internals => ("this subset of the `doc` attribute is meant for internal use only".to_string(), &[]), + sym::doc_notable_trait => ("the `doc(notable_trait)` attribute is experimental".to_string(), &[]), + sym::doc_cfg => ("the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental".to_string(), &[]), + sym::doc_masked => ("the `doc(masked)` attribute is experimental".to_string(), &[]), _ => (format!("the `{attr_path}` attribute is an experimental feature"), &[]), }; - let mut diag = feature_err(self.sess, gate_name, attr_path.span, explain); + // For unstable subsets of an attribute, point at that + let err_span = if matches!( + gate_name, + sym::rustdoc_internals | sym::doc_notable_trait | sym::doc_cfg | sym::doc_masked + ) { + attr_span + } else { + attr_path.span + }; + + let mut diag = feature_err(self.sess, gate_name, err_span, explain); // Remove the suggestion for `#![feature(staged_api)]` as these attributes are currently // not usable outside std. If we do ever expose `#[stable]` etc under a different feature diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 80353e4c8dba4..0f216aa9f68df 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -10,7 +10,6 @@ doctest = false [dependencies] # tidy-alphabetical-start rustc_ast = { path = "../rustc_ast" } -rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_attr_ir = { path = "../rustc_attr_ir" } rustc_attr_parsing = { path = "../rustc_attr_parsing" } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e4d84847db55d..024ee2871b125 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -2245,15 +2245,13 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { attr } - // Detect use of feature-gated or invalid attributes on macro invocations + // Run attributes through the attribute parser // since they will not be detected after macro expansion. fn check_attributes(&self, attrs: &[ast::Attribute], call: &ast::MacCall) { use SyntheticAttr::*; - let features = self.cx.ecfg.features; let mut attrs = attrs.iter().peekable(); let mut span: Option = None; while let Some(attr) = attrs.next() { - rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features); validate_attr::check_attr(&self.cx.sess.psess, attr); AttributeParser::parse_limited_all( self.cx.sess, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index b45a9d1a6ec54..29b1773ddc5ca 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -3,7 +3,7 @@ use std::mem; use rustc_ast::visit::FnKind; use rustc_ast::*; use rustc_attr_parsing as attr; -use rustc_attr_parsing::{AttributeParser, OmitDoc, ShouldEmit}; +use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_expand::expand::AstFragment; use rustc_hir as hir; use rustc_hir::Target; @@ -187,7 +187,6 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { &i.attrs, i.span, Target::MacroDef, - OmitDoc::Skip, std::convert::identity, |_lint_id, _span, _kind| { // FIXME(jdonszelmann): emit lints here properly diff --git a/tests/rustdoc-ui/feature-gate-doc_cfg.stderr b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr index 68a86c1abb777..db2f3b5737549 100644 --- a/tests/rustdoc-ui/feature-gate-doc_cfg.stderr +++ b/tests/rustdoc-ui/feature-gate-doc_cfg.stderr @@ -1,58 +1,58 @@ -error[E0658]: `#[doc(auto_cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:1:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:1:8 | LL | #![doc(auto_cfg)] - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(auto_cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:2:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:2:8 | LL | #![doc(auto_cfg(false))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(auto_cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:3:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:3:8 | LL | #![doc(auto_cfg(true))] - | ^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(auto_cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:4:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:4:8 | LL | #![doc(auto_cfg(hide(feature = "solecism")))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(auto_cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:5:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:5:8 | LL | #![doc(auto_cfg(show(feature = "bla")))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:6:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:6:8 | LL | #![doc(cfg(feature = "solecism"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable diff --git a/tests/ui/attributes/attr-on-mac-call.rs b/tests/ui/attributes/attr-on-mac-call.rs index 7b30ec810ff81..618e583d0af5e 100644 --- a/tests/ui/attributes/attr-on-mac-call.rs +++ b/tests/ui/attributes/attr-on-mac-call.rs @@ -110,4 +110,15 @@ fn main() { #[register_tool(xyz)] //~^ ERROR crate-level attribute should be an inner attribute unreachable!(); + #[deprecated = concat!("woah", "dude")] + //~^ ERROR attribute value must be a literal + #[doc = concat!("woah", "dude")] + unreachable!(); + #[doc = { + let a = 1; + let b = 1; + let sum = a + b; + assert_eq!(sum, 2); + }] + unreachable!(); } diff --git a/tests/ui/attributes/attr-on-mac-call.stderr b/tests/ui/attributes/attr-on-mac-call.stderr index 3454998af2922..aa158bee22768 100644 --- a/tests/ui/attributes/attr-on-mac-call.stderr +++ b/tests/ui/attributes/attr-on-mac-call.stderr @@ -38,6 +38,12 @@ note: this attribute does not have an `!`, which means it is applied to this mac LL | unreachable!(); | ^^^^^^^^^^^^^^ +error: attribute value must be a literal + --> $DIR/attr-on-mac-call.rs:113:20 + | +LL | #[deprecated = concat!("woah", "dude")] + | ^^^^^^^^^^^^^^^^^^^^^^^ + warning: the `export_name` attribute cannot be used on macro calls --> $DIR/attr-on-mac-call.rs:8:7 | @@ -341,6 +347,6 @@ LL | #[repr(Rust)] = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: placing this attribute on a macro invocation does nothing even if the macro expands to what would be a valid target for the attribute -error: aborting due to 4 previous errors; 30 warnings emitted +error: aborting due to 5 previous errors; 30 warnings emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/doc-rust-logo.rs b/tests/ui/feature-gates/doc-rust-logo.rs index e6a58512944bb..08857cc778f5b 100644 --- a/tests/ui/feature-gates/doc-rust-logo.rs +++ b/tests/ui/feature-gates/doc-rust-logo.rs @@ -1,5 +1,7 @@ #![doc(rust_logo)] -//~^ ERROR the `#[doc(rust_logo)]` attribute is used for Rust branding +//~^ ERROR this subset of the `doc` attribute is meant for internal use only //! This is not an official rust crate +#[doc(rust_logo)] +//~^ WARN this attribute can only be applied at the crate level fn main() {} diff --git a/tests/ui/feature-gates/doc-rust-logo.stderr b/tests/ui/feature-gates/doc-rust-logo.stderr index 5c64652667ed8..f31837be284d1 100644 --- a/tests/ui/feature-gates/doc-rust-logo.stderr +++ b/tests/ui/feature-gates/doc-rust-logo.stderr @@ -1,4 +1,4 @@ -error[E0658]: the `#[doc(rust_logo)]` attribute is used for Rust branding +error[E0658]: this subset of the `doc` attribute is meant for internal use only --> $DIR/doc-rust-logo.rs:1:8 | LL | #![doc(rust_logo)] @@ -7,7 +7,17 @@ LL | #![doc(rust_logo)] = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: the `#[doc(rust_logo)]` attribute is used for Rust branding -error: aborting due to 1 previous error +warning: this attribute can only be applied at the crate level + --> $DIR/doc-rust-logo.rs:5:7 + | +LL | #[doc(rust_logo)] + | ^^^^^^^^^ + | + = note: read for more information + = note: `#[warn(invalid_doc_attributes)]` on by default + +error: aborting due to 1 previous error; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-doc_cfg.stderr b/tests/ui/feature-gates/feature-gate-doc_cfg.stderr index 5315aaeeb3edb..cb2898960472a 100644 --- a/tests/ui/feature-gates/feature-gate-doc_cfg.stderr +++ b/tests/ui/feature-gates/feature-gate-doc_cfg.stderr @@ -1,8 +1,8 @@ -error[E0658]: `#[doc(cfg)]` is experimental - --> $DIR/feature-gate-doc_cfg.rs:1:1 +error[E0658]: the `doc(cfg)` and `doc(auto_cfg)` attributes are experimental + --> $DIR/feature-gate-doc_cfg.rs:1:7 | LL | #[doc(cfg(unix))] - | ^^^^^^^^^^^^^^^^^ + | ^^^ | = note: see issue #43781 for more information = help: add `#![feature(doc_cfg)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-doc_masked.rs b/tests/ui/feature-gates/feature-gate-doc_masked.rs index bde3af6b594c2..a2776cb696c49 100644 --- a/tests/ui/feature-gates/feature-gate-doc_masked.rs +++ b/tests/ui/feature-gates/feature-gate-doc_masked.rs @@ -1,4 +1,8 @@ -#[doc(masked)] //~ ERROR: `#[doc(masked)]` is experimental +#[doc(masked)] //~ ERROR the `doc(masked)` attribute is experimental extern crate std as realstd; -fn main() {} +fn main() { + #[doc(masked)] + //~^ ERROR the `doc(masked)` attribute is experimental [E0658] + println!(); +} diff --git a/tests/ui/feature-gates/feature-gate-doc_masked.stderr b/tests/ui/feature-gates/feature-gate-doc_masked.stderr index 10607a19757cb..8f1b03e50325c 100644 --- a/tests/ui/feature-gates/feature-gate-doc_masked.stderr +++ b/tests/ui/feature-gates/feature-gate-doc_masked.stderr @@ -1,13 +1,23 @@ -error[E0658]: `#[doc(masked)]` is experimental - --> $DIR/feature-gate-doc_masked.rs:1:1 +error[E0658]: the `doc(masked)` attribute is experimental + --> $DIR/feature-gate-doc_masked.rs:5:11 + | +LL | #[doc(masked)] + | ^^^^^^ + | + = note: see issue #44027 for more information + = help: add `#![feature(doc_masked)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `doc(masked)` attribute is experimental + --> $DIR/feature-gate-doc_masked.rs:1:7 | LL | #[doc(masked)] - | ^^^^^^^^^^^^^^ + | ^^^^^^ | = note: see issue #44027 for more information = help: add `#![feature(doc_masked)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs index 7f3392eadadb3..1bc1028b9e0ef 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.rs @@ -1,4 +1,8 @@ -#[doc(notable_trait)] //~ ERROR: `#[doc(notable_trait)]` is experimental +#[doc(notable_trait)] //~ ERROR the `doc(notable_trait)` attribute is experimental trait SomeTrait {} -fn main() {} +fn main() { + #[doc(notable_trait)] + //~^ ERROR the `doc(notable_trait)` attribute is experimental [E0658] + println!(); +} diff --git a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr index 1b40b9ac18a8f..632c91b929185 100644 --- a/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr +++ b/tests/ui/feature-gates/feature-gate-doc_notable_trait.stderr @@ -1,13 +1,23 @@ -error[E0658]: `#[doc(notable_trait)]` is experimental - --> $DIR/feature-gate-doc_notable_trait.rs:1:1 +error[E0658]: the `doc(notable_trait)` attribute is experimental + --> $DIR/feature-gate-doc_notable_trait.rs:5:11 + | +LL | #[doc(notable_trait)] + | ^^^^^^^^^^^^^ + | + = note: see issue #45040 for more information + = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: the `doc(notable_trait)` attribute is experimental + --> $DIR/feature-gate-doc_notable_trait.rs:1:7 | LL | #[doc(notable_trait)] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | = note: see issue #45040 for more information = help: add `#![feature(doc_notable_trait)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs index 7aa6dcbd5daac..e39221a204ca8 100644 --- a/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs +++ b/tests/ui/feature-gates/feature-gate-rustdoc_internals.rs @@ -1,17 +1,29 @@ -#[doc(keyword = "match")] //~ ERROR: `#[doc(keyword)]` is meant for internal use only +#[doc(keyword = "match")] //~ ERROR: this subset of the `doc` attribute is meant for internal use only /// wonderful const _: () = (); -#[doc(attribute = "repr")] //~ ERROR: `#[doc(attribute)]` is meant for internal use only +#[doc(attribute = "repr")] //~ ERROR this subset of the `doc` attribute is meant for internal use only /// wonderful const _: () = (); trait Mine {} -#[doc(fake_variadic)] //~ ERROR: `#[doc(fake_variadic)]` is meant for internal use only +#[doc(fake_variadic)] //~ ERROR this subset of the `doc` attribute is meant for internal use only impl Mine for (T,) {} -#[doc(search_unbox)] //~ ERROR: `#[doc(search_unbox)]` is meant for internal use only +#[doc(search_unbox)] //~ ERROR this subset of the `doc` attribute is meant for internal use only struct Wrap (T); -fn main() {} +fn main() { + #[doc(search_unbox)] + //~^ ERROR this subset of the `doc` attribute is meant for internal use only [E0658] + println!(); + + #[doc(fake_variadic)] + //~^ ERROR this subset of the `doc` attribute is meant for internal use only [E0658] + println!(); + + #[doc(attribute = "repr")] + //~^ ERROR this subset of the `doc` attribute is meant for internal use only [E0658] + println!(); +} diff --git a/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr b/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr index 5a6d4d3b45e0f..ab542eb85bacf 100644 --- a/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr +++ b/tests/ui/feature-gates/feature-gate-rustdoc_internals.stderr @@ -1,43 +1,73 @@ -error[E0658]: `#[doc(keyword)]` is meant for internal use only - --> $DIR/feature-gate-rustdoc_internals.rs:1:1 +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:18:11 + | +LL | #[doc(search_unbox)] + | ^^^^^^^^^^^^ + | + = note: see issue #90418 for more information + = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:22:11 + | +LL | #[doc(fake_variadic)] + | ^^^^^^^^^^^^^ + | + = note: see issue #90418 for more information + = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:26:11 + | +LL | #[doc(attribute = "repr")] + | ^^^^^^^^^ + | + = note: see issue #90418 for more information + = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:1:7 | LL | #[doc(keyword = "match")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^ | = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(attribute)]` is meant for internal use only - --> $DIR/feature-gate-rustdoc_internals.rs:5:1 +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:5:7 | LL | #[doc(attribute = "repr")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^ | = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(fake_variadic)]` is meant for internal use only - --> $DIR/feature-gate-rustdoc_internals.rs:11:1 +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:11:7 | LL | #[doc(fake_variadic)] - | ^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ | = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0658]: `#[doc(search_unbox)]` is meant for internal use only - --> $DIR/feature-gate-rustdoc_internals.rs:14:1 +error[E0658]: this subset of the `doc` attribute is meant for internal use only + --> $DIR/feature-gate-rustdoc_internals.rs:14:7 | LL | #[doc(search_unbox)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = note: see issue #90418 for more information = help: add `#![feature(rustdoc_internals)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 4 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/lint/unused/unused-doc-comments-for-macros.rs b/tests/ui/lint/unused/unused-doc-comments-for-macros.rs index 0a95b79988894..62c4fe82348cc 100644 --- a/tests/ui/lint/unused/unused-doc-comments-for-macros.rs +++ b/tests/ui/lint/unused/unused-doc-comments-for-macros.rs @@ -16,11 +16,12 @@ fn main() { foo!(); // Even invalid doc attributes should emit the warning. - #[doc = { //~ ERROR: unused doc comment + #[doc = { let a = 1; let b = 1; let sum = a + b; assert_eq!(sum, 2); }] + //~^^^^^^ ERROR: unused doc comment foo!(); } From f1f423d070d22d7e8e0703ae281472941283a602 Mon Sep 17 00:00:00 2001 From: Nia Deckers Date: Mon, 10 Aug 2026 01:07:55 +0200 Subject: [PATCH 2/8] tidy: enforce documented unsafe in alloc safety comments in alloc this was pain and it's not even half i think oepsje woepsje --- library/alloc/src/alloc.rs | 8 + library/alloc/src/boxed.rs | 69 +++-- library/alloc/src/boxed/convert.rs | 19 +- library/alloc/src/boxed/thin.rs | 71 ++--- .../alloc/src/collections/binary_heap/mod.rs | 33 ++- library/alloc/src/collections/btree/map.rs | 20 +- library/alloc/src/collections/btree/mem.rs | 2 + .../alloc/src/collections/btree/navigate.rs | 30 ++- library/alloc/src/collections/btree/node.rs | 97 ++++++- library/alloc/src/collections/btree/remove.rs | 3 + library/alloc/src/collections/btree/search.rs | 13 + library/alloc/src/collections/btree/set.rs | 5 + library/alloc/src/collections/linked_list.rs | 57 +++- .../alloc/src/collections/vec_deque/drain.rs | 30 ++- .../src/collections/vec_deque/extract_if.rs | 2 +- .../alloc/src/collections/vec_deque/iter.rs | 2 +- .../src/collections/vec_deque/iter_mut.rs | 2 +- .../alloc/src/collections/vec_deque/mod.rs | 243 +++++++++++------- .../src/collections/vec_deque/spec_extend.rs | 31 ++- .../alloc/src/collections/vec_deque/splice.rs | 5 + library/alloc/src/ffi/c_str.rs | 41 +-- library/alloc/src/io/buf_read.rs | 1 + library/alloc/src/io/buffered/bufreader.rs | 1 + library/alloc/src/io/buffered/bufwriter.rs | 1 + library/alloc/src/io/cursor.rs | 9 +- library/alloc/src/io/error.rs | 5 +- library/alloc/src/io/read.rs | 3 + library/alloc/src/io/util.rs | 2 +- library/alloc/src/raw_vec/mod.rs | 22 +- library/alloc/src/rc.rs | 102 +++++++- library/alloc/src/slice.rs | 9 +- library/alloc/src/str.rs | 34 ++- library/alloc/src/string.rs | 16 +- library/alloc/src/sync.rs | 106 +++++++- library/alloc/src/task.rs | 8 + library/alloc/src/vec/drain.rs | 7 + library/alloc/src/vec/extract_if.rs | 6 +- library/alloc/src/vec/in_place_collect.rs | 9 +- library/alloc/src/vec/in_place_drop.rs | 3 + library/alloc/src/vec/into_iter.rs | 42 +-- library/alloc/src/vec/is_zero.rs | 1 + library/alloc/src/vec/mod.rs | 68 +++-- library/alloc/src/vec/spec_extend.rs | 2 + library/alloc/src/vec/spec_from_elem.rs | 2 + library/alloc/src/vec/spec_from_iter.rs | 1 + .../alloc/src/vec/spec_from_iter_nested.rs | 2 +- library/alloc/src/vec/splice.rs | 5 + library/alloc/src/vec/sve_retain.rs | 7 +- src/tools/tidy/src/style.rs | 7 +- 49 files changed, 947 insertions(+), 317 deletions(-) diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index c0d7d8b605227..b88d2391bfb61 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -122,6 +122,7 @@ unsafe impl core::alloc::StaticAllocator for Global {} #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: Upheld by caller. unsafe { // Make sure we don't accidentally allow omitting the allocator shim in // stable code until it is actually stabilized. @@ -165,6 +166,7 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: Upheld by caller. unsafe { dealloc_nonnull(NonNull::new_unchecked(ptr), layout) } } @@ -172,6 +174,7 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn dealloc_nonnull(ptr: NonNull, layout: Layout) { + // SAFETY: Upheld by caller. unsafe { __rust_dealloc(ptr, layout.size(), layout.alignment()) } } @@ -218,6 +221,7 @@ unsafe fn dealloc_nonnull(ptr: NonNull, layout: Layout) { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: Upheld by caller. unsafe { realloc_nonnull(NonNull::new_unchecked(ptr), layout, new_size) } } @@ -225,6 +229,7 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces unsafe fn realloc_nonnull(ptr: NonNull, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: Upheld by caller. unsafe { __rust_realloc(ptr, layout.size(), layout.alignment(), new_size) } } @@ -282,6 +287,7 @@ unsafe fn realloc_nonnull(ptr: NonNull, layout: Layout, new_size: usize) -> #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: Upheld by caller. unsafe { // Make sure we don't accidentally allow omitting the allocator shim in // stable code until it is actually stabilized. @@ -525,6 +531,7 @@ impl Global { cmp::min(old_layout.size(), new_layout.size()), ); } + // SAFETY: Caller ensures the ptr & layout are correct. unsafe { self.deallocate_impl(ptr, old_layout); } @@ -639,6 +646,7 @@ pub const fn handle_alloc_error(layout: Layout) -> ! { #[inline] fn rt_error(layout: Layout) -> ! { + // SAFETY: Safe to call; we control this function. unsafe { __rust_alloc_error_handler(layout.size(), layout.align()); } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 613791448eb5b..d8ea1de4da4b4 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -266,6 +266,8 @@ const fn box_new_uninit(layout: Layout) -> *mut u8 { pub const fn box_assume_init_into_vec_unsafe( b: Box>, ) -> crate::vec::Vec { + // SAFETY: Technically not, but this can't be + // called stably except in ways we control. unsafe { (b.assume_init() as Box<[T]>).into_vec() } } @@ -452,6 +454,7 @@ impl Box { { let mut boxed = Self::new_uninit_in(alloc); boxed.write(x); + // SAFETY: Initialised by the above. unsafe { boxed.assume_init() } } @@ -478,6 +481,7 @@ impl Box { { let mut boxed = Self::try_new_uninit_in(alloc)?; boxed.write(x); + // SAFETY: Initialised by the above. unsafe { Ok(boxed.assume_init()) } } @@ -542,6 +546,7 @@ impl Box { let layout = Layout::new::>(); alloc.allocate(layout)?.cast() }; + // SAFETY: Pointer is nonnull and matches the allocator. unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } } @@ -614,6 +619,7 @@ impl Box { let layout = Layout::new::>(); alloc.allocate_zeroed(layout)?.cast() }; + // SAFETY: Pointer is nonnull and matches the allocator. unsafe { Ok(Box::from_raw_in(ptr.as_ptr(), alloc)) } } @@ -650,6 +656,7 @@ impl Box { #[unstable(feature = "box_into_boxed_slice", issue = "71582")] pub fn into_boxed_slice(boxed: Self) -> Box<[T], A> { let (raw, alloc) = Box::into_raw_with_allocator(boxed); + // SAFETY: A pointer to T is also a valid pointer to [T; 1]. unsafe { Box::from_raw_in(raw as *mut [T; 1], alloc) } } @@ -693,6 +700,8 @@ impl Box { /// ``` #[unstable(feature = "box_take", issue = "147212")] pub fn take(boxed: Self) -> (T, Box, A>) { + // SAFETY: Reading out an initialised value & leaving behind a + // box with uninit contents. unsafe { let (raw, alloc) = Box::into_non_null_with_allocator(boxed); let value = raw.read(); @@ -724,9 +733,11 @@ impl Box { let (value, allocation) = Box::take(this); let (raw, alloc) = Box::into_non_null_with_allocator(allocation); if size_of::() == size_of::() && align_of::() == align_of::() { + // ignore-tidy-undocumented-unsafe let allocation = unsafe { Box::from_non_null_in(raw.cast::>(), alloc) }; Box::write(allocation, f(value)) } else { + // ignore-tidy-undocumented-unsafe unsafe { alloc.deallocate(raw.cast(), Layout::for_value(&value)) } Box::new_in(f(value), alloc) } @@ -764,9 +775,11 @@ impl Box { let (raw, alloc) = Box::into_non_null_with_allocator(allocation); if size_of::() == size_of::() && align_of::() == align_of::() { let allocation = + // ignore-tidy-undocumented-unsafe unsafe { Box::from_non_null_in(raw.cast::>(), alloc) }; try { Box::write(allocation, f(value)?) } } else { + // ignore-tidy-undocumented-unsafe unsafe { alloc.deallocate(raw.cast(), Layout::for_value(&value)) } try { Box::new_in(f(value)?, alloc) } } @@ -865,7 +878,7 @@ impl Box { impl<'a, A: Allocator> Drop for DeallocDropGuard<'a, A> { fn drop(&mut self) { let &mut DeallocDropGuard(layout, alloc, ptr) = self; - // Safety: `ptr` was allocated by `*alloc` with layout `layout` + // SAFETY: `ptr` was allocated by `*alloc` with layout `layout` unsafe { alloc.deallocate(ptr, layout); } @@ -880,7 +893,7 @@ impl Box { (ptr, Some(DeallocDropGuard(layout, &alloc, ptr))) }; let ptr = ptr.as_ptr(); - // Safety: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`, + // SAFETY: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`, // and is valid for writes for `size_of_val(src)`. // If this panics, then `guard` will deallocate for us (if allocation occuured) unsafe { @@ -888,7 +901,7 @@ impl Box { } // Defuse the deallocate guard core::mem::forget(guard); - // Safety: We just initialized `*ptr` as a clone of `src` + // SAFETY: We just initialized `*ptr` as a clone of `src` Ok(unsafe { Box::from_raw_in(ptr.with_metadata_of(src), alloc) }) } } @@ -912,6 +925,7 @@ impl Box<[T]> { #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Box<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { RawVec::with_capacity(len).into_box(len) } } @@ -935,6 +949,7 @@ impl Box<[T]> { #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Box<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { RawVec::with_capacity_zeroed(len).into_box(len) } } @@ -968,6 +983,7 @@ impl Box<[T]> { }; Global.allocate(layout)?.cast() }; + // ignore-tidy-undocumented-unsafe unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) } } @@ -1002,6 +1018,7 @@ impl Box<[T]> { }; Global.allocate_zeroed(layout)?.cast() }; + // ignore-tidy-undocumented-unsafe unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) } } } @@ -1029,6 +1046,7 @@ impl Box<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { RawVec::with_capacity_in(len, alloc).into_box(len) } } @@ -1056,6 +1074,7 @@ impl Box<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[must_use] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Box<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { RawVec::with_capacity_zeroed_in(len, alloc).into_box(len) } } @@ -1094,6 +1113,7 @@ impl Box<[T], A> { }; alloc.allocate(layout)?.cast() }; + // ignore-tidy-undocumented-unsafe unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } } @@ -1133,6 +1153,7 @@ impl Box<[T], A> { }; alloc.allocate_zeroed(layout)?.cast() }; + // ignore-tidy-undocumented-unsafe unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, alloc).into_box(len)) } } @@ -1229,6 +1250,7 @@ impl Box, A> { #[stable(feature = "box_uninit_write", since = "1.87.0")] #[inline] pub fn write(mut boxed: Self, value: T) -> Box { + // SAFETY: Writing initialises the boxed value. unsafe { (*boxed).write(value); boxed.assume_init() @@ -1265,6 +1287,7 @@ impl Box<[mem::MaybeUninit], A> { #[inline] pub unsafe fn assume_init(self) -> Box<[T], A> { let (raw, alloc) = Box::into_raw_with_allocator(self); + // SAFETY: Upheld by caller. unsafe { Box::from_raw_in(raw as *mut [T], alloc) } } } @@ -1318,6 +1341,7 @@ impl Box { #[inline] #[must_use = "call `drop(Box::from_raw(ptr))` if you intend to drop the `Box`"] pub unsafe fn from_raw(raw: *mut T) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_raw_in(raw, Global) } } @@ -1370,6 +1394,7 @@ impl Box { #[inline] #[must_use = "call `drop(Box::from_non_null(ptr))` if you intend to drop the `Box`"] pub unsafe fn from_non_null(ptr: NonNull) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_raw(ptr.as_ptr()) } } @@ -1549,6 +1574,7 @@ impl Box { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn from_raw_in(raw: *mut T, alloc: A) -> Self { + // SAFETY: Upheld by caller. Box(unsafe { Unique::new_unchecked(raw) }, alloc) } @@ -1667,6 +1693,7 @@ impl Box { // In case `A` *is* `Global`, this does not quite have the right behavior; `into_raw` // works around that. let ptr = &raw mut **b; + // SAFETY: See above. let alloc = unsafe { ptr::read(&b.1) }; (ptr, alloc) } @@ -1734,6 +1761,7 @@ impl Box { #[doc(hidden)] pub fn into_unique(b: Self) -> (Unique, A) { let (ptr, alloc) = Box::into_raw_with_allocator(b); + // SAFETY: Pointer is valid and unique. unsafe { (Unique::from(&mut *ptr), alloc) } } @@ -1933,6 +1961,7 @@ impl Box { { let (ptr, alloc) = Box::into_raw_with_allocator(b); mem::forget(alloc); + // SAFETY: Pointer is valid and unique. unsafe { &mut *ptr } } @@ -1971,9 +2000,9 @@ impl Box { where A: StaticAllocator, { - // It's not possible to move or replace the insides of a `Pin>` - // when `T: !Unpin`, so it's safe to pin it directly without any - // additional requirements. + // SAFETY: It's not possible to move or replace the insides of a + // `Pin>` when `T: !Unpin`, so it's safe to pin it directly + // without any additional requirements. unsafe { Pin::new_unchecked(boxed) } } } @@ -1986,6 +2015,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Box { let ptr = self.0; + // ignore-tidy-undocumented-unsafe unsafe { let layout = Layout::for_value_raw(ptr.as_ptr()); if layout.size() != 0 { @@ -2002,19 +2032,18 @@ impl Default for Box { #[inline] fn default() -> Self { let mut x: Box> = Box::new_uninit(); - unsafe { - // SAFETY: `x` is valid for writing and has the same layout as `T`. - // If `T::default()` panics, dropping `x` will just deallocate the Box as `MaybeUninit` - // does not have a destructor. - // - // We use `ptr::write` as `MaybeUninit::write` creates - // extra stack copies of `T` in debug mode. - // - // See https://github.com/rust-lang/rust/issues/136043 for more context. - ptr::write(&raw mut *x as *mut T, T::default()); - // SAFETY: `x` was just initialized above. - x.assume_init() - } + + // SAFETY: `x` is valid for writing and has the same layout as `T`. + // If `T::default()` panics, dropping `x` will just deallocate the Box as `MaybeUninit` + // does not have a destructor. + // + // We use `ptr::write` as `MaybeUninit::write` creates + // extra stack copies of `T` in debug mode. + // + // See https://github.com/rust-lang/rust/issues/136043 for more context. + unsafe { ptr::write(&raw mut *x as *mut T, T::default()) }; + // SAFETY: `x` was just initialized above. + unsafe { x.assume_init() } } } @@ -2079,6 +2108,7 @@ impl Clone for Box { fn clone(&self) -> Self { // Pre-allocate memory to allow writing the cloned value directly. let mut boxed = Self::new_uninit_in(self.1.clone()); + // SAFETY: Destination pointer is valid and will then become initialised. unsafe { (**self).clone_to_uninit(boxed.as_mut_ptr().cast()); boxed.assume_init() @@ -2148,6 +2178,7 @@ impl Clone for Box<[T], A> { impl Clone for Box { fn clone(&self) -> Self { let buf = Box::clone_from_ref_in(self.as_bytes(), self.1.clone()); + // SAFETY: We know the [u8] is a valid str. unsafe { from_boxed_utf8_unchecked_in(buf) } } } diff --git a/library/alloc/src/boxed/convert.rs b/library/alloc/src/boxed/convert.rs index 4f5dd89e6e3b3..f5c16dc7e7040 100644 --- a/library/alloc/src/boxed/convert.rs +++ b/library/alloc/src/boxed/convert.rs @@ -217,6 +217,7 @@ impl From> for Box<[u8], A> { #[inline] fn from(s: Box) -> Self { let (raw, alloc) = Box::into_raw_with_allocator(s); + // SAFETY: All `str`s are also valid if reinterpreted as `[u8]`s. unsafe { Box::from_raw_in(raw as *mut [u8], alloc) } } } @@ -270,6 +271,7 @@ impl TryFrom> for Box<[T; N]> { /// `boxed_slice.len()` does not equal `N`. fn try_from(boxed_slice: Box<[T]>) -> Result { if boxed_slice.len() == N { + // SAFETY: Checked length. Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) }) } else { Err(boxed_slice) @@ -303,6 +305,7 @@ impl TryFrom> for Box<[T; N]> { fn try_from(vec: Vec) -> Result { if vec.len() == N { let boxed_slice = vec.into_boxed_slice(); + // SAFETY: Checked length. Ok(unsafe { boxed_slice_as_array_unchecked(boxed_slice) }) } else { Err(vec) @@ -331,6 +334,7 @@ impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn downcast(self) -> Result, Self> { + // SAFETY: Check ensures the type is correct. if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } } @@ -362,6 +366,7 @@ impl Box { #[unstable(feature = "downcast_unchecked", issue = "90850")] pub unsafe fn downcast_unchecked(self) -> Box { debug_assert!(self.is::()); + // SAFETY: Caller ensures the type is correct. unsafe { let (raw, alloc): (*mut dyn Any, _) = Box::into_raw_with_allocator(self); Box::from_raw_in(raw as *mut T, alloc) @@ -390,6 +395,7 @@ impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn downcast(self) -> Result, Self> { + // SAFETY: Check ensures the type is correct. if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } } @@ -421,6 +427,7 @@ impl Box { #[unstable(feature = "downcast_unchecked", issue = "90850")] pub unsafe fn downcast_unchecked(self) -> Box { debug_assert!(self.is::()); + // SAFETY: Caller ensures the type is correct. unsafe { let (raw, alloc): (*mut (dyn Any + Send), _) = Box::into_raw_with_allocator(self); Box::from_raw_in(raw as *mut T, alloc) @@ -449,6 +456,7 @@ impl Box { #[inline] #[stable(feature = "box_send_sync_any_downcast", since = "1.51.0")] pub fn downcast(self) -> Result, Self> { + // SAFETY: Check ensures the type is correct. if self.is::() { unsafe { Ok(self.downcast_unchecked::()) } } else { Err(self) } } @@ -480,6 +488,7 @@ impl Box { #[unstable(feature = "downcast_unchecked", issue = "90850")] pub unsafe fn downcast_unchecked(self) -> Box { debug_assert!(self.is::()); + // SAFETY: Caller ensures the type is correct. unsafe { let (raw, alloc): (*mut (dyn Any + Send + Sync), _) = Box::into_raw_with_allocator(self); @@ -709,6 +718,7 @@ impl dyn Error { #[rustc_allow_incoherent_impl] pub fn downcast(self: Box) -> Result, Box> { if self.is::() { + // SAFETY: Check ensures the type is correct. unsafe { let raw: *mut dyn Error = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T)) @@ -726,10 +736,9 @@ impl dyn Error + Send { #[rustc_allow_incoherent_impl] pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; - ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send` marker. - mem::transmute::, Box>(s) - }) + ::downcast(err) + // SAFETY: Reapplying the `Send` marker we already know to hold. + .map_err(|s| unsafe { mem::transmute::, Box>(s) }) } } @@ -740,8 +749,8 @@ impl dyn Error + Send + Sync { #[rustc_allow_incoherent_impl] pub fn downcast(self: Box) -> Result, Box> { let err: Box = self; + // SAFETY: Reapplying the `Send` and `Sync` markers we already know to hold. ::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` markers. mem::transmute::, Box>(s) }) } diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 22c3d89e3ccdb..c78fed424c1c4 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -146,6 +146,7 @@ impl Deref for ThinBox { let value = self.data(); let metadata = self.meta(); let pointer = ptr::from_raw_parts(value as *const (), metadata); + // SAFETY: &ThinBox is also a valid pointer for T. unsafe { &*pointer } } } @@ -156,6 +157,7 @@ impl DerefMut for ThinBox { let value = self.data(); let metadata = self.meta(); let pointer = ptr::from_raw_parts_mut::(value as *mut (), metadata); + // SAFETY: &mut ThinBox is also a valid pointer for T. unsafe { &mut *pointer } } } @@ -163,6 +165,7 @@ impl DerefMut for ThinBox { #[unstable(feature = "thin_box", issue = "92791")] impl Drop for ThinBox { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { let value = self.deref_mut(); let value = value as *mut T; @@ -174,8 +177,7 @@ impl Drop for ThinBox { #[unstable(feature = "thin_box", issue = "92791")] impl ThinBox { fn meta(&self) -> ::Metadata { - // Safety: - // - NonNull and valid. + // SAFETY: NonNull and valid. unsafe { *self.with_header().header() } } @@ -238,6 +240,7 @@ impl WithHeader { alloc::handle_alloc_error(Layout::new::<()>()); }; + // ignore-tidy-undocumented-unsafe unsafe { // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so // we use `layout.dangling()` for this case, which should have a valid @@ -275,6 +278,7 @@ impl WithHeader { return Err(core::alloc::AllocError); }; + // ignore-tidy-undocumented-unsafe unsafe { // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so // we use `layout.dangling()` for this case, which should have a valid @@ -330,26 +334,26 @@ impl WithHeader { let alloc_size = max(align_of::(), size_of::<::Metadata>()); - unsafe { - // SAFETY: align is power of two because it is the maximum of two alignments. - let alloc: *mut u8 = const_allocate(alloc_size, alloc_align); + // SAFETY: align is power of two because it is the maximum of two alignments. + let alloc: *mut u8 = unsafe { const_allocate(alloc_size, alloc_align) }; - let metadata_offset = - alloc_size.checked_sub(size_of::<::Metadata>()).unwrap(); + let metadata_offset = + alloc_size.checked_sub(size_of::<::Metadata>()).unwrap(); + let metadata_ptr: *mut ::Metadata = // SAFETY: adding offset within the allocation. - let metadata_ptr: *mut ::Metadata = - alloc.add(metadata_offset).cast(); - // SAFETY: `*metadata_ptr` is within the allocation. + unsafe { alloc.add(metadata_offset).cast() }; + // SAFETY: `*metadata_ptr` is within the allocation. + unsafe { metadata_ptr.write(ptr::metadata::(ptr::dangling::() as *const Dyn)); - // SAFETY: valid heap allocation - const_make_global(alloc); - // SAFETY: we have just written the metadata. - &*metadata_ptr } + // SAFETY: valid heap allocation + unsafe { const_make_global(alloc) }; + // SAFETY: we have just written the metadata. + unsafe { &*metadata_ptr } }; - // SAFETY: `alloc` points to `::Metadata`, so addition stays in-bounds. let value_ptr = + // SAFETY: `alloc` points to `::Metadata`, so addition stays in-bounds. unsafe { (alloc as *const ::Metadata).add(1) }.cast::().cast_mut(); debug_assert!(value_ptr.is_aligned()); mem::forget(value); @@ -373,34 +377,33 @@ impl WithHeader { return; } - unsafe { + let (layout, value_offset) = // SAFETY: Layout must have been computable if we're in drop - let (layout, value_offset) = - WithHeader::::alloc_layout(self.value_layout).unwrap_unchecked(); + unsafe { WithHeader::::alloc_layout(self.value_layout).unwrap_unchecked() }; - // Since we only allocate for non-ZSTs, the layout size cannot be zero. - debug_assert!(layout.size() != 0); - alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); - } + // Since we only allocate for non-ZSTs, the layout size cannot be zero. + debug_assert!(layout.size() != 0); + // SAFETY: We own the allocation with `layout` at `ptr - value_offset`. + unsafe { alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout) }; } } - unsafe { - // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. - let _guard = DropGuard { - ptr: self.0, - value_layout: Layout::for_value_raw(value), - _marker: PhantomData::, - }; + // `_guard` will deallocate the memory when dropped, even if `drop_in_place` unwinds. + let _guard = DropGuard { + ptr: self.0, + // SAFETY: Caller ensures `value` is valid. + value_layout: unsafe { Layout::for_value_raw(value) }, + _marker: PhantomData::, + }; - // We only drop the value because the Pointee trait requires that the metadata is copy - // aka trivially droppable. - ptr::drop_in_place::(value); - } + // We only drop the value because the Pointee trait requires that the metadata is copy + // aka trivially droppable. + // SAFETY: We're the only droppers of `value` and it's not dropped again. + unsafe { ptr::drop_in_place::(value) }; } fn header(&self) -> *mut H { - // Safety: + // SAFETY: // - At least `size_of::()` bytes are allocated ahead of the pointer. // - We know that H will be aligned because the middle pointer is aligned to the greater // of the alignment of the header and the data and the header size includes the padding diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs index 98192e1fb5b01..cf6018f917a54 100644 --- a/library/alloc/src/collections/binary_heap/mod.rs +++ b/library/alloc/src/collections/binary_heap/mod.rs @@ -326,7 +326,7 @@ impl Deref for PeekMut<'_, T, A> { type Target = T; fn deref(&self) -> &T { debug_assert!(!self.heap.is_empty()); - // SAFE: PeekMut is only instantiated for non-empty heaps + // SAFETY: PeekMut is only instantiated for non-empty heaps unsafe { self.heap.data.get_unchecked(0) } } } @@ -346,16 +346,14 @@ impl DerefMut for PeekMut<'_, T, A> { // // This is technique is described throughout several other places in // the standard library as "leak amplification". - unsafe { - // SAFETY: len > 1 so len != 0. - self.original_len = Some(NonZero::new_unchecked(len)); - // SAFETY: len > 1 so all this does for now is leak elements, - // which is safe. - self.heap.data.set_len(1); - } + // SAFETY: len > 1 so len != 0. + self.original_len = Some(unsafe { NonZero::new_unchecked(len) }); + // SAFETY: len > 1 so all this does for now is leak elements, + // which is safe. + unsafe { self.heap.data.set_len(1) }; } - // SAFE: PeekMut is only instantiated for non-empty heaps + // SAFETY: PeekMut is only instantiated for non-empty heaps unsafe { self.heap.data.get_unchecked_mut(0) } } } @@ -1532,11 +1530,13 @@ struct Hole<'a, T: 'a> { impl<'a, T> Hole<'a, T> { /// Creates a new `Hole` at index `pos`. /// - /// Unsafe because pos must be within the data slice. + /// # Safety + /// + /// `pos` must be within the data slice. #[inline] unsafe fn new(data: &'a mut [T], pos: usize) -> Self { debug_assert!(pos < data.len()); - // SAFE: pos should be inside the slice + // SAFETY: Caller ensures pos is inside the slice. let elt = unsafe { ptr::read(data.get_unchecked(pos)) }; Hole { data, elt: ManuallyDrop::new(elt), pos } } @@ -1554,21 +1554,27 @@ impl<'a, T> Hole<'a, T> { /// Returns a reference to the element at `index`. /// - /// Unsafe because index must be within the data slice and not equal to pos. + /// # Safety + /// + /// `index` must be within the data slice and not equal to the current position. #[inline] unsafe fn get(&self, index: usize) -> &T { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); + // SAFETY: Upheld by caller. unsafe { self.data.get_unchecked(index) } } /// Move hole to new location /// - /// Unsafe because index must be within the data slice and not equal to pos. + /// # Safety + /// + /// `index` must be within the data slice and not equal to the current position. #[inline] unsafe fn move_to(&mut self, index: usize) { debug_assert!(index != self.pos); debug_assert!(index < self.data.len()); + // ignore-tidy-undocumented-unsafe unsafe { let ptr = self.data.as_mut_ptr(); let index_ptr: *const _ = ptr.add(index); @@ -1583,6 +1589,7 @@ impl Drop for Hole<'_, T> { #[inline] fn drop(&mut self) { // fill the hole again + // ignore-tidy-undocumented-unsafe unsafe { let pos = self.pos; ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 80317ab2f17ed..e8832fd6e27ca 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -205,6 +205,7 @@ pub struct BTreeMap< #[stable(feature = "btree_drop", since = "1.7.0")] unsafe impl<#[may_dangle] K, #[may_dangle] V, A: AllocatorClone> Drop for BTreeMap { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe drop(unsafe { ptr::read(self) }.into_iter()) } } @@ -279,6 +280,7 @@ impl Clone for BTreeMap { // We can't destructure subtree directly // because BTreeMap implements Drop + // ignore-tidy-undocumented-unsafe let (subroot, sublength) = unsafe { let subtree = ManuallyDrop::new(subtree); let root = ptr::read(&subtree.root); @@ -1320,10 +1322,10 @@ impl BTreeMap { // this through using a drop handler and transmutating CursorMutKey // to CursorMutKey, ManuallyDrop> (see PR #152418) if let Some((k, v)) = self_cursor.remove_next() { + let v = conflict(&k, v, first_other_val); // SAFETY: we remove the K, V out of the next entry, // apply 'f' to get a new (K, V), and insert it back // into the next entry that the cursor is pointing at - let v = conflict(&k, v, first_other_val); unsafe { self_cursor.insert_after_unchecked(k, v) }; } } @@ -1356,10 +1358,10 @@ impl BTreeMap { // this through using a drop handler and transmutating CursorMutKey // to CursorMutKey, ManuallyDrop> (see PR #152418) if let Some((k, v)) = self_cursor.remove_next() { + let v = conflict(&k, v, other_val); // SAFETY: we remove the K, V out of the next entry, // apply 'f' to get a new (K, V), and insert it back // into the next entry that the cursor is pointing at - let v = conflict(&k, v, other_val); unsafe { self_cursor.insert_after_unchecked(k, v) }; } break; @@ -1737,6 +1739,7 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { None } else { self.length -= 1; + // SAFETY: Ensured by check. Some(unsafe { self.range.next_unchecked() }) } } @@ -1774,6 +1777,7 @@ impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> { None } else { self.length -= 1; + // SAFETY: Ensured by check. Some(unsafe { self.range.next_back_unchecked() }) } } @@ -1815,6 +1819,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { None } else { self.length -= 1; + // SAFETY: Ensured by check. Some(unsafe { self.range.next_unchecked() }) } } @@ -1849,6 +1854,7 @@ impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> { None } else { self.length -= 1; + // SAFETY: Ensured by check. Some(unsafe { self.range.next_back_unchecked() }) } } @@ -1889,12 +1895,14 @@ impl IntoIterator for BTreeMap { IntoIter { range: full_range, length: me.length, + // ignore-tidy-undocumented-unsafe alloc: unsafe { ManuallyDrop::take(&mut me.alloc) }, } } else { IntoIter { range: LazyLeafRange::none(), length: 0, + // ignore-tidy-undocumented-unsafe alloc: unsafe { ManuallyDrop::take(&mut me.alloc) }, } } @@ -1937,6 +1945,7 @@ impl IntoIter { None } else { self.length -= 1; + // ignore-tidy-undocumented-unsafe Some(unsafe { self.range.deallocating_next_unchecked(self.alloc.clone()) }) } } @@ -1951,6 +1960,7 @@ impl IntoIter { None } else { self.length -= 1; + // ignore-tidy-undocumented-unsafe Some(unsafe { self.range.deallocating_next_back_unchecked(self.alloc.clone()) }) } } @@ -3345,6 +3355,7 @@ impl<'a, K, V, A> CursorMutKey<'a, K, V, A> { let (k, v) = unsafe { kv.reborrow_mut().into_kv_mut() }; let (k, v) = (k as *mut _, v as *mut _); self.current = Some(kv.next_leaf_edge()); + // ignore-tidy-undocumented-unsafe Some(unsafe { (&mut *k, &mut *v) }) } Err(root) => { @@ -3370,6 +3381,7 @@ impl<'a, K, V, A> CursorMutKey<'a, K, V, A> { let (k, v) = unsafe { kv.reborrow_mut().into_kv_mut() }; let (k, v) = (k as *mut _, v as *mut _); self.current = Some(kv.next_back_leaf_edge()); + // ignore-tidy-undocumented-unsafe Some(unsafe { (&mut *k, &mut *v) }) } Err(root) => { @@ -3532,6 +3544,7 @@ impl<'a, K: Ord, V, A: AllocatorClone> CursorMutKey<'a, K, V, A> { return Err(UnorderedKeyError {}); } } + // SAFETY: Ensured by checks above. unsafe { self.insert_after_unchecked(key, value); } @@ -3560,6 +3573,7 @@ impl<'a, K: Ord, V, A: AllocatorClone> CursorMutKey<'a, K, V, A> { return Err(UnorderedKeyError {}); } } + // SAFETY: Ensured by checks above. unsafe { self.insert_before_unchecked(key, value); } @@ -3641,6 +3655,7 @@ impl<'a, K: Ord, V, A: AllocatorClone> CursorMut<'a, K, V, A> { /// * All keys in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_after_unchecked(&mut self, key: K, value: V) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_after_unchecked(key, value) } } @@ -3659,6 +3674,7 @@ impl<'a, K: Ord, V, A: AllocatorClone> CursorMut<'a, K, V, A> { /// * All keys in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_before_unchecked(&mut self, key: K, value: V) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_before_unchecked(key, value) } } diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index 4643c4133d55d..ad86e9422d974 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -23,8 +23,10 @@ pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { } } let guard = PanicGuard; + // SAFETY: v is valid for reads and we write a new value before returning. let value = unsafe { ptr::read(v) }; let (new_value, ret) = change(value); + // SAFETY: new_value is T and v is valid for writes. unsafe { ptr::write(v, new_value); } diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index d5b514e67e82e..28054cdeb5dd6 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -57,11 +57,13 @@ impl<'a, K, V> LeafRange, K, V> { impl<'a, K, V> LeafRange, K, V> { #[inline] pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + // ignore-tidy-undocumented-unsafe self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + // ignore-tidy-undocumented-unsafe self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -158,11 +160,13 @@ impl LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + // SAFETY: Upheld by caller. unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + // SAFETY: Upheld by caller. unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -170,11 +174,13 @@ impl<'a, K, V> LazyLeafRange, K, V> { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + // SAFETY: Upheld by caller. unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + // SAFETY: Upheld by caller. unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -196,6 +202,7 @@ impl LazyLeafRange { ) -> Handle, marker::KV> { debug_assert!(self.front.is_some()); let front = self.init_front().unwrap(); + // ignore-tidy-undocumented-unsafe unsafe { front.deallocating_next_unchecked(alloc) } } @@ -206,6 +213,7 @@ impl LazyLeafRange { ) -> Handle, marker::KV> { debug_assert!(self.back.is_some()); let back = self.init_back().unwrap(); + // ignore-tidy-undocumented-unsafe unsafe { back.deallocating_next_back_unchecked(alloc) } } @@ -222,6 +230,7 @@ impl LazyLeafRange { &mut self, ) -> Option<&mut Handle, marker::Edge>> { if let Some(LazyLeafHandle::Root(root)) = &self.front { + // ignore-tidy-undocumented-unsafe self.front = Some(LazyLeafHandle::Edge(unsafe { ptr::read(root) }.first_leaf_edge())); } match &mut self.front { @@ -236,6 +245,7 @@ impl LazyLeafRange { &mut self, ) -> Option<&mut Handle, marker::Edge>> { if let Some(LazyLeafHandle::Root(root)) = &self.back { + // ignore-tidy-undocumented-unsafe self.back = Some(LazyLeafHandle::Edge(unsafe { ptr::read(root) }.last_leaf_edge())); } match &mut self.back { @@ -279,7 +289,9 @@ impl NodeRef { + // ignore-tidy-undocumented-unsafe let mut lower_edge = unsafe { Handle::new_edge(ptr::read(&node), lower_edge_idx) }; + // ignore-tidy-undocumented-unsafe let mut upper_edge = unsafe { Handle::new_edge(node, upper_edge_idx) }; loop { match (lower_edge.force(), upper_edge.force()) { @@ -345,6 +357,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> K: Borrow, R: RangeBounds, { + // ignore-tidy-undocumented-unsafe unsafe { self.find_leaf_edges_spanning_range(range) } } @@ -352,8 +365,8 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. pub(super) fn full_range(self) -> LazyLeafRange, K, V> { - // We duplicate the root NodeRef here -- we will never visit the same KV - // twice, and never end up with overlapping value references. + // SAFETY: We duplicate the root NodeRef here -- we will never visit the + // same KV twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; full_range(self, self2) } @@ -364,8 +377,8 @@ impl NodeRef { /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. pub(super) fn full_range(self) -> LazyLeafRange { - // We duplicate the root NodeRef here -- we will never access it in a way - // that overlaps references obtained from the root. + // SAFETY: We duplicate the root NodeRef here -- we will never access + // it in a way that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; full_range(self, self2) } @@ -464,8 +477,10 @@ impl Handle, marker::Edge> { let mut edge = self.forget_node_type(); loop { edge = match edge.right_kv() { + // ignore-tidy-undocumented-unsafe Ok(kv) => return Some((unsafe { ptr::read(&kv) }.next_leaf_edge(), kv)), Err(last_edge) => { + // ignore-tidy-undocumented-unsafe match unsafe { last_edge.into_node().deallocate_and_ascend(alloc.clone()) } { Some(parent_edge) => parent_edge.forget_node_type(), None => return None, @@ -496,8 +511,10 @@ impl Handle, marker::Edge> { let mut edge = self.forget_node_type(); loop { edge = match edge.left_kv() { + // ignore-tidy-undocumented-unsafe Ok(kv) => return Some((unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv)), Err(last_edge) => { + // ignore-tidy-undocumented-unsafe match unsafe { last_edge.into_node().deallocate_and_ascend(alloc.clone()) } { Some(parent_edge) => parent_edge.forget_node_type(), None => return None, @@ -516,6 +533,7 @@ impl Handle, marker::Edge> { fn deallocating_end(self, alloc: A) { let mut edge = self.forget_node_type(); while let Some(parent_edge) = + // ignore-tidy-undocumented-unsafe unsafe { edge.into_node().deallocate_and_ascend(alloc.clone()) } { edge = parent_edge.forget_node_type(); @@ -558,6 +576,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::E unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { let kv = super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_kv().ok().unwrap(); + // ignore-tidy-undocumented-unsafe (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv) }); // Doing this last is faster, according to benchmarks. @@ -572,6 +591,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::E unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { let kv = super::mem::replace(self, |leaf_edge| { let kv = leaf_edge.next_back_kv().ok().unwrap(); + // ignore-tidy-undocumented-unsafe (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv) }); // Doing this last is faster, according to benchmarks. @@ -596,6 +616,7 @@ impl Handle, marker::Edge> { &mut self, alloc: A, ) -> Handle, marker::KV> { + // ignore-tidy-undocumented-unsafe super::mem::replace(self, |leaf_edge| unsafe { leaf_edge.deallocating_next(alloc).unwrap() }) @@ -617,6 +638,7 @@ impl Handle, marker::Edge> { &mut self, alloc: A, ) -> Handle, marker::KV> { + // ignore-tidy-undocumented-unsafe super::mem::replace(self, |leaf_edge| unsafe { leaf_edge.deallocating_next_back(alloc).unwrap() }) diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 8088fec38ed6a..aa38d17bb6dbc 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -75,6 +75,7 @@ impl LeafNode { unsafe fn init(this: *mut Self) { // As a general policy, we leave fields uninitialized if they can be, as this should // be both slightly faster and easier to track in Valgrind. + // ignore-tidy-undocumented-unsafe unsafe { // parent_idx, keys, and vals are all MaybeUninit (&raw mut (*this).parent).write(None); @@ -85,12 +86,11 @@ impl LeafNode { /// Creates a new boxed `LeafNode`. fn new(alloc: A) -> Box { let mut leaf = Box::new_uninit_in(alloc); - unsafe { - // SAFETY: `leaf` points to a `LeafNode` - LeafNode::init(leaf.as_mut_ptr()); - // SAFETY: `leaf` was just initialized - leaf.assume_init() - } + + // SAFETY: `leaf` points to a `LeafNode`. + unsafe { LeafNode::init(leaf.as_mut_ptr()) }; + // SAFETY: `leaf` was just initialized. + unsafe { leaf.assume_init() } } } @@ -119,12 +119,11 @@ impl InternalNode { /// such an edge. unsafe fn new(alloc: A) -> Box { let mut node = Box::::new_uninit_in(alloc); - unsafe { - // SAFETY: argument points to the `node.data` `LeafNode` - LeafNode::init(&raw mut (*node.as_mut_ptr()).data); - // SAFETY: `node.data` was just initialized and `node.edges` is MaybeUninit. - node.assume_init() - } + + // SAFETY: argument points to the `node.data` `LeafNode`. + unsafe { LeafNode::init(&raw mut (*node.as_mut_ptr()).data) }; + // SAFETY: `node.data` was just initialized and `node.edges` is MaybeUninit. + unsafe { node.assume_init() } } } @@ -235,6 +234,7 @@ impl NodeRef { impl NodeRef { /// Creates a new internal (height > 0) `NodeRef` fn new_internal(child: Root, alloc: A) -> Self { + // ignore-tidy-undocumented-unsafe let mut new_node = unsafe { InternalNode::new(alloc) }; new_node.edges[0].write(child.node); NodeRef::from_new_internal(new_node, NonZero::new(child.height + 1).unwrap()) @@ -275,6 +275,7 @@ impl<'a, K, V> NodeRef, K, V, marker::Internal> { /// Borrows exclusive access to the data of an internal node. fn as_internal_mut(&mut self) -> &mut InternalNode { let ptr = Self::as_internal_ptr(self); + // ignore-tidy-undocumented-unsafe unsafe { &mut *ptr } } } @@ -285,7 +286,7 @@ impl NodeRef { /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. pub(super) fn len(&self) -> usize { - // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, + // SAFETY: We only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } } @@ -335,10 +336,12 @@ impl NodeRef // We need to use raw pointers to nodes because, if BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. let leaf_ptr: *const _ = Self::as_leaf_ptr(&self); + // ignore-tidy-undocumented-unsafe unsafe { (*leaf_ptr).parent } .as_ref() .map(|parent| Handle { node: NodeRef::from_internal(*parent, self.height + 1), + // ignore-tidy-undocumented-unsafe idx: unsafe { usize::from((*leaf_ptr).parent_idx.assume_init()) }, _marker: PhantomData, }) @@ -346,11 +349,13 @@ impl NodeRef } pub(super) fn first_edge(self) -> Handle { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self, 0) } } pub(super) fn last_edge(self) -> Handle { let len = self.len(); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self, len) } } @@ -358,6 +363,7 @@ impl NodeRef pub(super) fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_kv(self, 0) } } @@ -365,6 +371,7 @@ impl NodeRef pub(super) fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_kv(self, len - 1) } } } @@ -393,6 +400,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows a view into the keys stored in the node. pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); + // ignore-tidy-undocumented-unsafe unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } } @@ -408,6 +416,7 @@ impl NodeRef { let height = self.height; let node = self.node; let ret = self.ascend().ok(); + // ignore-tidy-undocumented-unsafe unsafe { alloc.deallocate( node.cast(), @@ -533,12 +542,16 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { // to avoid aliasing with outstanding references to other elements, // in particular, those returned to the caller in earlier iterations. let leaf = Self::as_leaf_ptr(&mut self); + // ignore-tidy-undocumented-unsafe let keys = unsafe { &raw const (*leaf).keys }; + // ignore-tidy-undocumented-unsafe let vals = unsafe { &raw mut (*leaf).vals }; // We must coerce to unsized array pointers because of Rust issue #74679. let keys: *const [_] = keys; let vals: *mut [_] = vals; + // ignore-tidy-undocumented-unsafe let key = unsafe { (&*keys.get_unchecked(idx)).assume_init_ref() }; + // ignore-tidy-undocumented-unsafe let val = unsafe { (&mut *vals.get_unchecked_mut(idx)).assume_init_mut() }; (key, val) } @@ -557,12 +570,14 @@ impl<'a, K, V> NodeRef, K, V, marker::Internal> { unsafe fn correct_childrens_parent_links>(&mut self, range: R) { for i in range { debug_assert!(i <= self.len()); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.reborrow_mut(), i) }.correct_parent_link(); } } fn correct_all_childrens_parent_links(&mut self) { let len = self.len(); + // ignore-tidy-undocumented-unsafe unsafe { self.correct_childrens_parent_links(0..=len) }; } } @@ -572,7 +587,9 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// without invalidating other references to the node. fn set_parent_link(&mut self, parent: NonNull>, parent_idx: usize) { let leaf = Self::as_leaf_ptr(self); + // ignore-tidy-undocumented-unsafe unsafe { (*leaf).parent = Some(parent) }; + // ignore-tidy-undocumented-unsafe unsafe { (*leaf).parent_idx.write(parent_idx as u16) }; } } @@ -627,6 +644,7 @@ impl NodeRef { self.height -= 1; self.clear_parent_link(); + // ignore-tidy-undocumented-unsafe unsafe { alloc.deallocate(top.cast(), Layout::new::>()); } @@ -669,6 +687,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; + // ignore-tidy-undocumented-unsafe unsafe { self.key_area_mut(idx).write(key); self.val_area_mut(idx).write(val); @@ -697,6 +716,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { let idx = usize::from(*len); assert!(idx < CAPACITY); *len += 1; + // ignore-tidy-undocumented-unsafe unsafe { self.key_area_mut(idx).write(key); self.val_area_mut(idx).write(val); @@ -805,10 +825,12 @@ impl Handle, mar } pub(super) fn left_edge(self) -> Handle, marker::Edge> { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.node, self.idx) } } pub(super) fn right_edge(self) -> Handle, marker::Edge> { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -844,6 +866,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type + // ignore-tidy-undocumented-unsafe Handle { node: unsafe { self.node.reborrow_mut() }, idx: self.idx, _marker: PhantomData } } @@ -867,6 +890,7 @@ impl Handle( self, ) -> Handle, K, V, NodeType>, HandleType> { + // ignore-tidy-undocumented-unsafe Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -884,6 +908,7 @@ impl Handle, mar self, ) -> Result, marker::KV>, Self> { if self.idx > 0 { + // ignore-tidy-undocumented-unsafe Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { Err(self) @@ -894,6 +919,7 @@ impl Handle, mar self, ) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { + // ignore-tidy-undocumented-unsafe Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { Err(self) @@ -934,6 +960,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark debug_assert!(self.node.len() < CAPACITY); let new_len = self.node.len() + 1; + // ignore-tidy-undocumented-unsafe unsafe { slice_insert(self.node.key_area_mut(..new_len), self.idx, key); slice_insert(self.node.val_area_mut(..new_len), self.idx, val); @@ -965,12 +992,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark (None, handle.dormant()) } else { let (middle_kv_idx, insertion) = splitpoint(self.idx); + // ignore-tidy-undocumented-unsafe let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; let mut result = middle.split(alloc); let insertion_edge = match insertion { + // ignore-tidy-undocumented-unsafe LeftOrRight::Left(insert_idx) => unsafe { Handle::new_edge(result.left.reborrow_mut(), insert_idx) }, + // ignore-tidy-undocumented-unsafe LeftOrRight::Right(insert_idx) => unsafe { Handle::new_edge(result.right.borrow_mut(), insert_idx) }, @@ -988,6 +1018,7 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: /// links to. This is useful when the ordering of edges has been changed, fn correct_parent_link(self) { // Create backpointer without invalidating other references to the node. + // ignore-tidy-undocumented-unsafe let ptr = unsafe { NonNull::new_unchecked(NodeRef::as_internal_ptr(&self.node)) }; let idx = self.idx; let mut child = self.descend(); @@ -1004,6 +1035,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, debug_assert!(edge.height == self.node.height - 1); let new_len = self.node.len() + 1; + // ignore-tidy-undocumented-unsafe unsafe { slice_insert(self.node.key_area_mut(..new_len), self.idx, key); slice_insert(self.node.val_area_mut(..new_len), self.idx, val); @@ -1031,12 +1063,15 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, None } else { let (middle_kv_idx, insertion) = splitpoint(self.idx); + // ignore-tidy-undocumented-unsafe let middle = unsafe { Handle::new_kv(self.node, middle_kv_idx) }; let mut result = middle.split(alloc); let mut insertion_edge = match insertion { + // ignore-tidy-undocumented-unsafe LeftOrRight::Left(insert_idx) => unsafe { Handle::new_edge(result.left.reborrow_mut(), insert_idx) }, + // ignore-tidy-undocumented-unsafe LeftOrRight::Right(insert_idx) => unsafe { Handle::new_edge(result.right.borrow_mut(), insert_idx) }, @@ -1112,6 +1147,7 @@ impl // reference (Rust issue #73987) and invalidate any other references // to or inside the array, should any be around. let parent_ptr = NodeRef::as_internal_ptr(&self.node); + // ignore-tidy-undocumented-unsafe let node = unsafe { (*parent_ptr).edges.get_unchecked(self.idx).assume_init_read() }; NodeRef { node, height: self.node.height - 1, _marker: PhantomData } } @@ -1121,7 +1157,9 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); + // ignore-tidy-undocumented-unsafe let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; + // ignore-tidy-undocumented-unsafe let v = unsafe { leaf.vals.get_unchecked(self.idx).assume_init_ref() }; (k, v) } @@ -1129,19 +1167,23 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { pub(super) fn key_mut(&mut self) -> &mut K { + // ignore-tidy-undocumented-unsafe unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); + // ignore-tidy-undocumented-unsafe unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); + // ignore-tidy-undocumented-unsafe let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; + // ignore-tidy-undocumented-unsafe let v = unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() }; (k, v) } @@ -1149,6 +1191,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + // ignore-tidy-undocumented-unsafe unsafe { self.node.into_key_val_mut_at(self.idx) } } } @@ -1158,6 +1201,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. + // ignore-tidy-undocumented-unsafe unsafe { let leaf = self.node.as_leaf_mut(); let key = leaf.keys.get_unchecked_mut(self.idx).assume_init_mut(); @@ -1180,6 +1224,7 @@ impl Handle, marker::KV> pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); + // ignore-tidy-undocumented-unsafe unsafe { let key = leaf.keys.get_unchecked_mut(self.idx).assume_init_read(); let val = leaf.vals.get_unchecked_mut(self.idx).assume_init_read(); @@ -1197,6 +1242,7 @@ impl Handle, marker::KV> impl Drop for Dropper<'_, T> { #[inline] fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { self.0.assume_init_drop(); } @@ -1205,6 +1251,7 @@ impl Handle, marker::KV> debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); + // ignore-tidy-undocumented-unsafe unsafe { let key = leaf.keys.get_unchecked_mut(self.idx); let val = leaf.vals.get_unchecked_mut(self.idx); @@ -1223,6 +1270,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> let old_len = self.node.len(); let new_len = old_len - self.idx - 1; new_node.len = new_len as u16; + // ignore-tidy-undocumented-unsafe unsafe { let k = self.node.key_area_mut(self.idx).assume_init_read(); let v = self.node.val_area_mut(self.idx).assume_init_read(); @@ -1268,6 +1316,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); + // ignore-tidy-undocumented-unsafe unsafe { let k = slice_remove(self.node.key_area_mut(..old_len), self.idx); let v = slice_remove(self.node.val_area_mut(..old_len), self.idx); @@ -1290,6 +1339,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { let old_len = self.node.len(); + // ignore-tidy-undocumented-unsafe unsafe { let mut new_node = InternalNode::new(alloc); let kv = self.split_leaf_data(&mut new_node.data); @@ -1318,7 +1368,9 @@ pub(super) struct BalancingContext<'a, K, V> { impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + // ignore-tidy-undocumented-unsafe let self1 = unsafe { ptr::read(&self) }; + // ignore-tidy-undocumented-unsafe let self2 = unsafe { ptr::read(&self) }; BalancingContext { parent: self, @@ -1344,15 +1396,18 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// the right, instead of shifting at least N of the sibling's elements to /// the left. pub(super) fn choose_parent_kv(self) -> Result>, Self> { + // ignore-tidy-undocumented-unsafe match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { + // ignore-tidy-undocumented-unsafe parent: unsafe { ptr::read(&left_parent_kv) }, left_child: left_parent_kv.left_edge().descend(), right_child: self, })), Err(parent_edge) => match parent_edge.right_kv() { Ok(right_parent_kv) => Ok(LeftOrRight::Right(BalancingContext { + // ignore-tidy-undocumented-unsafe parent: unsafe { ptr::read(&right_parent_kv) }, left_child: self, right_child: right_parent_kv.right_edge().descend(), @@ -1413,6 +1468,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { assert!(new_left_len <= CAPACITY); + // ignore-tidy-undocumented-unsafe unsafe { *left_node.len_mut() = new_left_len as u16; @@ -1497,6 +1553,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { LeftOrRight::Left(idx) => idx, LeftOrRight::Right(idx) => old_left_len + 1 + idx, }; + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(child, new_idx) } } @@ -1509,6 +1566,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { self.bulk_steal_left(1); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.right_child, 1 + track_right_edge_idx) } } @@ -1521,12 +1579,14 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { self.bulk_steal_right(1); + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.left_child, track_left_edge_idx) } } /// This does stealing similar to `steal_left` but steals multiple elements at once. pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); + // ignore-tidy-undocumented-unsafe unsafe { let left_node = &mut self.left_child; let old_left_len = left_node.len(); @@ -1590,6 +1650,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// The symmetric clone of `bulk_steal_left`. pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); + // ignore-tidy-undocumented-unsafe unsafe { let left_node = &mut self.left_child; let old_left_len = left_node.len(); @@ -1656,6 +1717,7 @@ impl Handle, marker::E pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } } } @@ -1664,6 +1726,7 @@ impl Handle, marke pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } } } @@ -1672,6 +1735,7 @@ impl Handle, marker::K pub(super) fn forget_node_type( self, ) -> Handle, marker::KV> { + // ignore-tidy-undocumented-unsafe unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } } } @@ -1700,6 +1764,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { + // ignore-tidy-undocumented-unsafe let node = unsafe { self.node.cast_to_leaf_unchecked() }; Handle { node, idx: self.idx, _marker: PhantomData } } @@ -1712,6 +1777,7 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { + // ignore-tidy-undocumented-unsafe unsafe { let new_left_len = self.idx; let mut left_node = self.reborrow_mut().into_node(); @@ -1820,6 +1886,7 @@ pub(super) mod marker { /// # Safety /// The slice has more than `idx` elements. unsafe fn slice_insert(slice: &mut [MaybeUninit], idx: usize, val: T) { + // ignore-tidy-undocumented-unsafe unsafe { let len = slice.len(); debug_assert!(len > idx); @@ -1837,6 +1904,7 @@ unsafe fn slice_insert(slice: &mut [MaybeUninit], idx: usize, val: T) { /// # Safety /// The slice has more than `idx` elements. unsafe fn slice_remove(slice: &mut [MaybeUninit], idx: usize) -> T { + // ignore-tidy-undocumented-unsafe unsafe { let len = slice.len(); debug_assert!(idx < len); @@ -1852,6 +1920,7 @@ unsafe fn slice_remove(slice: &mut [MaybeUninit], idx: usize) -> T { /// # Safety /// The slice has at least `distance` elements. unsafe fn slice_shl(slice: &mut [MaybeUninit], distance: usize) { + // ignore-tidy-undocumented-unsafe unsafe { let slice_ptr = slice.as_mut_ptr(); ptr::copy(slice_ptr.add(distance), slice_ptr, slice.len() - distance); @@ -1863,6 +1932,7 @@ unsafe fn slice_shl(slice: &mut [MaybeUninit], distance: usize) { /// # Safety /// The slice has at least `distance` elements. unsafe fn slice_shr(slice: &mut [MaybeUninit], distance: usize) { + // ignore-tidy-undocumented-unsafe unsafe { let slice_ptr = slice.as_mut_ptr(); ptr::copy(slice_ptr, slice_ptr.add(distance), slice.len() - distance); @@ -1874,6 +1944,7 @@ unsafe fn slice_shr(slice: &mut [MaybeUninit], distance: usize) { /// Works like `dst.copy_from_slice(src)` but does not require `T` to be `Copy`. fn move_to_slice(src: &mut [MaybeUninit], dst: &mut [MaybeUninit]) { assert!(src.len() == dst.len()); + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), src.len()); } diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index b21c7e78b5bb3..e2835efa50960 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -53,6 +53,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark right_parent_kv.steal_right(idx) } } + // ignore-tidy-undocumented-unsafe Err(pos) => unsafe { Handle::new_edge(pos, idx) }, }; // SAFETY: `new_pos` is the leaf we started from or a sibling. @@ -85,11 +86,13 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, // the element we were asked to remove. Prefer the left adjacent KV, // for the reasons listed in `choose_parent_kv`. let left_leaf_kv = self.left_edge().descend().last_leaf_edge().left_kv(); + // ignore-tidy-undocumented-unsafe let left_leaf_kv = unsafe { left_leaf_kv.ok().unwrap_unchecked() }; let (left_kv, left_hole) = left_leaf_kv.remove_leaf_kv(handle_emptied_internal_root, alloc); // The internal node may have been stolen from or merged. Go back right // to find where the original KV ended up. + // ignore-tidy-undocumented-unsafe let mut internal = unsafe { left_hole.next_kv().ok().unwrap_unchecked() }; let old_kv = internal.replace_kv(left_kv.0, left_kv.1); let pos = internal.next_leaf_edge(); diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 96e5bf108024b..c26a536d70e65 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -128,6 +128,7 @@ impl NodeRef NodeRef return Err(common_edge), @@ -165,6 +167,7 @@ impl NodeRef, { let (edge_idx, bound) = self.find_lower_bound_index(bound); + // ignore-tidy-undocumented-unsafe let edge = unsafe { Handle::new_edge(self, edge_idx) }; (edge, bound) } @@ -178,7 +181,9 @@ impl NodeRef, { + // ignore-tidy-undocumented-unsafe let (edge_idx, bound) = unsafe { self.find_upper_bound_index(bound, 0) }; + // ignore-tidy-undocumented-unsafe let edge = unsafe { Handle::new_edge(self, edge_idx) }; (edge, bound) } @@ -200,8 +205,11 @@ impl NodeRef { Q: Ord, K: Borrow, { + // ignore-tidy-undocumented-unsafe match unsafe { self.find_key_index(key, 0) } { + // ignore-tidy-undocumented-unsafe IndexResult::KV(idx) => Found(unsafe { Handle::new_kv(self, idx) }), + // ignore-tidy-undocumented-unsafe IndexResult::Edge(idx) => GoDown(unsafe { Handle::new_edge(self, idx) }), } } @@ -222,6 +230,7 @@ impl NodeRef { let node = self.reborrow(); let keys = node.keys(); debug_assert!(start_index <= keys.len()); + // ignore-tidy-undocumented-unsafe for (offset, k) in unsafe { keys.get_unchecked(start_index..) }.iter().enumerate() { match key.cmp(k.borrow()) { Ordering::Greater => {} @@ -246,10 +255,12 @@ impl NodeRef { K: Borrow, { match bound { + // ignore-tidy-undocumented-unsafe Included(key) => match unsafe { self.find_key_index(key, 0) } { IndexResult::KV(idx) => (idx, AllExcluded), IndexResult::Edge(idx) => (idx, bound), }, + // ignore-tidy-undocumented-unsafe Excluded(key) => match unsafe { self.find_key_index(key, 0) } { IndexResult::KV(idx) => (idx + 1, AllIncluded), IndexResult::Edge(idx) => (idx, bound), @@ -274,10 +285,12 @@ impl NodeRef { K: Borrow, { match bound { + // ignore-tidy-undocumented-unsafe Included(key) => match unsafe { self.find_key_index(key, start_index) } { IndexResult::KV(idx) => (idx + 1, AllExcluded), IndexResult::Edge(idx) => (idx, bound), }, + // ignore-tidy-undocumented-unsafe Excluded(key) => match unsafe { self.find_key_index(key, start_index) } { IndexResult::KV(idx) => (idx, AllIncluded), IndexResult::Edge(idx) => (idx, bound), diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs index 7c211e200f42d..3b15bda185d9d 100644 --- a/library/alloc/src/collections/btree/set.rs +++ b/library/alloc/src/collections/btree/set.rs @@ -2303,6 +2303,7 @@ impl<'a, T, A> CursorMut<'a, T, A> { /// * All elements in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn with_mutable_key(self) -> CursorMutKey<'a, T, A> { + // ignore-tidy-undocumented-unsafe CursorMutKey { inner: unsafe { self.inner.with_mutable_key() } } } } @@ -2372,6 +2373,7 @@ impl<'a, T: Ord, A: AllocatorClone> CursorMut<'a, T, A> { /// * All elements in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_after_unchecked(&mut self, value: T) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_after_unchecked(value, SetValZST) } } @@ -2390,6 +2392,7 @@ impl<'a, T: Ord, A: AllocatorClone> CursorMut<'a, T, A> { /// * All elements in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_before_unchecked(&mut self, value: T) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_before_unchecked(value, SetValZST) } } @@ -2458,6 +2461,7 @@ impl<'a, T: Ord, A: AllocatorClone> CursorMutKey<'a, T, A> { /// * All elements in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_after_unchecked(&mut self, value: T) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_after_unchecked(value, SetValZST) } } @@ -2476,6 +2480,7 @@ impl<'a, T: Ord, A: AllocatorClone> CursorMutKey<'a, T, A> { /// * All elements in the tree must remain in sorted order. #[unstable(feature = "btree_cursors", issue = "107540")] pub unsafe fn insert_before_unchecked(&mut self, value: T) { + // SAFETY: Upheld by caller. unsafe { self.inner.insert_before_unchecked(value, SetValZST) } } diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index a0542d2b5737c..1417f56e46cf9 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -172,8 +172,8 @@ impl LinkedList { /// This method takes ownership of the node, so the pointer should not be used again. #[inline] unsafe fn push_front_node(&mut self, node: NonNull>) { - // This method takes care not to create mutable references to whole nodes, - // to maintain validity of aliasing pointers into `element`. + // SAFETY: This method takes care not to create mutable references to + // whole nodes, to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = self.head; (*node.as_ptr()).prev = None; @@ -193,8 +193,8 @@ impl LinkedList { /// Removes and returns the node at the front of the list. #[inline] fn pop_front_node(&mut self) -> Option, &A>> { - // This method takes care not to create mutable references to whole nodes, - // to maintain validity of aliasing pointers into `element`. + // SAFETY: This method takes care not to create mutable references to + // whole nodes, to maintain validity of aliasing pointers into `element`. self.head.map(|node| unsafe { let node = Box::from_raw_in(node.as_ptr(), &self.alloc); self.head = node.next; @@ -217,8 +217,8 @@ impl LinkedList { /// This method takes ownership of the node, so the pointer should not be used again. #[inline] unsafe fn push_back_node(&mut self, node: NonNull>) { - // This method takes care not to create mutable references to whole nodes, - // to maintain validity of aliasing pointers into `element`. + // SAFETY: This method takes care not to create mutable references to + // whole nodes, to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = None; (*node.as_ptr()).prev = self.tail; @@ -238,8 +238,8 @@ impl LinkedList { /// Removes and returns the node at the back of the list. #[inline] fn pop_back_node(&mut self) -> Option, &A>> { - // This method takes care not to create mutable references to whole nodes, - // to maintain validity of aliasing pointers into `element`. + // SAFETY: This method takes care not to create mutable references to + // whole nodes, to maintain validity of aliasing pointers into `element`. self.tail.map(|node| unsafe { let node = Box::from_raw_in(node.as_ptr(), &self.alloc); self.tail = node.prev; @@ -263,16 +263,19 @@ impl LinkedList { /// maintain validity of aliasing pointers. #[inline] unsafe fn unlink_node(&mut self, mut node: NonNull>) { - let node = unsafe { node.as_mut() }; // this one is ours now, we can create an &mut. + // SAFETY: This is ours now, we can create a &mut. + let node = unsafe { node.as_mut() }; // Not creating new mutable (unique!) references overlapping `element`. match node.prev { + // ignore-tidy-undocumented-unsafe Some(prev) => unsafe { (*prev.as_ptr()).next = node.next }, // this node is the head node None => self.head = node.next, }; match node.next { + // ignore-tidy-undocumented-unsafe Some(next) => unsafe { (*next.as_ptr()).prev = node.prev }, // this node is the tail node None => self.tail = node.prev, @@ -296,6 +299,7 @@ impl LinkedList { // This method takes care not to create multiple mutable references to whole nodes at the same time, // to maintain validity of aliasing pointers into `element`. if let Some(mut existing_prev) = existing_prev { + // ignore-tidy-undocumented-unsafe unsafe { existing_prev.as_mut().next = Some(splice_start); } @@ -303,12 +307,14 @@ impl LinkedList { self.head = Some(splice_start); } if let Some(mut existing_next) = existing_next { + // ignore-tidy-undocumented-unsafe unsafe { existing_next.as_mut().prev = Some(splice_end); } } else { self.tail = Some(splice_end); } + // ignore-tidy-undocumented-unsafe unsafe { splice_start.as_mut().prev = existing_prev; splice_end.as_mut().next = existing_next; @@ -347,10 +353,12 @@ impl LinkedList { if let Some(mut split_node) = split_node { let first_part_head; let first_part_tail; + // ignore-tidy-undocumented-unsafe unsafe { first_part_tail = split_node.as_mut().prev.take(); } if let Some(mut tail) = first_part_tail { + // ignore-tidy-undocumented-unsafe unsafe { tail.as_mut().next = None; } @@ -391,10 +399,12 @@ impl LinkedList { if let Some(mut split_node) = split_node { let second_part_head; let second_part_tail; + // ignore-tidy-undocumented-unsafe unsafe { second_part_head = split_node.as_mut().next.take(); } if let Some(mut head) = second_part_head { + // ignore-tidy-undocumented-unsafe unsafe { head.as_mut().prev = None; } @@ -483,9 +493,9 @@ impl LinkedList { match self.tail { None => mem::swap(self, other), Some(mut tail) => { - // `as_mut` is okay here because we have exclusive access to the entirety - // of both lists. if let Some(mut other_head) = other.head.take() { + // SAFETY: `as_mut` is okay here because we have exclusive + // access to the entirety of both lists. unsafe { tail.as_mut().next = Some(other_head); other_head.as_mut().prev = Some(tail); @@ -743,6 +753,7 @@ impl LinkedList { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_confusables("first")] pub fn front(&self) -> Option<&T> { + // ignore-tidy-undocumented-unsafe unsafe { self.head.as_ref().map(|node| &node.as_ref().element) } } @@ -772,6 +783,7 @@ impl LinkedList { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn front_mut(&mut self) -> Option<&mut T> { + // ignore-tidy-undocumented-unsafe unsafe { self.head.as_mut().map(|node| &mut node.as_mut().element) } } @@ -795,6 +807,7 @@ impl LinkedList { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub fn back(&self) -> Option<&T> { + // ignore-tidy-undocumented-unsafe unsafe { self.tail.as_ref().map(|node| &node.as_ref().element) } } @@ -823,6 +836,7 @@ impl LinkedList { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn back_mut(&mut self) -> Option<&mut T> { + // ignore-tidy-undocumented-unsafe unsafe { self.tail.as_mut().map(|node| &mut node.as_mut().element) } } @@ -1024,6 +1038,7 @@ impl LinkedList { } iter.tail }; + // ignore-tidy-undocumented-unsafe unsafe { self.split_off_after_node(split_node, at) } } @@ -1427,6 +1442,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { self.index = 0; } // We had a previous element, so let's go to its next + // ignore-tidy-undocumented-unsafe Some(current) => unsafe { self.current = current.as_ref().next; self.index += 1; @@ -1448,6 +1464,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { self.index = self.list.len().saturating_sub(1); } // Have a prev. Yield it and go to the previous element. + // ignore-tidy-undocumented-unsafe Some(current) => unsafe { self.current = current.as_ref().prev; self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); @@ -1463,6 +1480,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn current(&self) -> Option<&'a T> { + // ignore-tidy-undocumented-unsafe unsafe { self.current.map(|current| &(*current.as_ptr()).element) } } @@ -1474,6 +1492,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn peek_next(&self) -> Option<&'a T> { + // ignore-tidy-undocumented-unsafe unsafe { let next = match self.current { None => self.list.head, @@ -1491,6 +1510,7 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn peek_prev(&self) -> Option<&'a T> { + // ignore-tidy-undocumented-unsafe unsafe { let prev = match self.current { None => self.list.tail, @@ -1554,6 +1574,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { self.index = 0; } // We had a previous element, so let's go to its next + // ignore-tidy-undocumented-unsafe Some(current) => unsafe { self.current = current.as_ref().next; self.index += 1; @@ -1575,6 +1596,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { self.index = self.list.len().saturating_sub(1); } // Have a prev. Yield it and go to the previous element. + // ignore-tidy-undocumented-unsafe Some(current) => unsafe { self.current = current.as_ref().prev; self.index = self.index.checked_sub(1).unwrap_or_else(|| self.list.len()); @@ -1590,6 +1612,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { #[must_use] #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn current(&mut self) -> Option<&mut T> { + // ignore-tidy-undocumented-unsafe unsafe { self.current.map(|current| &mut (*current.as_ptr()).element) } } @@ -1600,6 +1623,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// element of the `LinkedList` then this returns `None`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn peek_next(&mut self) -> Option<&mut T> { + // ignore-tidy-undocumented-unsafe unsafe { let next = match self.current { None => self.list.head, @@ -1616,6 +1640,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// element of the `LinkedList` then this returns `None`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn peek_prev(&mut self) -> Option<&mut T> { + // ignore-tidy-undocumented-unsafe unsafe { let prev = match self.current { None => self.list.tail, @@ -1658,6 +1683,7 @@ impl<'a, T> CursorMut<'a, T> { /// inserted at the start of the `LinkedList`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn splice_after(&mut self, list: LinkedList) { + // ignore-tidy-undocumented-unsafe unsafe { let Some((splice_head, splice_tail, splice_len)) = list.detach_all_nodes() else { return; @@ -1680,6 +1706,7 @@ impl<'a, T> CursorMut<'a, T> { /// inserted at the end of the `LinkedList`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn splice_before(&mut self, list: LinkedList) { + // ignore-tidy-undocumented-unsafe unsafe { let (splice_head, splice_tail, splice_len) = match list.detach_all_nodes() { Some(parts) => parts, @@ -1702,6 +1729,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// inserted at the front of the `LinkedList`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_after(&mut self, item: T) { + // ignore-tidy-undocumented-unsafe unsafe { let spliced_node = Box::into_non_null_with_allocator(Box::new_in(Node::new(item), &self.list.alloc)).0; @@ -1723,6 +1751,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { /// inserted at the end of the `LinkedList`. #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn insert_before(&mut self, item: T) { + // ignore-tidy-undocumented-unsafe unsafe { let spliced_node = Box::into_non_null_with_allocator(Box::new_in(Node::new(item), &self.list.alloc)).0; @@ -1745,6 +1774,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { #[unstable(feature = "linked_list_cursors", issue = "58533")] pub fn remove_current(&mut self) -> Option { let unlinked_node = self.current?; + // ignore-tidy-undocumented-unsafe unsafe { self.current = unlinked_node.as_ref().next; self.list.unlink_node(unlinked_node); @@ -1766,6 +1796,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { A: AllocatorClone, { let mut unlinked_node = self.current?; + // ignore-tidy-undocumented-unsafe unsafe { self.current = unlinked_node.as_ref().next; self.list.unlink_node(unlinked_node); @@ -1798,6 +1829,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { // The "ghost" non-element's index has changed to 0. self.index = 0; } + // ignore-tidy-undocumented-unsafe unsafe { self.list.split_off_after_node(self.current, split_off_idx) } } @@ -1814,6 +1846,7 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { { let split_off_idx = self.index; self.index = 0; + // ignore-tidy-undocumented-unsafe unsafe { self.list.split_off_before_node(self.current, split_off_idx) } } @@ -1985,6 +2018,7 @@ where fn next(&mut self) -> Option { while let Some(mut node) = self.it { + // ignore-tidy-undocumented-unsafe unsafe { self.it = node.as_ref().next; self.idx += 1; @@ -2012,6 +2046,7 @@ where A: Allocator, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // ignore-tidy-undocumented-unsafe let peek = self.it.map(|node| unsafe { &node.as_ref().element }); f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive() } diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index da4b803c64d56..b56af5f0e85b6 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -55,6 +55,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { // Only returns pointers to the slices, as that's all we need // to drop them. May only be called if `self.remaining != 0`. pub(super) unsafe fn as_slices(&self) -> (*mut [T], *mut [T]) { + // ignore-tidy-undocumented-unsafe unsafe { let deque = self.deque.as_ref(); @@ -98,16 +99,17 @@ impl Drop for Drain<'_, T, A> { let guard = DropGuard(self); if mem::needs_drop::() && guard.0.remaining != 0 { - unsafe { - // SAFETY: We just checked that `self.remaining != 0`. - let (front, back) = guard.0.as_slices(); - // since idx is a logical index, we don't need to worry about wrapping. - guard.0.idx += front.len(); - guard.0.remaining -= front.len(); - ptr::drop_in_place(front); - guard.0.remaining = 0; - ptr::drop_in_place(back); - } + // SAFETY: We just checked that `self.remaining != 0`. + let (front, back) = unsafe { guard.0.as_slices() }; + // since idx is a logical index, we don't need to worry about wrapping. + guard.0.idx += front.len(); + guard.0.remaining -= front.len(); + // SAFETY: This can't have been dropped before since + // `idx` & `remaining` track what's been dropped. + unsafe { ptr::drop_in_place(front) }; + guard.0.remaining = 0; + // SAFETY: Ditto. + unsafe { ptr::drop_in_place(back) }; } // Dropping `guard` handles moving the remaining elements into place. @@ -115,14 +117,15 @@ impl Drop for Drain<'_, T, A> { #[inline] fn drop(&mut self) { if mem::needs_drop::() && self.0.remaining != 0 { + // SAFETY: We just checked that `self.remaining != 0`. unsafe { - // SAFETY: We just checked that `self.remaining != 0`. let (front, back) = self.0.as_slices(); ptr::drop_in_place(front); ptr::drop_in_place(back); } } + // ignore-tidy-undocumented-unsafe let source_deque = unsafe { self.0.deque.as_mut() }; let drain_len = self.0.drain_len; @@ -212,6 +215,7 @@ impl Drop for Drain<'_, T, A> { len = tail_len; }; + // ignore-tidy-undocumented-unsafe unsafe { source_deque.wrap_copy(src, dst, len); } @@ -241,9 +245,11 @@ impl Iterator for Drain<'_, T, A> { if self.remaining == 0 { return None; } + // ignore-tidy-undocumented-unsafe let wrapped_idx = unsafe { self.deque.as_ref().to_wrapped_index(self.idx) }; self.idx += 1; self.remaining -= 1; + // ignore-tidy-undocumented-unsafe Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) } @@ -263,7 +269,9 @@ impl DoubleEndedIterator for Drain<'_, T, A> { } self.remaining -= 1; let wrapped_idx = + // ignore-tidy-undocumented-unsafe unsafe { self.deque.as_ref().to_wrapped_index(self.idx + self.remaining) }; + // ignore-tidy-undocumented-unsafe Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) } } diff --git a/library/alloc/src/collections/vec_deque/extract_if.rs b/library/alloc/src/collections/vec_deque/extract_if.rs index 19439dfb4f05d..61a75cadf4a26 100644 --- a/library/alloc/src/collections/vec_deque/extract_if.rs +++ b/library/alloc/src/collections/vec_deque/extract_if.rs @@ -74,6 +74,7 @@ where fn next(&mut self) -> Option { while self.idx < self.end { let i = self.idx; + let idx = self.vec.to_wrapped_index(i); // SAFETY: // We know that `i < self.end` from the if guard and that `self.end <= self.old_len` from // the validity of `Self`. Therefore `i` points to an element within `vec`. @@ -83,7 +84,6 @@ where // // Note: we can't use `vec.get_mut(i).unwrap()` here since the precondition for that // function is that i < vec.len, but we've set vec's length to zero. - let idx = self.vec.to_wrapped_index(i); let cur = unsafe { &mut *self.vec.ptr().add(idx.as_index()) }; let drained = (self.pred)(cur); // Update the index *after* the predicate is called. If the index diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index d3dbd10c863fb..7794fb450d776 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -145,7 +145,7 @@ impl<'a, T> Iterator for Iter<'a, T> { #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { - // Safety: The TrustedRandomAccess contract requires that callers only pass an index + // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index // that is in bounds. unsafe { let i1_len = self.i1.len(); diff --git a/library/alloc/src/collections/vec_deque/iter_mut.rs b/library/alloc/src/collections/vec_deque/iter_mut.rs index 0c5f06e752b7b..9d7b99d765fac 100644 --- a/library/alloc/src/collections/vec_deque/iter_mut.rs +++ b/library/alloc/src/collections/vec_deque/iter_mut.rs @@ -209,7 +209,7 @@ impl<'a, T> Iterator for IterMut<'a, T> { #[inline] unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item { - // Safety: The TrustedRandomAccess contract requires that callers only pass an index + // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index // that is in bounds. unsafe { let i1_len = self.i1.len(); diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index b007e3054ee6a..385e172b23207 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -139,6 +139,7 @@ struct Dropper<'a, T>(&'a mut [T]); impl Drop for Dropper<'_, T> { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { ptr::drop_in_place(self.0); } @@ -149,6 +150,7 @@ impl Drop for Dropper<'_, T> { unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { fn drop(&mut self) { let (front, back) = self.as_mut_slices(); + // ignore-tidy-undocumented-unsafe unsafe { let _back_dropper = Dropper(back); // use drop for [T] @@ -206,6 +208,7 @@ impl VecDeque { /// Moves an element out of the buffer #[inline] unsafe fn buffer_read(&mut self, off: WrappedIndex) -> T { + // SAFETY: Upheld by caller. unsafe { ptr::read(self.ptr().add(off.as_index())) } } @@ -215,6 +218,7 @@ impl VecDeque { /// May only be called if `off < self.capacity()`. #[inline] unsafe fn buffer_write(&mut self, off: WrappedIndex, value: T) -> &mut T { + // SAFETY: Upheld by caller. unsafe { let ptr = self.ptr().add(off.as_index()); ptr::write(ptr, value); @@ -226,6 +230,7 @@ impl VecDeque { /// `range` must lie inside `0..self.capacity()`. #[inline] unsafe fn buffer_range(&self, range: Range) -> *mut [T] { + // SAFETY: Upheld by caller. unsafe { self.ptr().add(range.start).cast_slice(range.end - range.start) } } @@ -304,6 +309,7 @@ impl VecDeque { self.capacity(), ); + // ignore-tidy-undocumented-unsafe unsafe { let ptr = self.ptr(); let src_ptr = ptr.add(wrapped_src.as_index()); @@ -348,6 +354,7 @@ impl VecDeque { len, self.capacity() ); + // SAFETY: Upheld by caller. unsafe { ptr::copy(self.ptr().add(src.as_index()), self.ptr().add(dst.as_index()), len); } @@ -372,6 +379,7 @@ impl VecDeque { len, self.capacity() ); + // SAFETY: Upheld by caller. unsafe { ptr::copy_nonoverlapping( self.ptr().add(src.as_index()), @@ -416,6 +424,7 @@ impl VecDeque { // 2 [_ _ A A A A B B _] // D . . . // + // ignore-tidy-undocumented-unsafe unsafe { self.copy(src, dst, len); } @@ -429,6 +438,7 @@ impl VecDeque { // 3 [B B B B _ _ _ A A] // . . D . // + // ignore-tidy-undocumented-unsafe unsafe { self.copy(src, dst, dst_pre_wrap_len); self.copy( @@ -447,6 +457,7 @@ impl VecDeque { // 3 [B B _ _ _ A A A A] // . . D . // + // ignore-tidy-undocumented-unsafe unsafe { self.copy( src.add(dst_pre_wrap_len), @@ -465,6 +476,7 @@ impl VecDeque { // 3 [C C _ _ _ B B C C] // D . . . // + // ignore-tidy-undocumented-unsafe unsafe { self.copy(src, dst, src_pre_wrap_len); self.copy( @@ -483,6 +495,7 @@ impl VecDeque { // 3 [C C A A _ _ _ C C] // D . . . // + // ignore-tidy-undocumented-unsafe unsafe { self.copy( WrappedIndex::zero(), @@ -504,6 +517,7 @@ impl VecDeque { // debug_assert!(dst_pre_wrap_len > src_pre_wrap_len); let delta = dst_pre_wrap_len - src_pre_wrap_len; + // ignore-tidy-undocumented-unsafe unsafe { self.copy(src, dst, src_pre_wrap_len); self.copy(WrappedIndex::zero(), dst.add(src_pre_wrap_len), delta); @@ -526,6 +540,7 @@ impl VecDeque { // debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); let delta = src_pre_wrap_len - dst_pre_wrap_len; + // ignore-tidy-undocumented-unsafe unsafe { self.copy( WrappedIndex::zero(), @@ -550,11 +565,13 @@ impl VecDeque { debug_assert!(src.len() <= self.capacity()); let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst.as_index()), src.len()); } } else { let (left, right) = src.split_at(head_room); + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy_nonoverlapping(left.as_ptr(), self.ptr().add(dst.as_index()), left.len()); ptr::copy_nonoverlapping(right.as_ptr(), self.ptr(), right.len()); @@ -572,6 +589,7 @@ impl VecDeque { /// See [`ptr::copy_nonoverlapping`]. unsafe fn copy_nonoverlapping_reversed(src: *const T, dst: *mut T, count: usize) { for i in 0..count { + // SAFETY: Upheld by caller. unsafe { ptr::copy_nonoverlapping(src.add(count - 1 - i), dst.add(i), 1) }; } } @@ -579,6 +597,7 @@ impl VecDeque { debug_assert!(src.len() <= self.capacity()); let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { + // ignore-tidy-undocumented-unsafe unsafe { copy_nonoverlapping_reversed( src.as_ptr(), @@ -588,6 +607,7 @@ impl VecDeque { } } else { let (left, right) = src.split_at(src.len() - head_room); + // ignore-tidy-undocumented-unsafe unsafe { copy_nonoverlapping_reversed( right.as_ptr(), @@ -612,6 +632,7 @@ impl VecDeque { iter: impl Iterator, written: &mut usize, ) { + // ignore-tidy-undocumented-unsafe iter.enumerate().for_each(|(i, element)| unsafe { self.buffer_write(dst.add(i), element); *written += 1; @@ -648,8 +669,10 @@ impl VecDeque { let mut guard = Guard { deque: self, written: 0 }; if head_room >= len { + // ignore-tidy-undocumented-unsafe unsafe { guard.deque.write_iter(dst, iter, &mut guard.written) }; } else { + // ignore-tidy-undocumented-unsafe unsafe { guard.deque.write_iter( dst, @@ -697,6 +720,7 @@ impl VecDeque { let tail_len = self.len - head_len; if head_len > tail_len && new_capacity - old_capacity >= tail_len { // B + // ignore-tidy-undocumented-unsafe unsafe { self.copy_nonoverlapping( WrappedIndex::zero(), @@ -707,6 +731,7 @@ impl VecDeque { } else { // C let new_head = WrappedIndex::from_arbitrary_number(new_capacity - head_len); + // ignore-tidy-undocumented-unsafe unsafe { // can't use copy_nonoverlapping here, because if e.g. head_len = 2 // and new_capacity = old_capacity + 1, then the heads overlap. @@ -966,6 +991,7 @@ impl VecDeque { pub fn get(&self, index: usize) -> Option<&T> { if index < self.len { let idx = self.to_wrapped_index(index); + // ignore-tidy-undocumented-unsafe unsafe { Some(&*self.ptr().add(idx.as_index())) } } else { None @@ -996,6 +1022,7 @@ impl VecDeque { pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index < self.len { let idx = self.to_wrapped_index(index); + // ignore-tidy-undocumented-unsafe unsafe { Some(&mut *self.ptr().add(idx.as_index())) } } else { None @@ -1031,6 +1058,7 @@ impl VecDeque { assert!(j < self.len()); let ri = self.to_wrapped_index(i); let rj = self.to_wrapped_index(j); + // ignore-tidy-undocumented-unsafe unsafe { ptr::swap(self.ptr().add(ri.as_index()), self.ptr().add(rj.as_index())) } } @@ -1080,6 +1108,7 @@ impl VecDeque { if new_cap > old_cap { self.buf.reserve_exact(self.len, additional); + // ignore-tidy-undocumented-unsafe unsafe { self.handle_capacity_increase(old_cap); } @@ -1112,6 +1141,7 @@ impl VecDeque { // we don't need to reserve_exact(), as the size doesn't have // to be a power of 2. self.buf.reserve(self.len, additional); + // ignore-tidy-undocumented-unsafe unsafe { self.handle_capacity_increase(old_cap); } @@ -1163,6 +1193,7 @@ impl VecDeque { if new_cap > old_cap { self.buf.try_reserve_exact(self.len, additional)?; + // ignore-tidy-undocumented-unsafe unsafe { self.handle_capacity_increase(old_cap); } @@ -1211,6 +1242,7 @@ impl VecDeque { if new_cap > old_cap { self.buf.try_reserve(self.len, additional)?; + // ignore-tidy-undocumented-unsafe unsafe { self.handle_capacity_increase(old_cap); } @@ -1292,8 +1324,10 @@ impl VecDeque { // [. . . . . . . . o o o o o o o . ] // H L // [o o o o o o o . ] + // + // SAFETY: `self.head >= target_cap >= self.len`, therefore these accesses + // do not overlap. unsafe { - // nonoverlapping because `self.head >= target_cap >= self.len`. self.copy_nonoverlapping(self.head, WrappedIndex::zero(), self.len); } self.head = WrappedIndex::zero(); @@ -1309,7 +1343,7 @@ impl VecDeque { // L H // [o o . o o o o o ] let len = self.head + self.len - target_cap; - // Safety: head is < target_cap, so the index is wrapped + // SAFETY: head is < target_cap, so the index is wrapped unsafe { self.copy_nonoverlapping( WrappedIndex::from_arbitrary_number(target_cap), @@ -1332,6 +1366,7 @@ impl VecDeque { // head_len is at least one, so new_head will be < target_cap let new_head = WrappedIndex::from_arbitrary_number(target_cap - head_len); + // ignore-tidy-undocumented-unsafe unsafe { // can't use `copy_nonoverlapping()` here because the new and old // regions for the head might overlap. @@ -1349,11 +1384,9 @@ impl VecDeque { impl Drop for Guard<'_, T, A> { #[cold] fn drop(&mut self) { - unsafe { - // SAFETY: This is only called if `buf.shrink_to_fit` unwinds, - // which is the only time it's safe to call `abort_shrink`. - self.deque.abort_shrink(self.old_head, self.target_cap) - } + // SAFETY: This is only called if `buf.shrink_to_fit` unwinds, + // which is the only time it's safe to call `abort_shrink`. + unsafe { self.deque.abort_shrink(self.old_head, self.target_cap) } } } @@ -1391,9 +1424,9 @@ impl VecDeque { // There's enough spare capacity to copy the tail to the back (because `tail_len < self.capacity() - target_cap`), // and copying the tail should be cheaper than copying the head (because `tail_len <= head_len`). + // SAFETY: The old tail and the new tail can't overlap because the head slice lies + // between them. The head slice ends at `target_cap`, so that's where we copy to. unsafe { - // The old tail and the new tail can't overlap because the head slice lies between them. The - // head slice ends at `target_cap`, so that's where we copy to. self.copy_nonoverlapping( WrappedIndex::zero(), WrappedIndex::from_arbitrary_number(target_cap), @@ -1403,6 +1436,7 @@ impl VecDeque { } else { // Either there's not enough spare capacity to make the deque contiguous, or the head is shorter than the tail // (and therefore hopefully cheaper to copy). + // ignore-tidy-undocumented-unsafe unsafe { // The old and the new head slice can overlap, so we can't use `copy_nonoverlapping` here. self.copy(self.head, old_head, head_len); @@ -1433,8 +1467,7 @@ impl VecDeque { #[doc(alias = "retain_front")] #[stable(feature = "deque_extras", since = "1.16.0")] pub fn truncate(&mut self, len: usize) { - // Safe because: - // + // SAFETY: // * Any slice passed to `drop_in_place` is valid; the second case has // `len <= front.len()` and returning on `len > self.len()` ensures // `begin <= back.len()` in the first case @@ -1487,6 +1520,7 @@ impl VecDeque { #[doc(alias = "truncate_front")] #[stable(feature = "vec_deque_truncate_front", since = "1.99.0")] pub fn retain_back(&mut self, len: usize) { + // ignore-tidy-undocumented-unsafe unsafe { if len >= self.len { // No action is taken @@ -1565,6 +1599,7 @@ impl VecDeque { let fptr = front.as_mut_ptr(); let bptr = back.as_mut_ptr(); + // ignore-tidy-undocumented-unsafe unsafe { let (drop_a, drop_b, drop_c) = if end <= flen { // Kept range lies in `front`. The dropped suffix is the rest of `front` @@ -1858,8 +1893,8 @@ impl VecDeque { // are valid ranges into the physical buffer, so // it's ok to pass them to `buffer_range` and // dereference the result. - let a = unsafe { &*self.buffer_range(a_range) }; - let b = unsafe { &*self.buffer_range(b_range) }; + let (a, b) = unsafe { (&*self.buffer_range(a_range), &*self.buffer_range(b_range)) }; + Iter::new(a.iter(), b.iter()) } @@ -1894,12 +1929,13 @@ impl VecDeque { R: RangeBounds, { let (a_range, b_range) = self.slice_ranges(range, self.len); - // SAFETY: The ranges returned by `slice_ranges` - // are valid ranges into the physical buffer, so - // it's ok to pass them to `buffer_range` and - // dereference the result. - let a = unsafe { &mut *self.buffer_range(a_range) }; - let b = unsafe { &mut *self.buffer_range(b_range) }; + let (a, b) = + // SAFETY: The ranges returned by `slice_ranges` + // are valid ranges into the physical buffer, so + // it's ok to pass them to `buffer_range` and + // dereference the result. + unsafe { (&mut *self.buffer_range(a_range), &mut *self.buffer_range(b_range)) }; + IterMut::new(a.iter_mut(), b.iter_mut()) } @@ -1975,6 +2011,7 @@ impl VecDeque { // "forget" about the values after the start of the drain until after // the drain is complete and the Drain destructor is run. + // ignore-tidy-undocumented-unsafe unsafe { Drain::new(self, drain_start, drain_len) } } @@ -2202,6 +2239,7 @@ impl VecDeque { let old_head = self.head; self.head = self.to_wrapped_index(1); self.len -= 1; + // ignore-tidy-undocumented-unsafe unsafe { core::hint::assert_unchecked(self.len < self.capacity()); Some(self.buffer_read(old_head)) @@ -2229,6 +2267,7 @@ impl VecDeque { None } else { self.len -= 1; + // ignore-tidy-undocumented-unsafe unsafe { core::hint::assert_unchecked(self.len < self.capacity()); Some(self.buffer_read(self.to_wrapped_index(self.len))) @@ -2361,6 +2400,7 @@ impl VecDeque { let len = self.len; self.len += 1; + // ignore-tidy-undocumented-unsafe unsafe { self.buffer_write(self.to_wrapped_index(len), value) } } @@ -2573,6 +2613,7 @@ impl VecDeque { // `index + 1` can't overflow, because if index was usize::MAX, then either the // assert would've failed, or the deque would've tried to grow past usize::MAX // and panicked. + // ignore-tidy-undocumented-unsafe unsafe { // see `remove()` for explanation why this wrap_copy() call is safe. self.wrap_copy(self.to_wrapped_index(index), self.to_wrapped_index(index + 1), k); @@ -2582,6 +2623,7 @@ impl VecDeque { } else { let old_head = self.head; self.head = self.wrap_sub(self.head, 1); + // ignore-tidy-undocumented-unsafe unsafe { self.wrap_copy(old_head, self.head, index); self.len += 1; @@ -2620,18 +2662,20 @@ impl VecDeque { let wrapped_idx = self.to_wrapped_index(index); + // ignore-tidy-undocumented-unsafe let elem = unsafe { Some(self.buffer_read(wrapped_idx)) }; let k = self.len - index - 1; - // safety: due to the nature of the if-condition, whichever wrap_copy gets called, - // its length argument will be at most `self.len / 2`, so there can't be more than - // one overlapping area. if k < index { + // SAFETY: due to the nature of the if-condition, whichever wrap_copy gets called, + // its length argument will be at most `self.len / 2`, so there can't be more than + // one overlapping area. unsafe { self.wrap_copy(self.wrap_add(wrapped_idx, 1), wrapped_idx, k) }; self.len -= 1; } else { let old_head = self.head; self.head = self.to_wrapped_index(1); + // ignore-tidy-undocumented-unsafe unsafe { self.wrap_copy(old_head, self.head, index) }; self.len -= 1; } @@ -2679,6 +2723,7 @@ impl VecDeque { let first_len = first_half.len(); let second_len = second_half.len(); + // ignore-tidy-undocumented-unsafe unsafe { if at < first_len { // `at` lies in the first half. @@ -2740,6 +2785,7 @@ impl VecDeque { } self.reserve(other.len); + // ignore-tidy-undocumented-unsafe unsafe { let (left, right) = other.as_slices(); self.copy_slice(self.to_wrapped_index(self.len), left); @@ -2859,6 +2905,7 @@ impl VecDeque { debug_assert!(self.is_full()); let old_cap = self.capacity(); self.buf.grow_one(); + // ignore-tidy-undocumented-unsafe unsafe { self.handle_capacity_increase(old_cap); } @@ -2963,6 +3010,7 @@ impl VecDeque { } if self.is_contiguous() { + // ignore-tidy-undocumented-unsafe unsafe { return slice::from_raw_parts_mut(self.ptr().add(self.head.as_index()), self.len); } @@ -2988,6 +3036,7 @@ impl VecDeque { // // from: DEFGH....ABC // to: ABCDEFGH.... + // ignore-tidy-undocumented-unsafe unsafe { self.copy( WrappedIndex::zero(), @@ -3007,6 +3056,7 @@ impl VecDeque { // // from: FGH....ABCDE // to: ...ABCDEFGH. + // ignore-tidy-undocumented-unsafe unsafe { self.copy(head, tail, head_len); // FGHABCDE.... @@ -3039,6 +3089,7 @@ impl VecDeque { // 2. rotate used part of the buffer // 3. update head to point to the new beginning (which is just `free`) + // ignore-tidy-undocumented-unsafe unsafe { // if there is no free space in the buffer, then the slices are already // right next to each other and we don't need to move any memory. @@ -3071,6 +3122,7 @@ impl VecDeque { // 2. rotate used part of the buffer // 3. update head to point to the new beginning (which is the beginning of the buffer) + // ignore-tidy-undocumented-unsafe unsafe { // if there is no free space in the buffer, then the slices are already // right next to each other and we don't need to move any memory. @@ -3098,6 +3150,7 @@ impl VecDeque { } } + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts_mut(ptr.add(self.head.as_index()), self.len) } } @@ -3138,8 +3191,10 @@ impl VecDeque { assert!(n <= self.len()); let k = self.len - n; if n <= k { + // SAFETY: Ensured by check. unsafe { self.rotate_left_inner(n) } } else { + // SAFETY: Ensured by check. unsafe { self.rotate_right_inner(k) } } } @@ -3181,8 +3236,10 @@ impl VecDeque { assert!(n <= self.len()); let k = self.len - n; if n <= k { + // SAFETY: Ensured by check. unsafe { self.rotate_right_inner(n) } } else { + // SAFETY: Ensured by check. unsafe { self.rotate_left_inner(k) } } } @@ -3197,6 +3254,7 @@ impl VecDeque { unsafe fn rotate_left_inner(&mut self, mid: usize) { debug_assert!(mid * 2 <= self.len()); + // SAFETY: Upheld by caller. unsafe { self.wrap_copy(self.head, self.to_wrapped_index(self.len), mid); } @@ -3206,6 +3264,7 @@ impl VecDeque { unsafe fn rotate_right_inner(&mut self, k: usize) { debug_assert!(k * 2 <= self.len()); self.head = self.wrap_sub(self.head, k); + // SAFETY: Upheld by caller. unsafe { self.wrap_copy(self.to_wrapped_index(self.len), self.head, k); } @@ -3567,20 +3626,21 @@ impl SpecExtendFromWithin for VecDeque { let count = src.end - src.start; let src = src.start; - unsafe { - // SAFETY: - // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. - // - Ranges are in bounds: guaranteed by the caller. - let ranges = self.nonoverlapping_ranges(src, dst, count, self.head); - - // `len` is updated after every clone to prevent leaking and - // leave the deque in the right state when a clone implementation panics - - for (src, dst, count) in ranges { - for offset in 0..count { - dst.add(offset).write((*src.add(offset)).clone()); - self.len += 1; - } + // SAFETY: + // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. + // - Ranges are in bounds: guaranteed by the caller. + let ranges = unsafe { self.nonoverlapping_ranges(src, dst, count, self.head) }; + + // `len` is updated after every clone to prevent leaking and + // leave the deque in the right state when a clone implementation panics + + for (src, dst, count) in ranges { + for offset in 0..count { + // SAFETY: The allocations of `dst` and `src` go up to `count` elems, + // and `nonoverlapping_ranges` ensures `dst` and `src` are valid + // for writes and reads respectively. + unsafe { dst.add(offset).write((*src.add(offset)).clone()) }; + self.len += 1; } } } @@ -3593,45 +3653,50 @@ impl SpecExtendFromWithin for VecDeque { let new_head = self.wrap_sub(self.head, count); let cap = self.capacity(); - unsafe { - // SAFETY: - // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. - // - Ranges are in bounds: guaranteed by the caller. - let ranges = self.nonoverlapping_ranges(src, dst, count, new_head); - - // Cloning is done in reverse because we prepend to the front of the deque, - // we can't get holes in the *logical* buffer. - // `head` and `len` are updated after every clone to prevent leaking and - // leave the deque in the right state when a clone implementation panics - - // Clone the first range - let (src, dst, count) = ranges[1]; - for offset in (0..count).rev() { - dst.add(offset).write((*src.add(offset)).clone()); - self.head = self.head.sub(1); - self.len += 1; - } + // SAFETY: + // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. + // - Ranges are in bounds: guaranteed by the caller. + let ranges = unsafe { self.nonoverlapping_ranges(src, dst, count, new_head) }; + + // Cloning is done in reverse because we prepend to the front of the deque, + // we can't get holes in the *logical* buffer. + // `head` and `len` are updated after every clone to prevent leaking and + // leave the deque in the right state when a clone implementation panics + + // Clone the first range + let (src, dst, count) = ranges[1]; + for offset in (0..count).rev() { + // ignore-tidy-undocumented-unsafe + unsafe { dst.add(offset).write((*src.add(offset)).clone()) }; + // ignore-tidy-undocumented-unsafe + self.head = unsafe { self.head.sub(1) }; + self.len += 1; + } - // Clone the second range - let (src, dst, count) = ranges[0]; - let mut iter = (0..count).rev(); - if let Some(offset) = iter.next() { - dst.add(offset).write((*src.add(offset)).clone()); - // After the first clone of the second range, wrap `head` around - if self.head.is_zero() { - // SAFETY: the wrapped index may be temporarily equal to the capacity even if it - // is not zero, because we subtract it one line below. - self.head = WrappedIndex::from_arbitrary_number(cap); - } - self.head = self.head.sub(1); + // Clone the second range + let (src, dst, count) = ranges[0]; + let mut iter = (0..count).rev(); + if let Some(offset) = iter.next() { + // ignore-tidy-undocumented-unsafe + unsafe { dst.add(offset).write((*src.add(offset)).clone()) }; + // After the first clone of the second range, wrap `head` around + if self.head.is_zero() { + // SAFETY: the wrapped index may be temporarily equal to the capacity even if it + // is not zero, because we subtract it one line below. + // FIXME: should `from_arbitrary_number` be unsafe? its docs imply so... + self.head = WrappedIndex::from_arbitrary_number(cap); + } + // ignore-tidy-undocumented-unsafe + self.head = unsafe { self.head.sub(1) }; + self.len += 1; + + // Continue like normal + for offset in iter { + // ignore-tidy-undocumented-unsafe + unsafe { dst.add(offset).write((*src.add(offset)).clone()) }; + // ignore-tidy-undocumented-unsafe + self.head = unsafe { self.head.sub(1) }; self.len += 1; - - // Continue like normal - for offset in iter { - dst.add(offset).write((*src.add(offset)).clone()); - self.head = self.head.sub(1); - self.len += 1; - } } } } @@ -3644,14 +3709,13 @@ impl SpecExtendFromWithin for VecDeque { let count = src.end - src.start; let src = src.start; - unsafe { - // SAFETY: - // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. - // - Ranges are in bounds: guaranteed by the caller. - let ranges = self.nonoverlapping_ranges(src, dst, count, self.head); - for (src, dst, count) in ranges { - ptr::copy_nonoverlapping(src, dst, count); - } + // SAFETY: + // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. + // - Ranges are in bounds: guaranteed by the caller. + let ranges = unsafe { self.nonoverlapping_ranges(src, dst, count, self.head) }; + for (src, dst, count) in ranges { + // SAFETY: Ditto. + unsafe { ptr::copy_nonoverlapping(src, dst, count) }; } // SAFETY: @@ -3666,14 +3730,13 @@ impl SpecExtendFromWithin for VecDeque { let new_head = self.wrap_sub(self.head, count); - unsafe { - // SAFETY: - // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. - // - Ranges are in bounds: guaranteed by the caller. - let ranges = self.nonoverlapping_ranges(src, dst, count, new_head); - for (src, dst, count) in ranges { - ptr::copy_nonoverlapping(src, dst, count); - } + // SAFETY: + // - Ranges do not overlap: src entirely spans initialized values, dst entirely spans uninitialized values. + // - Ranges are in bounds: guaranteed by the caller. + let ranges = unsafe { self.nonoverlapping_ranges(src, dst, count, new_head) }; + for (src, dst, count) in ranges { + // SAFETY: Ditto. + unsafe { ptr::copy_nonoverlapping(src, dst, count) }; } // SAFETY: @@ -3996,6 +4059,7 @@ impl From> for VecDeque { Self { head: WrappedIndex::zero(), len, + // ignore-tidy-undocumented-unsafe buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) }, } } @@ -4035,6 +4099,7 @@ impl From> for Vec { fn from(mut other: VecDeque) -> Self { other.make_contiguous(); + // ignore-tidy-undocumented-unsafe unsafe { let other = ManuallyDrop::new(other); let buf = other.buf.ptr(); diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index 0699d403d9de5..fe39a0145d3a9 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -57,6 +57,7 @@ where ); self.reserve(additional); + // ignore-tidy-undocumented-unsafe let written = unsafe { self.write_iter_wrapping(self.to_wrapped_index(self.len), iter, additional) }; @@ -82,6 +83,7 @@ impl SpecExtend> for Ve let slice = iterator.as_slice(); self.reserve(slice.len()); + // ignore-tidy-undocumented-unsafe unsafe { self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); @@ -108,6 +110,7 @@ where let slice = iterator.as_slice(); self.reserve(slice.len()); + // ignore-tidy-undocumented-unsafe unsafe { self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); @@ -213,15 +216,16 @@ impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront> f } self.reserve(iter.remaining); + + // SAFETY: iter.remaining != 0. + let (left, right) = unsafe { iter.as_slices() }; + // SAFETY: + // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`. + // - The elements in `left` and `right` are forgotten after these calls. unsafe { - // SAFETY: iter.remaining != 0. - let (left, right) = iter.as_slices(); - // SAFETY: - // - `iter.remaining` space was reserved, `iter.remaining == left.len() + right.len()`. - // - The elements in `left` and `right` are forgotten after these calls. prepend_reversed(self, &*left); - prepend_reversed(self, &*right); - } + prepend_reversed(self, &*right) + }; iter.idx += iter.remaining; iter.remaining = 0; @@ -240,12 +244,13 @@ impl<'a, T, A1: Allocator, A2: Allocator> SpecExtendFront SpecExtendFront(deque: &mut VecDeque, slice: &[T]) { + // SAFETY: Upheld by caller. unsafe { deque.head = deque.wrap_sub(deque.head, slice.len()); deque.copy_slice(deque.head, slice); @@ -276,6 +282,7 @@ unsafe fn prepend(deque: &mut VecDeque, slice: &[T]) { /// - `deque` must have space for `slice.len()` new elements. /// - Elements of `slice` will be copied into the deque, make sure to forget the elements if `T` is not `Copy`. unsafe fn prepend_reversed(deque: &mut VecDeque, slice: &[T]) { + // SAFETY: Upheld by caller. unsafe { deque.head = deque.wrap_sub(deque.head, slice.len()); deque.copy_slice_reversed(deque.head, slice); diff --git a/library/alloc/src/collections/vec_deque/splice.rs b/library/alloc/src/collections/vec_deque/splice.rs index a29e9c3742564..4edd26c1e1e22 100644 --- a/library/alloc/src/collections/vec_deque/splice.rs +++ b/library/alloc/src/collections/vec_deque/splice.rs @@ -64,6 +64,7 @@ impl Drop for Splice<'_, I, A> { // At this point draining is done and the only remaining tasks are splicing // and moving things into the final place. + // ignore-tidy-undocumented-unsafe unsafe { let tail_len = self.drain.tail_len; // #elements behind the drain @@ -114,6 +115,7 @@ impl Drain<'_, T, A> { /// self.deque must be valid. self.deque.len and self.deque.len + self.drain_len must be less /// than twice the deque's capacity. unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + // ignore-tidy-undocumented-unsafe let deque = unsafe { self.deque.as_mut() }; let range_start = deque.len; let range_end = range_start + self.drain_len; @@ -121,6 +123,7 @@ impl Drain<'_, T, A> { for idx in range_start..range_end { if let Some(new_item) = replace_with.next() { let index = deque.to_wrapped_index(idx); + // ignore-tidy-undocumented-unsafe unsafe { deque.buffer_write(index, new_item) }; deque.len += 1; self.drain_len -= 1; @@ -137,6 +140,7 @@ impl Drain<'_, T, A> { /// /// self.deque must be valid. unsafe fn move_tail(&mut self, additional: usize) { + // SAFETY: Upheld by caller. let deque = unsafe { self.deque.as_mut() }; // `Drain::new` modifies the deque's len (so does `Drain::fill` here) @@ -182,6 +186,7 @@ impl Drain<'_, T, A> { } let new_tail_start = tail_start + additional; + // ignore-tidy-undocumented-unsafe unsafe { deque.wrap_copy( deque.to_wrapped_index(tail_start), diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b95804ddacecb..b6b6f77a6951e 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -264,6 +264,7 @@ impl CString { let bytes: Vec = self.into(); match memchr::memchr(0, &bytes) { Some(i) => Err(NulError(i, bytes)), + // SAFETY: We ensured there's no null bytes. None => Ok(unsafe { CString::_from_vec_unchecked(bytes) }), } } @@ -287,6 +288,7 @@ impl CString { // This allows better optimizations if lto enabled. match memchr::memchr(0, bytes) { Some(i) => Err(NulError(i, buffer)), + // SAFETY: We ensured there's no null bytes. None => Ok(unsafe { CString::_from_vec_unchecked(buffer) }), } } @@ -339,6 +341,7 @@ impl CString { #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_vec_unchecked(v: Vec) -> Self { debug_assert!(memchr::memchr(0, &v).is_none()); + // SAFETY: Upheld by caller. unsafe { Self::_from_vec_unchecked(v) } } @@ -478,6 +481,7 @@ impl CString { pub fn into_string(self) -> Result { String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError { error: e.utf8_error(), + // SAFETY: Strings never contain null bytes. inner: unsafe { Self::_from_vec_unchecked(e.into_bytes()) }, }) } @@ -584,6 +588,7 @@ impl CString { #[stable(feature = "as_c_str", since = "1.20.0")] #[rustc_diagnostic_item = "cstring_as_c_str"] pub fn as_c_str(&self) -> &CStr { + // SAFETY: Ensured by `as_bytes_with_nul`. unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } } @@ -599,17 +604,18 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "into_boxed_c_str", since = "1.20.0")] pub fn into_boxed_c_str(self) -> Box { + // SAFETY: Typecast of [u8] to CStr is valid and we know contents have no nulls. unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) } } /// Bypass "move out of struct which implements [`Drop`] trait" restriction. #[inline] fn into_inner(self) -> Box<[u8]> { - // Rationale: `mem::forget(self)` invalidates the previous call to `ptr::read(&self.inner)` + let this = mem::ManuallyDrop::new(self); + // SAFETY: `mem::forget(self)` invalidates the previous call to `ptr::read(&self.inner)` // so we use `ManuallyDrop` to ensure `self` is not dropped. // Then we can return the box directly without invalidating it. // See https://github.com/rust-lang/rust/issues/62553. - let this = mem::ManuallyDrop::new(self); unsafe { ptr::read(&this.inner) } } @@ -634,6 +640,7 @@ impl CString { #[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")] pub unsafe fn from_vec_with_nul_unchecked(v: Vec) -> Self { debug_assert!(memchr::memchr(0, &v).unwrap() + 1 == v.len()); + // SAFETY: Upheld by caller. unsafe { Self::_from_vec_with_nul_unchecked(v) } } @@ -702,6 +709,7 @@ impl CString { impl Drop for CString { #[inline] fn drop(&mut self) { + // SAFETY: Length is always at least one. unsafe { *self.inner.get_unchecked_mut(0) = 0; } @@ -802,6 +810,7 @@ impl From> for CString { #[inline] fn from(s: Box) -> CString { let raw = Box::into_raw(s) as *mut [u8]; + // SAFETY: Converting a *mut CStr -> *mut [u8] -> CString is valid. CString { inner: unsafe { Box::from_raw(raw) } } } } @@ -812,19 +821,17 @@ impl From>> for CString { /// copying nor checking for inner nul bytes. #[inline] fn from(v: Vec>) -> CString { - unsafe { - // Transmute `Vec>` to `Vec`. - let v: Vec = { - // SAFETY: - // - transmuting between `NonZero` and `u8` is sound; - // - `alloc::Layout> == alloc::Layout`. - let (ptr, len, cap): (*mut NonZero, _, _) = Vec::into_raw_parts(v); - Vec::from_raw_parts(ptr.cast::(), len, cap) - }; - // SAFETY: `v` cannot contain nul bytes, given the type-level - // invariant of `NonZero`. - Self::_from_vec_unchecked(v) - } + // Transmute `Vec>` to `Vec`. + let v: Vec = { + let (ptr, len, cap): (*mut NonZero, _, _) = Vec::into_raw_parts(v); + // SAFETY: + // - transmuting between `NonZero` and `u8` is sound; + // - `alloc::Layout> == alloc::Layout`. + unsafe { Vec::from_raw_parts(ptr.cast::(), len, cap) } + }; + // SAFETY: `v` cannot contain nul bytes, given the type-level + // invariant of `NonZero`. + unsafe { Self::_from_vec_unchecked(v) } } } @@ -906,6 +913,7 @@ impl From for Arc { #[inline] fn from(s: CString) -> Arc { let arc: Arc<[u8]> = Arc::from(s.into_inner()); + // SAFETY: Type conversion is valid. unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } } } @@ -918,6 +926,7 @@ impl From<&CStr> for Arc { #[inline] fn from(s: &CStr) -> Arc { let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul()); + // SAFETY: Type conversion is valid. unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } } } @@ -940,6 +949,7 @@ impl From for Rc { #[inline] fn from(s: CString) -> Rc { let rc: Rc<[u8]> = Rc::from(s.into_inner()); + // SAFETY: Type conversion is valid. unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } } } @@ -951,6 +961,7 @@ impl From<&CStr> for Rc { #[inline] fn from(s: &CStr) -> Rc { let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul()); + // SAFETY: Type conversion is valid. unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } } } diff --git a/library/alloc/src/io/buf_read.rs b/library/alloc/src/io/buf_read.rs index bba1c2b8c8a45..9509bd0f8fbc9 100644 --- a/library/alloc/src/io/buf_read.rs +++ b/library/alloc/src/io/buf_read.rs @@ -343,6 +343,7 @@ pub trait BufRead: Read { // Note that we are not calling the `.read_until` method here, but // rather our hardcoded implementation. For more details as to why, see // the comments in `default_read_to_string`. + // ignore-tidy-undocumented-unsafe unsafe { append_to_string(buf, |b| default_read_until(self, b'\n', b)) } } diff --git a/library/alloc/src/io/buffered/bufreader.rs b/library/alloc/src/io/buffered/bufreader.rs index e8be1abc56e17..c911746c2a611 100644 --- a/library/alloc/src/io/buffered/bufreader.rs +++ b/library/alloc/src/io/buffered/bufreader.rs @@ -456,6 +456,7 @@ impl Read for BufReader { // bytes but also modify existing bytes and render them invalid. On the other hand, // if `buf` is empty then by definition any writes must be appends and // `append_to_string` will validate all of the new bytes. + // ignore-tidy-undocumented-unsafe unsafe { crate::io::append_to_string(buf, |b| self.read_to_end(b)) } } else { // We cannot append our byte buffer directly onto the `buf` String as there could diff --git a/library/alloc/src/io/buffered/bufwriter.rs b/library/alloc/src/io/buffered/bufwriter.rs index 806e71c2772ae..d7e09675968e1 100644 --- a/library/alloc/src/io/buffered/bufwriter.rs +++ b/library/alloc/src/io/buffered/bufwriter.rs @@ -470,6 +470,7 @@ impl BufWriter { let old_len = self.buf.len(); let buf_len = buf.len(); let src = buf.as_ptr(); + // ignore-tidy-undocumented-unsafe unsafe { let dst = self.buf.as_mut_ptr().add(old_len); ptr::copy_nonoverlapping(src, dst, buf_len); diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs index 4bd5a59e54fad..7e05a079fc054 100644 --- a/library/alloc/src/io/cursor.rs +++ b/library/alloc/src/io/cursor.rs @@ -155,7 +155,7 @@ fn reserve_and_pad( // to eliminate that extra branch let spare = vec.spare_capacity_mut(); debug_assert!(spare.len() >= diff); - // Safety: we have allocated enough capacity for this. + // SAFETY: we have allocated enough capacity for this. // And we are only writing, not reading unsafe { spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); @@ -176,6 +176,7 @@ where A: Allocator, { debug_assert!(vec.capacity() >= pos + buf.len()); + // SAFETY: Upheld by caller. unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; pos + buf.len() } @@ -199,7 +200,7 @@ where let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available + // SAFETY: we have ensured that the capacity is available // and that all bytes get written up to pos unsafe { pos = vec_write_all_unchecked(pos, vec, buf); @@ -237,8 +238,8 @@ where let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available + // Write the buf then progress the vec forward if necessary. + // SAFETY: We have ensured that the capacity is available // and that all bytes get written up to the last pos unsafe { for buf in bufs { diff --git a/library/alloc/src/io/error.rs b/library/alloc/src/io/error.rs index 7b3e4b06ae38a..0e3768c9a7ad8 100644 --- a/library/alloc/src/io/error.rs +++ b/library/alloc/src/io/error.rs @@ -215,7 +215,7 @@ impl Error { { Ok(*err) } else { - // Safety: We have just checked that the condition is true + // SAFETY: We have just checked that the condition is true unsafe { core::hint::unreachable_unchecked() } } } else { @@ -256,8 +256,7 @@ fn custom_owner_from_box( /// /// `ptr` must be valid to pass into `Box::from_raw`. unsafe fn drop_box_raw(ptr: NonNull) { - // SAFETY - // Caller ensures `ptr` is valid to pass into `Box::from_raw`. + // SAFETY: Caller ensures `ptr` is valid to pass into `Box::from_raw`. drop(unsafe { Box::from_non_null(ptr) }) } diff --git a/library/alloc/src/io/read.rs b/library/alloc/src/io/read.rs index 502eddc827bfc..a05dade2bdcf6 100644 --- a/library/alloc/src/io/read.rs +++ b/library/alloc/src/io/read.rs @@ -640,6 +640,7 @@ pub trait Read { self.read_buf_exact(borrowed_buf.unfilled())?; // Guard against incorrect `read_buf_exact` implementations. assert_eq!(borrowed_buf.len(), N); + // SAFETY: Buffer was initialised above. Ok(unsafe { MaybeUninit::array_assume_init(buf) }) } @@ -814,6 +815,7 @@ where let len_original = buf.len(); // SAFETY: invalid UTF-8 discarded before return or unwind let buf_vec = unsafe { buf.as_mut_vec() }; + // ignore-tidy-undocumented-unsafe let mut g = DropGuard::new((len_original, buf_vec), |(len, buf)| unsafe { buf.set_len(len); }); @@ -1018,6 +1020,7 @@ pub fn default_read_to_string( // To prevent extraneously checking the UTF-8-ness of the entire buffer // we pass it to our hardcoded `default_read_to_end` implementation which // we know is guaranteed to only read data into the end of the buffer. + // ignore-tidy-undocumented-unsafe unsafe { append_to_string(buf, |b| default_read_to_end(r, b, size_hint)) } } diff --git a/library/alloc/src/io/util.rs b/library/alloc/src/io/util.rs index 9bf5a56dd7157..b4cc4046bae4b 100644 --- a/library/alloc/src/io/util.rs +++ b/library/alloc/src/io/util.rs @@ -287,8 +287,8 @@ impl Read for Take { unsafe { buf.set_init() }; } + // SAFETY: filled bytes have been filled unsafe { - // SAFETY: filled bytes have been filled buf.advance(filled); } diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 5d4ad3ac4bf98..98444f06f65ae 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -38,12 +38,14 @@ enum AllocInit { type Cap = core::num::niche_types::UsizeNoHighBit; +// SAFETY: 0 *definitely* is less than isize::MAX. const ZERO_CAP: Cap = unsafe { Cap::new_unchecked(0) }; /// `Cap(cap)`, except if `T` is a ZST then `Cap::ZERO`. /// /// # Safety: cap must be <= `isize::MAX`. const unsafe fn new_cap(cap: usize) -> Cap { + // SAFETY: Upheld by caller. if T::IS_ZST { ZERO_CAP } else { unsafe { Cap::new_unchecked(cap) } } } @@ -243,6 +245,7 @@ impl RawVec { ); let me = ManuallyDrop::new(self); + // ignore-tidy-undocumented-unsafe unsafe { let slice = me.ptr().cast::>().cast_slice(len); Box::from_raw_in(slice, ptr::read(&me.inner.alloc)) @@ -413,6 +416,7 @@ impl RawVec { /// Panics if the given amount is *larger* than the current capacity. #[inline] pub(crate) fn try_shrink_to_fit(&mut self, cap: usize) -> Result<(), TryReserveError> { + // SAFETY: Layout is valid for T. unsafe { self.inner.try_shrink_to_fit(cap, T::LAYOUT) } } } @@ -434,6 +438,7 @@ const impl RawVecInner { fn with_capacity_in(capacity: usize, alloc: A, elem_layout: Layout) -> Self { match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) { Ok(this) => { + // ignore-tidy-undocumented-unsafe unsafe { // Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate. hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout)); @@ -477,6 +482,7 @@ const impl RawVecInner { // here should change to `ptr.len() / size_of::()`. Ok(Self { ptr: Unique::from(ptr.cast()), + // ignore-tidy-undocumented-unsafe cap: unsafe { Cap::new_unchecked(capacity) }, alloc, }) @@ -548,9 +554,11 @@ const impl RawVecInner { ) -> Result, TryReserveError> { let new_layout = layout_array(cap, elem_layout)?; + // ignore-tidy-undocumented-unsafe let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } { // FIXME(const-hack): switch to `debug_assert_eq` debug_assert!(old_layout.align() == new_layout.align()); + // SAFETY: Upheld by caller. unsafe { // The allocator checks for alignment equality hint::assert_unchecked(old_layout.align() == new_layout.align()); @@ -592,6 +600,7 @@ impl RawVecInner { #[inline] const unsafe fn from_raw_parts_in(ptr: *mut u8, cap: Cap, alloc: A) -> Self { + // SAFETY: Upheld by caller. Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap, alloc } } @@ -635,6 +644,7 @@ impl RawVecInner { // and could hypothetically handle differences between stride and size, but this memory // has already been allocated so we know it can't overflow and currently Rust does not // support such types. So we can do better by skipping some checks and avoid an unwrap. + // ignore-tidy-undocumented-unsafe unsafe { let alloc_size = elem_layout.size().unchecked_mul(self.cap.as_inner()); let layout = Layout::from_size_align_unchecked(alloc_size, elem_layout.align()); @@ -668,6 +678,7 @@ impl RawVecInner { } if self.needs_to_grow(len, additional, elem_layout) { + // ignore-tidy-undocumented-unsafe unsafe { do_reserve_and_handle(self, len, additional, elem_layout); } @@ -690,6 +701,7 @@ impl RawVecInner { self.grow_amortized(len, additional, elem_layout)?; } } + // ignore-tidy-undocumented-unsafe unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); @@ -725,6 +737,7 @@ impl RawVecInner { self.grow_exact(len, additional, elem_layout)?; } } + // ignore-tidy-undocumented-unsafe unsafe { // Inform the optimizer that the reservation has succeeded or wasn't needed hint::assert_unchecked(!self.needs_to_grow(len, additional, elem_layout)); @@ -740,6 +753,7 @@ impl RawVecInner { #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) { + // SAFETY: Upheld by caller. if let Err(err) = unsafe { self.shrink(cap, elem_layout) } { handle_error(err); } @@ -756,6 +770,7 @@ impl RawVecInner { cap: usize, elem_layout: Layout, ) -> Result<(), TryReserveError> { + // SAFETY: Upheld by caller. unsafe { self.shrink(cap, elem_layout) } } @@ -771,6 +786,7 @@ impl RawVecInner { // the size requested. If that ever changes, the capacity here should // change to `ptr.len() / size_of::()`. self.ptr = Unique::from(ptr.cast()); + // SAFETY: Upheld by caller. self.cap = unsafe { Cap::new_unchecked(cap) }; } @@ -837,11 +853,14 @@ impl RawVecInner { // for the T::IS_ZST case since current_memory() will have returned // None. if cap == 0 { + // ignore-tidy-undocumented-unsafe unsafe { self.alloc.deallocate(ptr, layout) }; self.ptr = + // ignore-tidy-undocumented-unsafe unsafe { Unique::new_unchecked(ptr::without_provenance_mut(elem_layout.align())) }; self.cap = ZERO_CAP; } else { + // ignore-tidy-undocumented-unsafe let ptr = unsafe { // Layout cannot overflow here because it would have // overflowed earlier when capacity was larger. @@ -870,8 +889,9 @@ const impl RawVecInner { /// Ideally this function would take `self` by move, but it cannot because it exists to be /// called from a `Drop` impl. unsafe fn deallocate(&mut self, elem_layout: Layout) { - // SAFETY: Precondition passed to caller + // ignore-tidy-undocumented-unsafe if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } { + // SAFETY: Precondition passed to caller unsafe { self.alloc.deallocate(ptr, layout); } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 37714859ede38..2595dd2105e1a 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -359,11 +359,13 @@ unsafe impl CloneFromCell for Rc {} impl Rc { #[inline] unsafe fn from_inner(ptr: NonNull>) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_inner_in(ptr, Global) } } #[inline] unsafe fn from_ptr(ptr: *mut RcInner) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_inner(NonNull::new_unchecked(ptr)) } } } @@ -371,7 +373,7 @@ impl Rc { impl Rc { #[inline(always)] fn inner(&self) -> &RcInner { - // This unsafety is ok because while this Rc is alive we're guaranteed + // SAFETY: While this Rc is alive we're guaranteed // that the inner pointer is valid. unsafe { self.ptr.as_ref() } } @@ -379,6 +381,7 @@ impl Rc { #[inline] fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { let this = mem::ManuallyDrop::new(this); + // SAFETY: Pulling out the allocator we already own. (this.ptr, unsafe { ptr::read(&this.alloc) }) } @@ -389,6 +392,7 @@ impl Rc { #[inline] unsafe fn from_ptr_in(ptr: *mut RcInner, alloc: A) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_inner_in(NonNull::new_unchecked(ptr), alloc) } } @@ -402,6 +406,7 @@ impl Rc { // Destroy the contained object. // We cannot use `get_mut_unchecked` here, because `self.alloc` is borrowed. + // SAFETY: `self.ptr` is *not* borrowed. unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).value); } @@ -421,7 +426,7 @@ impl Rc { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn new(value: T) -> Rc { - // There is an implicit weak pointer owned by all the strong + // SAFETY: There is an implicit weak pointer owned by all the strong // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. @@ -513,6 +518,7 @@ impl Rc { #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit() -> Rc> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr(Rc::allocate_for_layout( Layout::new::(), @@ -544,6 +550,7 @@ impl Rc { #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed() -> Rc> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr(Rc::allocate_for_layout( Layout::new::(), @@ -566,7 +573,7 @@ impl Rc { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn try_new(value: T) -> Result, AllocError> { - // There is an implicit weak pointer owned by all the strong + // SAFETY: There is an implicit weak pointer owned by all the strong // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. @@ -603,6 +610,7 @@ impl Rc { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn try_new_uninit() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( Layout::new::(), @@ -635,6 +643,7 @@ impl Rc { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] pub fn try_new_zeroed() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Rc::from_ptr(Rc::try_allocate_for_layout( Layout::new::(), @@ -649,6 +658,7 @@ impl Rc { #[stable(feature = "pin", since = "1.33.0")] #[must_use] pub fn pin(value: T) -> Pin> { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Rc::new(value)) } } } @@ -704,6 +714,7 @@ impl Rc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_uninit_in(alloc: A) -> Rc, A> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr_in( Rc::allocate_for_layout( @@ -741,6 +752,7 @@ impl Rc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_zeroed_in(alloc: A) -> Rc, A> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr_in( Rc::allocate_for_layout( @@ -798,6 +810,7 @@ impl Rc { }, alloc, )); + // ignore-tidy-undocumented-unsafe let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); @@ -811,6 +824,7 @@ impl Rc { // otherwise. let data = data_fn(&weak); + // ignore-tidy-undocumented-unsafe unsafe { let inner = init_ptr.as_ptr(); ptr::write(&raw mut (*inner).value, data); @@ -853,6 +867,7 @@ impl Rc { RcInner { strong: Cell::new(1), weak: Cell::new(1), value }, alloc, )?); + // ignore-tidy-undocumented-unsafe Ok(unsafe { Self::from_inner_in(ptr.into(), alloc) }) } @@ -883,6 +898,7 @@ impl Rc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Rc::from_ptr_in( Rc::try_allocate_for_layout( @@ -921,6 +937,7 @@ impl Rc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Rc::from_ptr_in( Rc::try_allocate_for_layout( @@ -942,6 +959,7 @@ impl Rc { where A: 'static, { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Rc::new_in(value, alloc)) } } @@ -970,7 +988,9 @@ impl Rc { if Rc::strong_count(&this) == 1 { let this = ManuallyDrop::new(this); + // ignore-tidy-undocumented-unsafe let val: T = unsafe { ptr::read(&**this) }; // copy the contained object + // ignore-tidy-undocumented-unsafe let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator // Indicate to Weaks that they can't be promoted by decrementing @@ -1048,6 +1068,7 @@ impl Rc { && align_of::() == align_of::() && Rc::is_unique(&this) { + // ignore-tidy-undocumented-unsafe unsafe { let (ptr, alloc) = Rc::into_raw_with_allocator(this); let value = ptr.read(); @@ -1059,6 +1080,7 @@ impl Rc { } else { let output = f(&*this); let (ptr, alloc) = Rc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe unsafe { Rc::decrement_strong_count_in(ptr, &alloc) } Rc::new_in(output, alloc) @@ -1099,6 +1121,7 @@ impl Rc { && align_of::() == align_of::() && Rc::is_unique(&this) { + // ignore-tidy-undocumented-unsafe unsafe { let (ptr, alloc) = Rc::into_raw_with_allocator(this); let value = ptr.read(); @@ -1111,6 +1134,7 @@ impl Rc { } else { let output = f(&*this)?; let (ptr, alloc) = Rc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe unsafe { Rc::decrement_strong_count_in(ptr, &alloc) } try { Rc::new_in(output, alloc) } @@ -1142,6 +1166,7 @@ impl Rc<[T]> { #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Rc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr(Rc::allocate_for_slice(len)) } } @@ -1167,6 +1192,7 @@ impl Rc<[T]> { #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Rc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr(Rc::allocate_for_layout( Layout::array::(len).unwrap(), @@ -1206,6 +1232,7 @@ impl Rc<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr_in(Rc::allocate_for_slice_in(len, &alloc), alloc) } } @@ -1234,6 +1261,7 @@ impl Rc<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Rc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr_in( Rc::allocate_for_layout( @@ -1311,6 +1339,7 @@ impl Rc, A> { #[inline] pub unsafe fn assume_init(self) -> Rc { let (ptr, alloc) = Rc::into_inner_with_allocator(self); + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_inner_in(ptr.cast(), alloc) } } } @@ -1372,6 +1401,7 @@ impl Rc { let mut in_progress: UniqueRcUninit = UniqueRcUninit::new(value, alloc); // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe unsafe { // Clone. If the clone panics, `in_progress` will be dropped and clean up. value.clone_to_uninit(in_progress.data_ptr().cast()); @@ -1400,6 +1430,7 @@ impl Rc { let mut in_progress: UniqueRcUninit = UniqueRcUninit::try_new(value, alloc)?; // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe let initialized_clone = unsafe { // Clone. If the clone panics, `in_progress` will be dropped and clean up. value.clone_to_uninit(in_progress.data_ptr().cast()); @@ -1445,6 +1476,7 @@ impl Rc<[mem::MaybeUninit], A> { #[inline] pub unsafe fn assume_init(self) -> Rc<[T], A> { let (ptr, alloc) = Rc::into_inner_with_allocator(self); + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_ptr_in(ptr.as_ptr() as _, alloc) } } } @@ -1517,6 +1549,7 @@ impl Rc { #[inline] #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { + // ignore-tidy-undocumented-unsafe unsafe { Self::from_raw_in(ptr, Global) } } @@ -1575,6 +1608,7 @@ impl Rc { #[inline] #[stable(feature = "rc_mutate_strong_count", since = "1.53.0")] pub unsafe fn increment_strong_count(ptr: *const T) { + // ignore-tidy-undocumented-unsafe unsafe { Self::increment_strong_count_in(ptr, Global) } } @@ -1612,6 +1646,7 @@ impl Rc { #[inline] #[stable(feature = "rc_mutate_strong_count", since = "1.53.0")] pub unsafe fn decrement_strong_count(ptr: *const T) { + // ignore-tidy-undocumented-unsafe unsafe { Self::decrement_strong_count_in(ptr, Global) } } @@ -1649,9 +1684,13 @@ impl Rc { #[inline] #[unstable(feature = "arc_raw_get_strong", issue = "157021")] pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { + // SAFETY: Upheld by caller. let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcInner. + // SAFETY: Caller ensures this pointer was to an `Rc` allocation, + // so offsetting must be inbounds. let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut RcInner }; + // SAFETY: Per the above, an `RcInner` is stored here. unsafe { (*rc_ptr).strong.get() } } } @@ -1691,7 +1730,7 @@ impl Rc { pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); let ptr = Self::as_ptr(&this); - // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped let alloc = unsafe { ptr::read(&this.alloc) }; (ptr, alloc) } @@ -1795,11 +1834,14 @@ impl Rc { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { + // ignore-tidy-undocumented-unsafe let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcInner. + // ignore-tidy-undocumented-unsafe let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut RcInner }; + // ignore-tidy-undocumented-unsafe unsafe { Self::from_ptr_in(rc_ptr, alloc) } } @@ -1903,6 +1945,7 @@ impl Rc { A: AllocatorClone, { // Retain Rc, but don't touch refcount by wrapping in ManuallyDrop + // ignore-tidy-undocumented-unsafe let rc = unsafe { mem::ManuallyDrop::new(Rc::::from_raw_in(ptr, alloc)) }; // Now increase refcount, but don't drop new refcount either let _rc_clone: mem::ManuallyDrop<_> = rc.clone(); @@ -1945,6 +1988,7 @@ impl Rc { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn decrement_strong_count_in(ptr: *const T, alloc: A) { + // SAFETY: Upheld by caller. unsafe { drop(Rc::from_raw_in(ptr, alloc)) }; } @@ -1982,6 +2026,7 @@ impl Rc { #[inline] #[stable(feature = "rc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { + // SAFETY: Ensured by uniqueness check. if Rc::is_unique(this) { unsafe { Some(Rc::get_mut_unchecked(this)) } } else { None } } @@ -2050,6 +2095,7 @@ impl Rc { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { // We are careful to *not* create a reference covering the "count" fields, as // this would conflict with accesses to the reference counts (e.g. by `Weak`). + // ignore-tidy-undocumented-unsafe unsafe { &mut (*this.ptr.as_ptr()).value } } @@ -2140,6 +2186,7 @@ impl Rc { let mut in_progress: UniqueRcUninit = UniqueRcUninit::new(&**this, this.alloc.clone()); + // ignore-tidy-undocumented-unsafe unsafe { // Initialize `in_progress` with move of **this. // We have to express this in terms of bytes because `T: ?Sized`; there is no @@ -2166,7 +2213,7 @@ impl Rc { ptr::write(this, in_progress.into_rc()); } } - // This unsafety is ok because we're guaranteed that the pointer + // SAFETY: We're guaranteed that the pointer // returned is the *only* pointer that will ever be returned to T. Our // reference count is guaranteed to be 1 at this point, and we required // the `Rc` itself to be `mut`, so we're returning the only possible @@ -2234,6 +2281,7 @@ impl Rc { #[stable(feature = "rc_downcast", since = "1.29.0")] pub fn downcast(self) -> Result, Self> { if (*self).is::() { + // SAFETY: Check ensures typecast is corrext. unsafe { let (ptr, alloc) = Rc::into_inner_with_allocator(self); Ok(Rc::from_inner_in(ptr.cast(), alloc)) @@ -2272,6 +2320,7 @@ impl Rc { #[inline] #[unstable(feature = "downcast_unchecked", issue = "90850")] pub unsafe fn downcast_unchecked(self) -> Rc { + // SAFETY: Check ensures typecast is correct. unsafe { let (ptr, alloc) = Rc::into_inner_with_allocator(self); Rc::from_inner_in(ptr.cast(), alloc) @@ -2292,6 +2341,7 @@ impl Rc { mem_to_rc_inner: impl FnOnce(*mut u8) -> *mut RcInner, ) -> *mut RcInner { let layout = rc_inner_layout_for_value_layout(value_layout); + // ignore-tidy-undocumented-unsafe unsafe { Rc::try_allocate_for_layout(value_layout, allocate, mem_to_rc_inner) .unwrap_or_else(|_| handle_alloc_error(layout)) @@ -2317,6 +2367,7 @@ impl Rc { // Initialize the RcInner let inner = mem_to_rc_inner(ptr.as_non_null_ptr().as_ptr()); + // ignore-tidy-undocumented-unsafe unsafe { debug_assert_eq!(Layout::for_value_raw(inner), layout); @@ -2333,6 +2384,7 @@ impl Rc { #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_ptr_in(ptr: *const T, alloc: &A) -> *mut RcInner { // Allocate for the `RcInner` using the given value. + // ignore-tidy-undocumented-unsafe unsafe { Rc::::allocate_for_layout( Layout::for_value_raw(ptr), @@ -2344,6 +2396,7 @@ impl Rc { #[cfg(not(no_global_oom_handling))] fn from_box_in(src: Box) -> Rc { + // ignore-tidy-undocumented-unsafe unsafe { let value_size = size_of_val(&*src); let ptr = Self::allocate_for_ptr_in(&*src, Box::allocator(&src)); @@ -2369,6 +2422,7 @@ impl Rc<[T]> { /// Allocates an `RcInner<[T]>` with the given length. #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_slice(len: usize) -> *mut RcInner<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { Self::allocate_for_layout( Layout::array::(len).unwrap(), @@ -2384,6 +2438,7 @@ impl Rc<[T]> { /// bind `T: TrivialClone`. #[cfg(not(no_global_oom_handling))] unsafe fn copy_from_slice(v: &[T]) -> Rc<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(v.len()); ptr::copy_nonoverlapping(v.as_ptr(), (&raw mut (*ptr).value) as *mut T, v.len()); @@ -2408,6 +2463,7 @@ impl Rc<[T]> { impl Drop for Guard { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); @@ -2417,6 +2473,7 @@ impl Rc<[T]> { } } + // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len); @@ -2446,6 +2503,7 @@ impl Rc<[T], A> { #[inline] #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_slice_in(len: usize, alloc: &A) -> *mut RcInner<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { Rc::<[T]>::allocate_for_layout( Layout::array::(len).unwrap(), @@ -2466,6 +2524,7 @@ trait RcFromSlice { impl RcFromSlice for Rc<[T]> { #[inline] default fn from_slice(v: &[T]) -> Self { + // ignore-tidy-undocumented-unsafe unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } } } @@ -2543,6 +2602,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Rc { /// ``` #[inline] fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { self.inner().dec_strong(); if self.inner().strong() == 0 { @@ -2570,6 +2630,7 @@ impl Clone for Rc { /// ``` #[inline] fn clone(&self) -> Self { + // ignore-tidy-undocumented-unsafe unsafe { self.inner().inc_strong(); Self::from_inner_in(self.ptr, self.alloc.clone()) @@ -2598,6 +2659,7 @@ impl Default for Rc { /// ``` #[inline] fn default() -> Self { + // ignore-tidy-undocumented-unsafe unsafe { Self::from_inner( Box::leak(Box::write( @@ -2619,7 +2681,7 @@ impl Default for Rc { #[inline] fn default() -> Self { let rc = Rc::<[u8]>::default(); - // `[u8]` has the same layout as `str`. + // SAFETY: `[u8]` has the same layout as `str`. unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } } } @@ -2646,6 +2708,7 @@ where { #[inline] fn default() -> Self { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Rc::::default()) } } } @@ -2986,6 +3049,7 @@ impl From<&str> for Rc { #[inline] fn from(v: &str) -> Rc { let rc = Rc::<[u8]>::from(v.as_bytes()); + // ignore-tidy-undocumented-unsafe unsafe { Rc::from_raw(Rc::into_raw(rc) as *const str) } } } @@ -3063,6 +3127,7 @@ impl From> for Rc<[T], A> { /// ``` #[inline] fn from(v: Vec) -> Rc<[T], A> { + // ignore-tidy-undocumented-unsafe unsafe { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_allocator(); @@ -3131,6 +3196,7 @@ impl TryFrom> for Rc<[T; N], A> { fn try_from(boxed_slice: Rc<[T], A>) -> Result { if boxed_slice.len() == N { let (ptr, alloc) = Rc::into_inner_with_allocator(boxed_slice); + // ignore-tidy-undocumented-unsafe Ok(unsafe { Rc::from_inner_in(ptr.cast(), alloc) }) } else { Err(boxed_slice) @@ -3210,10 +3276,8 @@ impl> ToRcSlice for I { (low, high) ); - unsafe { - // SAFETY: We need to ensure that the iterator has an exact length and we have. - Rc::from_iter_exact(self, low) - } + // SAFETY: We need to ensure that the iterator has an exact length and we have. + unsafe { Rc::from_iter_exact(self, low) } } else { // TrustedLen contract guarantees that `upper_bound == None` implies an iterator // length exceeding `usize::MAX`. @@ -3378,6 +3442,7 @@ impl Weak { #[inline] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_raw_in(ptr, Global) } } @@ -3500,7 +3565,7 @@ impl Weak { pub fn into_raw_with_allocator(self) -> (*const T, A) { let this = mem::ManuallyDrop::new(self); let result = this.as_ptr(); - // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) } @@ -3609,6 +3674,7 @@ impl Weak { if inner.strong() == 0 { None } else { + // ignore-tidy-undocumented-unsafe unsafe { inner.inc_strong(); Some(Rc::from_inner_in(self.ptr, self.alloc.clone())) @@ -3652,6 +3718,7 @@ impl Weak { // We are careful to *not* create a reference covering the "data" field, as // the field may be mutated concurrently (for example, if the last `Rc` // is dropped, the data field will be dropped in-place). + // ignore-tidy-undocumented-unsafe Some(unsafe { let ptr = self.ptr.as_ptr(); WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } @@ -3739,6 +3806,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if inner.weak() == 0 { + // ignore-tidy-undocumented-unsafe unsafe { self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())); } @@ -4290,6 +4358,7 @@ impl UniqueRc { && align_of::() == align_of::() && UniqueRc::weak_count(&this) == 0 { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = UniqueRc::into_raw(this); let value = ptr.read(); @@ -4338,6 +4407,7 @@ impl UniqueRc { && align_of::() == align_of::() && UniqueRc::weak_count(&this) == 0 { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = UniqueRc::into_raw(this); let value = ptr.read(); @@ -4354,6 +4424,7 @@ impl UniqueRc { #[cfg(not(no_global_oom_handling))] fn unwrap(this: Self) -> T { let this = ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads. let val: T = unsafe { ptr::read(&**this) }; let _weak = Weak { ptr: this.ptr, alloc: Global }; @@ -4365,12 +4436,15 @@ impl UniqueRc { impl UniqueRc { #[cfg(not(no_global_oom_handling))] unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Caller upholds that data behind pointer is initialised & correct. let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original RcInner. + // SAFETY: As above. let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut RcInner }; Self { + // SAFETY: Upheld by caller. ptr: unsafe { NonNull::new_unchecked(rc_ptr) }, _marker: PhantomData, _marker2: PhantomData, @@ -4459,6 +4533,7 @@ impl UniqueRc { #[cfg(not(no_global_oom_handling))] fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { let this = mem::ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads. (this.ptr, unsafe { ptr::read(&this.alloc) }) } @@ -4489,6 +4564,7 @@ impl UniqueRc { impl UniqueRc, A> { unsafe fn assume_init(self) -> UniqueRc { let (ptr, alloc) = UniqueRc::into_inner_with_allocator(self); + // SAFETY: Upheld by caller. unsafe { UniqueRc::from_inner_in(ptr.cast(), alloc) } } } @@ -4516,6 +4592,7 @@ impl DerefMut for UniqueRc { #[unstable(feature = "unique_rc_arc", issue = "112566")] unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueRc { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { // destroy the contained object drop_in_place(DerefMut::deref_mut(self)); @@ -4547,6 +4624,7 @@ impl UniqueRcUninit { #[cfg(not(no_global_oom_handling))] fn new(for_value: &T, alloc: A) -> UniqueRcUninit { let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe let ptr = unsafe { Rc::allocate_for_layout( layout, @@ -4561,6 +4639,7 @@ impl UniqueRcUninit { /// returning an error if allocation fails. fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe let ptr = unsafe { Rc::try_allocate_for_layout( layout, @@ -4574,6 +4653,7 @@ impl UniqueRcUninit { /// Returns the pointer to be written into to initialize the [`Rc`]. fn data_ptr(&mut self) -> *mut T { let offset = data_offset_alignment(self.layout_for_value.alignment()); + // ignore-tidy-undocumented-unsafe unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index e6b540f093ba5..c950569e9838b 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -446,10 +446,10 @@ impl [T] { fn to_vec(s: &[Self], alloc: A) -> Vec { let len = s.len(); let mut v = Vec::with_capacity_in(len, alloc); - // SAFETY: - // allocated above with the capacity of `s`, and initialize to `s.len()` in - // ptr::copy_to_non_overlapping below. if len > 0 { + // SAFETY: + // allocated above with the capacity of `s`, and initialize to `s.len()` in + // ptr::copy_to_non_overlapping below. unsafe { s.as_ptr().copy_to_nonoverlapping(v.as_mut_ptr(), len); v.set_len(len); @@ -479,6 +479,7 @@ impl [T] { #[rustc_const_unstable(feature = "const_heap", issue = "79597")] #[inline] pub const fn into_vec(self: Box) -> Vec { + // ignore-tidy-undocumented-unsafe unsafe { let len = self.len(); let (b, alloc) = Box::into_raw_with_allocator(self); @@ -531,6 +532,7 @@ impl [T] { // If `m > 0`, there are remaining bits up to the leftmost '1'. while m > 0 { // `buf.extend(buf)`: + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy_nonoverlapping::( buf.as_ptr(), @@ -551,6 +553,7 @@ impl [T] { let rem_len = capacity - buf.len(); // `self.len() * rem` if rem_len > 0 { // `buf.extend(buf[0 .. rem_len])`: + // ignore-tidy-undocumented-unsafe unsafe { // This is non-overlapping since `2^expn > rem`. ptr::copy_nonoverlapping::( diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 4c86d7e06ae44..72c8fc08fb8ff 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -73,6 +73,7 @@ impl> Join<&str> for [S] { type Output = String; fn join(slice: &Self, sep: &str) -> String { + // ignore-tidy-undocumented-unsafe unsafe { String::from_utf8_unchecked(join_generic_copy(slice, sep.as_bytes())) } } } @@ -180,6 +181,7 @@ where result.extend_from_slice(first); + // ignore-tidy-undocumented-unsafe unsafe { let pos = result.len(); debug_assert!(reserved_len >= pos); @@ -248,6 +250,7 @@ impl ToOwned for str { #[inline] fn to_owned(&self) -> String { + // ignore-tidy-undocumented-unsafe unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } } @@ -316,6 +319,7 @@ impl str { _ => None, } { if let [to_byte] = to.as_bytes() { + // ignore-tidy-undocumented-unsafe return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) }; } } @@ -328,10 +332,12 @@ impl str { let mut result = String::with_capacity(default_capacity); let mut last_end = 0; for (start, part) in self.match_indices(from) { + // ignore-tidy-undocumented-unsafe result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } + // ignore-tidy-undocumented-unsafe result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } @@ -368,10 +374,12 @@ impl str { let mut result = String::with_capacity(32); let mut last_end = 0; for (start, part) in self.match_indices(pat).take(count) { + // ignore-tidy-undocumented-unsafe result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } + // ignore-tidy-undocumented-unsafe result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } @@ -785,6 +793,7 @@ impl str { #[inline] pub fn into_string(self: Box) -> String { let slice = Box::<[u8]>::from(self); + // ignore-tidy-undocumented-unsafe unsafe { String::from_utf8_unchecked(slice.into_vec()) } } @@ -814,6 +823,7 @@ impl str { #[stable(feature = "repeat_str", since = "1.16.0")] #[inline] pub fn repeat(&self, n: usize) -> String { + // ignore-tidy-undocumented-unsafe unsafe { String::from_utf8_unchecked(self.as_bytes().repeat(n)) } } @@ -903,6 +913,7 @@ impl str { #[must_use] #[inline] pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { + // SAFETY: Upheld by caller. unsafe { Box::from_raw(Box::into_raw(v) as *mut str) } } @@ -915,6 +926,7 @@ pub(crate) unsafe fn from_boxed_utf8_unchecked_in( v: Box<[u8], A>, ) -> Box { let (ptr, alloc) = Box::into_raw_with_allocator(v); + // SAFETY: Upheld by caller. unsafe { Box::from_raw_in(ptr as *mut str, alloc) } } @@ -972,7 +984,9 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & } ascii_prefix_len += N; + // ignore-tidy-undocumented-unsafe slice = unsafe { slice.get_unchecked(N..) }; + // ignore-tidy-undocumented-unsafe out_slice = unsafe { out_slice.get_unchecked_mut(N..) }; } @@ -987,23 +1001,23 @@ pub unsafe fn convert_while_ascii(s: &str, convert: fn(&u8) -> u8) -> (String, & *out_slice.get_unchecked_mut(0) = MaybeUninit::new(convert(&byte)); } ascii_prefix_len += 1; + // ignore-tidy-undocumented-unsafe slice = unsafe { slice.get_unchecked(1..) }; + // ignore-tidy-undocumented-unsafe out_slice = unsafe { out_slice.get_unchecked_mut(1..) }; } - unsafe { - // SAFETY: ascii_prefix_len bytes have been initialized above - out.set_len(ascii_prefix_len); + // SAFETY: ascii_prefix_len bytes have been initialized above + unsafe { out.set_len(ascii_prefix_len) }; - // SAFETY: We have written only valid ascii to the output vec - let ascii_string = String::from_utf8_unchecked(out); + // SAFETY: We have written only valid ascii to the output vec + let ascii_string = unsafe { String::from_utf8_unchecked(out) }; - // SAFETY: we know this is a valid char boundary - // since we only skipped over leading ascii bytes - let rest = core::str::from_utf8_unchecked(slice); + // SAFETY: we know this is a valid char boundary + // since we only skipped over leading ascii bytes + let rest = unsafe { core::str::from_utf8_unchecked(slice) }; - (ascii_string, rest) - } + (ascii_string, rest) } #[inline] #[cfg(not(no_global_oom_handling))] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index 83dad22eccb61..38c36fa25e41e 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -802,6 +802,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; + // ignore-tidy-undocumented-unsafe match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -837,6 +838,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16le_lossy(v: &[u8]) -> String { + // ignore-tidy-undocumented-unsafe match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -875,6 +877,7 @@ impl String { let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error { kind: FromUtf16ErrorKind::OddBytes }); }; + // ignore-tidy-undocumented-unsafe match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), _ => { @@ -910,6 +913,7 @@ impl String { #[cfg(not(no_global_oom_handling))] #[stable(feature = "str_from_utf16_endian", since = "1.98.0")] pub fn from_utf16be_lossy(v: &[u8]) -> String { + // ignore-tidy-undocumented-unsafe match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", @@ -994,6 +998,7 @@ impl String { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String { + // SAFETY: Upheld by caller. unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity) } } } @@ -1138,6 +1143,7 @@ impl String { let additional: Saturating = slice.iter().map(|x| Saturating(x.len())).sum(); self.reserve(additional.0); let (ptr, len, cap) = core::mem::take(self).into_raw_parts(); + // ignore-tidy-undocumented-unsafe unsafe { let mut dst = ptr.add(len); for new in slice { @@ -1526,6 +1532,7 @@ impl String { pub fn pop(&mut self) -> Option { let ch = self.chars().rev().next()?; let newlen = self.len() - ch.len_utf8(); + // ignore-tidy-undocumented-unsafe unsafe { self.vec.set_len(newlen); } @@ -1564,6 +1571,7 @@ impl String { let next = idx + ch.len_utf8(); let len = self.len(); + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy(self.vec.as_ptr().add(next), self.vec.as_mut_ptr().add(idx), len - next); self.vec.set_len(len - (next - idx)); @@ -1637,6 +1645,7 @@ impl String { len += count; } + // ignore-tidy-undocumented-unsafe unsafe { self.vec.set_len(len); } @@ -1945,6 +1954,7 @@ impl String { pub fn split_off(&mut self, at: usize) -> String { assert!(self.is_char_boundary(at)); let other = self.vec.split_off(at); + // ignore-tidy-undocumented-unsafe unsafe { String::from_utf8_unchecked(other) } } @@ -2121,6 +2131,7 @@ impl String { "end of range should be a character boundary" ); + // ignore-tidy-undocumented-unsafe unsafe { self.as_mut_vec() }.splice(checked_range, replace_with.bytes()); } @@ -2206,6 +2217,7 @@ impl String { #[inline] pub fn into_boxed_str(self) -> Box { let slice = self.vec.into_boxed_slice(); + // ignore-tidy-undocumented-unsafe unsafe { from_boxed_utf8_unchecked(slice) } } @@ -2237,6 +2249,7 @@ impl String { #[inline] pub fn leak<'a>(self) -> &'a mut str { let slice = self.vec.leak(); + // ignore-tidy-undocumented-unsafe unsafe { from_utf8_unchecked_mut(slice) } } } @@ -3471,7 +3484,7 @@ impl IntoChars { #[unstable(feature = "string_into_chars", issue = "133125")] #[inline] pub fn into_string(self) -> String { - // Safety: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. + // SAFETY: `bytes` are kept in UTF-8 form, only removing whole `char`s at a time. unsafe { String::from_utf8_unchecked(self.bytes.collect()) } } @@ -3568,6 +3581,7 @@ unsafe impl Send for Drain<'_> {} #[stable(feature = "drain", since = "1.6.0")] impl Drop for Drain<'_> { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { // Use Vec::drain. "Reaffirm" the bounds checks to avoid // panic code being inserted again. diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index cca6f881e1740..bd461f8b414b6 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -303,10 +303,12 @@ unsafe impl CloneFromCell for Arc {} impl Arc { unsafe fn from_inner(ptr: NonNull>) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_inner_in(ptr, Global) } } unsafe fn from_ptr(ptr: *mut ArcInner) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_ptr_in(ptr, Global) } } } @@ -315,6 +317,7 @@ impl Arc { #[inline] fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { let this = mem::ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads. (this.ptr, unsafe { ptr::read(&this.alloc) }) } @@ -325,6 +328,7 @@ impl Arc { #[inline] unsafe fn from_ptr_in(ptr: *mut ArcInner, alloc: A) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_inner_in(NonNull::new_unchecked(ptr), alloc) } } } @@ -445,6 +449,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }); + // SAFETY: Pointer is valid. unsafe { Self::from_inner(Box::leak(x).into()) } } @@ -530,6 +535,7 @@ impl Arc { #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit() -> Arc> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr(Arc::allocate_for_layout( Layout::new::(), @@ -562,6 +568,7 @@ impl Arc { #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed() -> Arc> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr(Arc::allocate_for_layout( Layout::new::(), @@ -577,6 +584,7 @@ impl Arc { #[stable(feature = "pin", since = "1.33.0")] #[must_use] pub fn pin(data: T) -> Pin> { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Arc::new(data)) } } @@ -584,6 +592,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_pin(data: T) -> Result>, AllocError> { + // SAFETY: We own and create the pinned pointer. unsafe { Ok(Pin::new_unchecked(Arc::try_new(data)?)) } } @@ -608,6 +617,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, })?; + // SAFETY: Pointer is valid. unsafe { Ok(Self::from_inner(Box::leak(x).into())) } } @@ -633,6 +643,7 @@ impl Arc { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn try_new_uninit() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( Layout::new::(), @@ -665,6 +676,7 @@ impl Arc { /// [zeroed]: mem::MaybeUninit::zeroed #[unstable(feature = "allocator_api", issue = "32838")] pub fn try_new_zeroed() -> Result>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Arc::from_ptr(Arc::try_allocate_for_layout( Layout::new::(), @@ -703,6 +715,7 @@ impl Arc { alloc, ); let (ptr, alloc) = Box::into_unique(x); + // SAFETY: Pointer is valid. unsafe { Self::from_inner_in(ptr.into(), alloc) } } @@ -732,6 +745,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_uninit_in(alloc: A) -> Arc, A> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr_in( Arc::allocate_for_layout( @@ -769,6 +783,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_zeroed_in(alloc: A) -> Arc, A> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr_in( Arc::allocate_for_layout( @@ -827,6 +842,7 @@ impl Arc { }, alloc, )); + // SAFETY: Pointer is valid since we constructed it. let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into(); let init_ptr: NonNull> = uninit_ptr.cast(); @@ -842,6 +858,7 @@ impl Arc { // Now we can properly initialize the inner value and turn our weak // reference into a strong reference. + // ignore-tidy-undocumented-unsafe unsafe { let inner = init_ptr.as_ptr(); ptr::write(&raw mut (*inner).data, data); @@ -880,6 +897,7 @@ impl Arc { where A: 'static, { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } } @@ -891,6 +909,7 @@ impl Arc { where A: 'static, { + // SAFETY: We own and create the pinned pointer. unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } } @@ -921,6 +940,7 @@ impl Arc { alloc, )?; let (ptr, alloc) = Box::into_unique(x); + // SAFETY: Pointer is valid since we created it. Ok(unsafe { Self::from_inner_in(ptr.into(), alloc) }) } @@ -951,6 +971,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_new_uninit_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Arc::from_ptr_in( Arc::try_allocate_for_layout( @@ -989,6 +1010,7 @@ impl Arc { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn try_new_zeroed_in(alloc: A) -> Result, A>, AllocError> { + // ignore-tidy-undocumented-unsafe unsafe { Ok(Arc::from_ptr_in( Arc::try_allocate_for_layout( @@ -1043,7 +1065,9 @@ impl Arc { acquire!(this.inner().strong); let this = ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads. let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; + // SAFETY: As above. let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator // Make a weak pointer to clean up the implicit strong-weak reference @@ -1168,8 +1192,8 @@ impl Arc { // in `drop_slow`. Instead of dropping the value behind the pointer, // it is read and eventually returned; `ptr::read` has the same // safety conditions as `ptr::drop_in_place`. - let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) }; + // SAFETY: Pointer is valid for reads. let alloc = unsafe { ptr::read(&this.alloc) }; drop(Weak { ptr: this.ptr, alloc }); @@ -1204,6 +1228,7 @@ impl Arc { && align_of::() == align_of::() && Arc::is_unique(&this) { + // ignore-tidy-undocumented-unsafe unsafe { let (ptr, alloc) = Arc::into_raw_with_allocator(this); let value = ptr.read(); @@ -1215,6 +1240,7 @@ impl Arc { } else { let output = f(&*this); let (ptr, alloc) = Arc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } Arc::new_in(output, alloc) @@ -1255,6 +1281,7 @@ impl Arc { && align_of::() == align_of::() && Arc::is_unique(&this) { + // ignore-tidy-undocumented-unsafe unsafe { let (ptr, alloc) = Arc::into_raw_with_allocator(this); let value = ptr.read(); @@ -1267,6 +1294,7 @@ impl Arc { } else { let output = f(&*this)?; let (ptr, alloc) = Arc::into_raw_with_allocator(this); + // ignore-tidy-undocumented-unsafe unsafe { Arc::decrement_strong_count_in(ptr, &alloc) } try { Arc::new_in(output, alloc) } @@ -1299,6 +1327,7 @@ impl Arc<[T]> { #[stable(feature = "new_uninit", since = "1.82.0")] #[must_use] pub fn new_uninit_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr(Arc::allocate_for_slice(len)) } } @@ -1325,6 +1354,7 @@ impl Arc<[T]> { #[stable(feature = "new_zeroed_alloc", since = "1.92.0")] #[must_use] pub fn new_zeroed_slice(len: usize) -> Arc<[mem::MaybeUninit]> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr(Arc::allocate_for_layout( Layout::array::(len).unwrap(), @@ -1365,6 +1395,7 @@ impl Arc<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_uninit_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr_in(Arc::allocate_for_slice_in(len, &alloc), alloc) } } @@ -1393,6 +1424,7 @@ impl Arc<[T], A> { #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new_zeroed_slice_in(len: usize, alloc: A) -> Arc<[mem::MaybeUninit], A> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr_in( Arc::allocate_for_layout( @@ -1471,6 +1503,7 @@ impl Arc, A> { #[inline] pub unsafe fn assume_init(self) -> Arc { let (ptr, alloc) = Arc::into_inner_with_allocator(self); + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_inner_in(ptr.cast(), alloc) } } } @@ -1532,6 +1565,7 @@ impl Arc { let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(value, alloc); // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe unsafe { // Clone. If the clone panics, `in_progress` will be dropped and clean up. value.clone_to_uninit(in_progress.data_ptr().cast()); @@ -1560,6 +1594,7 @@ impl Arc { let mut in_progress: UniqueArcUninit = UniqueArcUninit::try_new(value, alloc)?; // Initialize with clone of value. + // ignore-tidy-undocumented-unsafe let initialized_clone = unsafe { // Clone. If the clone panics, `in_progress` will be dropped and clean up. value.clone_to_uninit(in_progress.data_ptr().cast()); @@ -1606,6 +1641,7 @@ impl Arc<[mem::MaybeUninit], A> { #[inline] pub unsafe fn assume_init(self) -> Arc<[T], A> { let (ptr, alloc) = Arc::into_inner_with_allocator(self); + // SAFETY: Upheld by caller. unsafe { Arc::from_ptr_in(ptr.as_ptr() as _, alloc) } } } @@ -1678,6 +1714,7 @@ impl Arc { #[inline] #[stable(feature = "rc_raw", since = "1.17.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. unsafe { Arc::from_raw_in(ptr, Global) } } @@ -1740,6 +1777,7 @@ impl Arc { #[inline] #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] pub unsafe fn increment_strong_count(ptr: *const T) { + // SAFETY: Upheld by caller. unsafe { Arc::increment_strong_count_in(ptr, Global) } } @@ -1780,6 +1818,7 @@ impl Arc { #[inline] #[stable(feature = "arc_mutate_strong_count", since = "1.51.0")] pub unsafe fn decrement_strong_count(ptr: *const T) { + // SAFETY: Upheld by caller. unsafe { Arc::decrement_strong_count_in(ptr, Global) } } @@ -1825,9 +1864,13 @@ impl Arc { #[must_use] #[unstable(feature = "arc_raw_get_strong", issue = "157021")] pub unsafe fn strong_count_from_raw(ptr: *const T) -> usize { + // SAFETY: Upheld by caller. let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original ArcInner. + // SAFETY: Caller ensures this pointer was to an `Arc` allocation, + // so offsetting must be inbounds. let arc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; + // SAFETY: Per the above, an `ArcInner` is stored here. unsafe { (*arc_ptr).strong.load(Relaxed) } } } @@ -1867,7 +1910,7 @@ impl Arc { pub fn into_raw_with_allocator(this: Self) -> (*const T, A) { let this = mem::ManuallyDrop::new(this); let ptr = Self::as_ptr(&this); - // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped let alloc = unsafe { ptr::read(&this.alloc) }; (ptr, alloc) } @@ -1973,6 +2016,7 @@ impl Arc { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn from_raw_in(ptr: *const T, alloc: A) -> Self { + // SAFETY: Upheld by caller. unsafe { let offset = data_offset(ptr); @@ -2134,6 +2178,7 @@ impl Arc { A: AllocatorClone, { // Retain Arc, but don't touch refcount by wrapping in ManuallyDrop + // SAFETY: Upheld by caller. let arc = unsafe { mem::ManuallyDrop::new(Arc::from_raw_in(ptr, alloc)) }; // Now increase refcount, but don't drop new refcount either let _arc_clone: mem::ManuallyDrop<_> = arc.clone(); @@ -2179,12 +2224,13 @@ impl Arc { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub unsafe fn decrement_strong_count_in(ptr: *const T, alloc: A) { + // SAFETY: Upheld by caller. unsafe { drop(Arc::from_raw_in(ptr, alloc)) }; } #[inline] fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed + // SAFETY: While this arc is alive we're guaranteed // that the inner pointer is valid. Furthermore, we know that the // `ArcInner` structure itself is `Sync` because the inner data is // `Sync` as well, so we're ok loaning out an immutable pointer to these @@ -2205,6 +2251,7 @@ impl Arc { // Destroy the data at this time, even though we must not free the box // allocation itself (there might still be weak pointers lying around). // We cannot use `get_mut_unchecked` here, because `self.alloc` is borrowed. + // ignore-tidy-undocumented-unsafe unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; } @@ -2249,6 +2296,7 @@ impl Arc { let ptr = allocate(layout).unwrap_or_else(|_| handle_alloc_error(layout)); + // ignore-tidy-undocumented-unsafe unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) } } @@ -2267,6 +2315,7 @@ impl Arc { let ptr = allocate(layout)?; + // ignore-tidy-undocumented-unsafe let inner = unsafe { Self::initialize_arcinner(ptr, layout, mem_to_arcinner) }; Ok(inner) @@ -2278,8 +2327,10 @@ impl Arc { mem_to_arcinner: impl FnOnce(*mut u8) -> *mut ArcInner, ) -> *mut ArcInner { let inner = mem_to_arcinner(ptr.as_non_null_ptr().as_ptr()); + // SAFETY: Upheld by caller. debug_assert_eq!(unsafe { Layout::for_value_raw(inner) }, layout); + // ignore-tidy-undocumented-unsafe unsafe { (&raw mut (*inner).strong).write(atomic::AtomicUsize::new(1)); (&raw mut (*inner).weak).write(atomic::AtomicUsize::new(1)); @@ -2295,6 +2346,7 @@ impl Arc { #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_ptr_in(ptr: *const T, alloc: &A) -> *mut ArcInner { // Allocate for the `ArcInner` using the given value. + // ignore-tidy-undocumented-unsafe unsafe { Arc::allocate_for_layout( Layout::for_value_raw(ptr), @@ -2306,6 +2358,7 @@ impl Arc { #[cfg(not(no_global_oom_handling))] fn from_box_in(src: Box) -> Arc { + // ignore-tidy-undocumented-unsafe unsafe { let value_size = size_of_val(&*src); let ptr = Self::allocate_for_ptr_in(&*src, Box::allocator(&src)); @@ -2331,6 +2384,7 @@ impl Arc<[T]> { /// Allocates an `ArcInner<[T]>` with the given length. #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_slice(len: usize) -> *mut ArcInner<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { Self::allocate_for_layout( Layout::array::(len).unwrap(), @@ -2346,6 +2400,7 @@ impl Arc<[T]> { /// bind `T: TrivialClone`. #[cfg(not(no_global_oom_handling))] unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(v.len()); @@ -2372,6 +2427,7 @@ impl Arc<[T]> { impl Drop for Guard { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); @@ -2381,6 +2437,7 @@ impl Arc<[T]> { } } + // ignore-tidy-undocumented-unsafe unsafe { let ptr = Self::allocate_for_slice(len); @@ -2410,6 +2467,7 @@ impl Arc<[T], A> { #[inline] #[cfg(not(no_global_oom_handling))] unsafe fn allocate_for_slice_in(len: usize, alloc: &A) -> *mut ArcInner<[T]> { + // ignore-tidy-undocumented-unsafe unsafe { Arc::allocate_for_layout( Layout::array::(len).unwrap(), @@ -2430,6 +2488,7 @@ trait ArcFromSlice { impl ArcFromSlice for Arc<[T]> { #[inline] default fn from_slice(v: &[T]) -> Self { + // ignore-tidy-undocumented-unsafe unsafe { Self::from_iter_exact(v.iter().cloned(), v.len()) } } } @@ -2494,6 +2553,7 @@ impl Clone for Arc { abort(); } + // SAFETY: Pointer is valid & allocator corresponds to the one used to allocate it. unsafe { Self::from_inner_in(self.ptr, self.alloc.clone()) } } } @@ -2633,6 +2693,7 @@ impl Arc { let mut in_progress: UniqueArcUninit = UniqueArcUninit::new(&**this, this.alloc.clone()); + // ignore-tidy-undocumented-unsafe unsafe { // Initialize `in_progress` with move of **this. // We have to express this in terms of bytes because `T: ?Sized`; there is no @@ -2660,7 +2721,7 @@ impl Arc { this.inner().strong.store(1, Release); } - // As with `get_mut()`, the unsafety is ok because our reference was + // SAFETY: As with `get_mut()`, our reference was // either unique to begin with, or became one upon cloning the contents. unsafe { Self::get_mut_unchecked(this) } } @@ -2731,7 +2792,7 @@ impl Arc { #[stable(feature = "arc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { if Self::is_unique(this) { - // This unsafety is ok because we're guaranteed that the pointer + // SAFETY: We're guaranteed that the pointer // returned is the *only* pointer that will ever be returned to T. Our // reference count is guaranteed to be 1 at this point, and we required // the Arc itself to be `mut`, so we're returning the only possible @@ -2807,6 +2868,7 @@ impl Arc { pub unsafe fn get_mut_unchecked(this: &mut Self) -> &mut T { // We are careful to *not* create a reference covering the "count" fields, as // this would alias with concurrent access to the reference counts (e.g. by `Weak`). + // ignore-tidy-undocumented-unsafe unsafe { &mut (*this.ptr.as_ptr()).data } } @@ -2966,6 +3028,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Arc { Likely decrement_strong_count or from_raw were called too many times.", ); + // ignore-tidy-undocumented-unsafe unsafe { self.drop_slow(); } @@ -2998,6 +3061,7 @@ impl Arc { T: Any + Send + Sync, { if (*self).is::() { + // SAFETY: Check ensures the typecast is okay. unsafe { let (ptr, alloc) = Arc::into_inner_with_allocator(self); Ok(Arc::from_inner_in(ptr.cast(), alloc)) @@ -3039,6 +3103,7 @@ impl Arc { where T: Any + Send + Sync, { + // SAFETY: Upheld by caller. unsafe { let (ptr, alloc) = Arc::into_inner_with_allocator(self); Arc::from_inner_in(ptr.cast(), alloc) @@ -3146,6 +3211,7 @@ impl Weak { #[inline] #[stable(feature = "weak_into_raw", since = "1.45.0")] pub unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. unsafe { Weak::from_raw_in(ptr, Global) } } @@ -3267,7 +3333,7 @@ impl Weak { pub fn into_raw_with_allocator(self) -> (*const T, A) { let this = mem::ManuallyDrop::new(self); let result = this.as_ptr(); - // Safety: `this` is ManuallyDrop so the allocator will not be double-dropped + // SAFETY: `this` is ManuallyDrop so the allocator will not be double-dropped let alloc = unsafe { ptr::read(&this.alloc) }; (result, alloc) } @@ -3454,6 +3520,7 @@ impl Weak { // We are careful to *not* create a reference covering the "data" field, as // the field may be mutated concurrently (for example, if the last `Arc` // is dropped, the data field will be dropped in-place). + // ignore-tidy-undocumented-unsafe Some(unsafe { WeakInner { strong: &(*ptr).strong, weak: &(*ptr).weak } }) } } @@ -3611,6 +3678,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for Weak { Likely decrement_strong_count or from_raw were called too many times.", ); + // ignore-tidy-undocumented-unsafe unsafe { self.alloc.deallocate(self.ptr.cast(), Layout::for_value_raw(self.ptr.as_ptr())) } @@ -3848,6 +3916,7 @@ impl Default for Arc { /// assert_eq!(*x, 0); /// ``` fn default() -> Arc { + // ignore-tidy-undocumented-unsafe unsafe { Self::from_inner( Box::leak(Box::write( @@ -3897,6 +3966,7 @@ impl Default for Arc { let arc: Arc<[u8]> = Default::default(); debug_assert!(core::str::from_utf8(&arc).is_ok()); let (ptr, alloc) = Arc::into_inner_with_allocator(arc); + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_ptr_in(ptr.as_ptr() as *mut ArcInner, alloc) } } } @@ -3915,6 +3985,7 @@ impl Default for Arc { NonNull::new(inner.as_ptr() as *mut ArcInner).unwrap(); // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. let this: mem::ManuallyDrop> = + // ignore-tidy-undocumented-unsafe unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; (*this).clone() } @@ -3937,6 +4008,7 @@ impl Default for Arc<[T]> { let inner: NonNull> = inner.cast(); // `this` semantically is the Arc "owned" by the static, so make sure not to drop it. let this: mem::ManuallyDrop> = + // ignore-tidy-undocumented-unsafe unsafe { mem::ManuallyDrop::new(Arc::from_inner(inner)) }; return (*this).clone(); } @@ -3956,6 +4028,7 @@ where { #[inline] fn default() -> Self { + // SAFETY: We own and create the pinned pointer. unsafe { Pin::new_unchecked(Arc::::default()) } } } @@ -4064,6 +4137,7 @@ impl From<&str> for Arc { #[inline] fn from(v: &str) -> Arc { let arc = Arc::<[u8]>::from(v.as_bytes()); + // ignore-tidy-undocumented-unsafe unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } } } @@ -4141,6 +4215,7 @@ impl From> for Arc<[T], A> { /// ``` #[inline] fn from(v: Vec) -> Arc<[T], A> { + // ignore-tidy-undocumented-unsafe unsafe { let (vec_ptr, len, cap, alloc) = v.into_raw_parts_with_allocator(); @@ -4209,6 +4284,7 @@ impl TryFrom> for Arc<[T; N], A> { fn try_from(boxed_slice: Arc<[T], A>) -> Result { if boxed_slice.len() == N { let (ptr, alloc) = Arc::into_inner_with_allocator(boxed_slice); + // ignore-tidy-undocumented-unsafe Ok(unsafe { Arc::from_inner_in(ptr.cast(), alloc) }) } else { Err(boxed_slice) @@ -4288,10 +4364,8 @@ impl> ToArcSlice for I { (low, high) ); - unsafe { - // SAFETY: We need to ensure that the iterator has an exact length and we have. - Arc::from_iter_exact(self, low) - } + // SAFETY: We need to ensure that the iterator has an exact length and we have. + unsafe { Arc::from_iter_exact(self, low) } } else { // TrustedLen contract guarantees that `upper_bound == None` implies an iterator // length exceeding `usize::MAX`. @@ -4356,6 +4430,7 @@ impl UniqueArcUninit { #[cfg(not(no_global_oom_handling))] fn new(for_value: &T, alloc: A) -> UniqueArcUninit { let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe let ptr = unsafe { Arc::allocate_for_layout( layout, @@ -4370,6 +4445,7 @@ impl UniqueArcUninit { /// returning an error if allocation fails. fn try_new(for_value: &T, alloc: A) -> Result, AllocError> { let layout = Layout::for_value(for_value); + // ignore-tidy-undocumented-unsafe let ptr = unsafe { Arc::try_allocate_for_layout( layout, @@ -4383,6 +4459,7 @@ impl UniqueArcUninit { /// Returns the pointer to be written into to initialize the [`Arc`]. fn data_ptr(&mut self) -> *mut T { let offset = data_offset_alignment(self.layout_for_value.alignment()); + // ignore-tidy-undocumented-unsafe unsafe { self.ptr.as_ptr().byte_add(offset) as *mut T } } @@ -4755,6 +4832,7 @@ impl UniqueArc { && align_of::() == align_of::() && UniqueArc::weak_count(&this) == 0 { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = UniqueArc::into_raw(this); let value = ptr.read(); @@ -4803,6 +4881,7 @@ impl UniqueArc { && align_of::() == align_of::() && UniqueArc::weak_count(&this) == 0 { + // ignore-tidy-undocumented-unsafe unsafe { let ptr = UniqueArc::into_raw(this); let value = ptr.read(); @@ -4819,6 +4898,7 @@ impl UniqueArc { #[cfg(not(no_global_oom_handling))] fn unwrap(this: Self) -> T { let this = ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads and `this` is ManuallyDrop. let val: T = unsafe { ptr::read(&**this) }; let _weak = Weak { ptr: this.ptr, alloc: Global }; @@ -4830,12 +4910,15 @@ impl UniqueArc { impl UniqueArc { #[cfg(not(no_global_oom_handling))] unsafe fn from_raw(ptr: *const T) -> Self { + // SAFETY: Upheld by caller. let offset = unsafe { data_offset(ptr) }; // Reverse the offset to find the original ArcInner. + // SAFETY: Upheld by caller. let rc_ptr = unsafe { ptr.byte_sub(offset) as *mut ArcInner }; Self { + // SAFETY: Upheld by caller. ptr: unsafe { NonNull::new_unchecked(rc_ptr) }, _marker: PhantomData, _marker2: PhantomData, @@ -4927,6 +5010,7 @@ impl UniqueArc { #[cfg(not(no_global_oom_handling))] fn into_inner_with_allocator(this: Self) -> (NonNull>, A) { let this = mem::ManuallyDrop::new(this); + // SAFETY: Pointer is valid for reads and only read once. (this.ptr, unsafe { ptr::read(&this.alloc) }) } @@ -4969,6 +5053,7 @@ impl UniqueArc { impl UniqueArc, A> { unsafe fn assume_init(self) -> UniqueArc { let (ptr, alloc) = UniqueArc::into_inner_with_allocator(self); + // SAFETY: Upheld by caller. unsafe { UniqueArc::from_inner_in(ptr.cast(), alloc) } } } @@ -5011,6 +5096,7 @@ unsafe impl<#[may_dangle] T: ?Sized, A: Allocator> Drop for UniqueArc { // SAFETY: This pointer was allocated at creation time so we know it is valid. let _weak = Weak { ptr: self.ptr, alloc: &self.alloc }; + // ignore-tidy-undocumented-unsafe unsafe { ptr::drop_in_place(&mut (*self.ptr.as_ptr()).data) }; } } diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 0e36c91f466fd..003890d49067c 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -189,6 +189,7 @@ fn raw_waker(waker: Arc) -> RawWaker { // within the vtables. #[inline(always)] unsafe fn clone_waker(waker: *const ()) -> RawWaker { + // ignore-tidy-undocumented-unsafe unsafe { Arc::increment_strong_count(waker as *const W) }; RawWaker::new( waker, @@ -198,18 +199,21 @@ fn raw_waker(waker: Arc) -> RawWaker { // Wake by value, moving the Arc into the Wake::wake function unsafe fn wake(waker: *const ()) { + // ignore-tidy-undocumented-unsafe let waker = unsafe { Arc::from_raw(waker as *const W) }; ::wake(waker); } // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it unsafe fn wake_by_ref(waker: *const ()) { + // ignore-tidy-undocumented-unsafe let waker = unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) }; ::wake_by_ref(&waker); } // Decrement the reference count of the Arc on drop unsafe fn drop_waker(waker: *const ()) { + // ignore-tidy-undocumented-unsafe unsafe { Arc::decrement_strong_count(waker as *const W) }; } @@ -401,6 +405,7 @@ fn local_raw_waker(waker: Rc) -> RawWaker { // always inline. #[inline(always)] unsafe fn clone_waker(waker: *const ()) -> RawWaker { + // ignore-tidy-undocumented-unsafe unsafe { Rc::increment_strong_count(waker as *const W) }; RawWaker::new( waker, @@ -410,18 +415,21 @@ fn local_raw_waker(waker: Rc) -> RawWaker { // Wake by value, moving the Rc into the LocalWake::wake function unsafe fn wake(waker: *const ()) { + // ignore-tidy-undocumented-unsafe let waker = unsafe { Rc::from_raw(waker as *const W) }; ::wake(waker); } // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it unsafe fn wake_by_ref(waker: *const ()) { + // ignore-tidy-undocumented-unsafe let waker = unsafe { ManuallyDrop::new(Rc::from_raw(waker as *const W)) }; ::wake_by_ref(&waker); } // Decrement the reference count of the Rc on drop unsafe fn drop_waker(waker: *const ()) { + // ignore-tidy-undocumented-unsafe unsafe { Rc::decrement_strong_count(waker as *const W) }; } diff --git a/library/alloc/src/vec/drain.rs b/library/alloc/src/vec/drain.rs index d12dea20b33cb..df3ff0a9b769f 100644 --- a/library/alloc/src/vec/drain.rs +++ b/library/alloc/src/vec/drain.rs @@ -62,6 +62,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { #[must_use] #[inline] pub fn allocator(&self) -> &A { + // SAFETY: `vec` is valid for reads. unsafe { self.vec.as_ref().allocator() } } @@ -101,6 +102,7 @@ impl<'a, T, A: Allocator> Drain<'a, T, A> { // 4. Do *not* drop self, as everything is put in a consistent state already, there is nothing to do let mut this = ManuallyDrop::new(self); + // ignore-tidy-undocumented-unsafe unsafe { let source_vec = this.vec.as_mut(); @@ -153,6 +155,7 @@ impl Iterator for Drain<'_, T, A> { #[inline] fn next(&mut self) -> Option { + // ignore-tidy-undocumented-unsafe self.iter.next().map(|elt| unsafe { ptr::read(elt as *const _) }) } @@ -165,6 +168,7 @@ impl Iterator for Drain<'_, T, A> { impl DoubleEndedIterator for Drain<'_, T, A> { #[inline] fn next_back(&mut self) -> Option { + // ignore-tidy-undocumented-unsafe self.iter.next_back().map(|elt| unsafe { ptr::read(elt as *const _) }) } } @@ -178,6 +182,7 @@ impl Drop for Drain<'_, T, A> { impl<'r, 'a, T, A: Allocator> Drop for DropGuard<'r, 'a, T, A> { fn drop(&mut self) { if self.0.tail_len > 0 { + // ignore-tidy-undocumented-unsafe unsafe { let source_vec = self.0.vec.as_mut(); // memmove back untouched tail, update to new length @@ -202,6 +207,7 @@ impl Drop for Drain<'_, T, A> { if T::IS_ZST { // ZSTs have no identity, so we don't need to move them around, we only need to drop the correct amount. // this can be achieved by manipulating the Vec length instead of moving values out from `iter`. + // ignore-tidy-undocumented-unsafe unsafe { let vec = vec.as_mut(); let old_len = vec.len(); @@ -225,6 +231,7 @@ impl Drop for Drain<'_, T, A> { // lead to invalid pointer arithmetic below. let drop_ptr = iter.as_slice().as_ptr(); + // ignore-tidy-undocumented-unsafe unsafe { // drop_ptr comes from a slice::Iter which only gives us a &[T] but for drop_in_place // a pointer with mutable provenance is necessary. Therefore we must reconstruct diff --git a/library/alloc/src/vec/extract_if.rs b/library/alloc/src/vec/extract_if.rs index a4c4c19682195..366ee1c4e0cbf 100644 --- a/library/alloc/src/vec/extract_if.rs +++ b/library/alloc/src/vec/extract_if.rs @@ -43,6 +43,7 @@ impl<'a, T, F, A: Allocator> ExtractIf<'a, T, F, A> { let Range { start, end } = slice::range(range, ..old_len); // Guard against the vec getting leaked (leak amplification) + // SAFETY: Setting length to 0 is always okay. unsafe { vec.set_len(0); } @@ -137,12 +138,13 @@ where // SAFETY: we always keep first `self.idx - self.del` elements valid. let retained = unsafe { slice::from_raw_parts(start, self.idx - self.del) }; - // SAFETY: we have not yet touched elements starting at `self.idx`. let valid_tail = + // SAFETY: we have not yet touched elements starting at `self.idx`. unsafe { slice::from_raw_parts(start.add(self.idx), self.old_len - self.idx) }; - // SAFETY: `end - idx <= old_len - idx`, because `end <= old_len`. Also `idx <= end` by invariant. let (remainder, skipped_tail) = + // SAFETY: `end - idx <= old_len - idx`, because `end <= old_len`. + // Also `idx <= end` by invariant. unsafe { valid_tail.split_at_unchecked(self.end - self.idx) }; f.debug_struct("ExtractIf") diff --git a/library/alloc/src/vec/in_place_collect.rs b/library/alloc/src/vec/in_place_collect.rs index a5566f70fea3c..2fc958968fdd6 100644 --- a/library/alloc/src/vec/in_place_collect.rs +++ b/library/alloc/src/vec/in_place_collect.rs @@ -251,6 +251,7 @@ where I: Iterator + InPlaceCollect, ::Source: AsVecIntoIter, { + // ignore-tidy-undocumented-unsafe let (src_buf, src_ptr, src_cap, mut dst_buf, dst_end, dst_cap) = unsafe { let inner = iterator.as_inner().as_into_iter(); ( @@ -269,6 +270,7 @@ where SpecInPlaceCollect::collect_in_place(&mut iterator, dst_buf.as_ptr() as *mut T, dst_end) }; + // ignore-tidy-undocumented-unsafe let src = unsafe { iterator.as_inner().as_into_iter() }; // check if SourceIter contract was upheld // caveat: if they weren't we might not even make it to this point @@ -278,6 +280,7 @@ where // then the source pointer will stay in its initial position and we can't use it as reference if src.ptr != src_ptr { debug_assert!( + // ignore-tidy-undocumented-unsafe unsafe { dst_buf.add(len).cast() } <= src.ptr, "InPlaceIterable contract violation, write pointer advanced beyond read pointer" ); @@ -306,6 +309,7 @@ where let alloc = Global; debug_assert_ne!(src_cap, 0); debug_assert_ne!(dst_cap, 0); + // ignore-tidy-undocumented-unsafe unsafe { // The old allocation exists, therefore it must have a valid layout. let src_align = align_of::(); @@ -328,6 +332,7 @@ where mem::forget(dst_guard); + // ignore-tidy-undocumented-unsafe unsafe { Vec::from_parts(dst_buf, len, dst_cap) } } @@ -335,6 +340,7 @@ fn write_in_place_with_drop( src_end: *const T, ) -> impl FnMut(InPlaceDrop, T) -> Result, !> { move |mut sink, item| { + // ignore-tidy-undocumented-unsafe unsafe { // the InPlaceIterable contract cannot be verified precisely here since // try_fold has an exclusive reference to the source pointer @@ -375,6 +381,7 @@ where let sink = self.try_fold::<_, _, Result<_, !>>(sink, write_in_place_with_drop(end)).into_ok(); // iteration succeeded, don't drop head + // ignore-tidy-undocumented-unsafe unsafe { ManuallyDrop::new(sink).dst.offset_from_unsigned(dst_buf) } } } @@ -388,7 +395,7 @@ where let len = self.size(); let mut drop_guard = InPlaceDrop { inner: dst_buf, dst: dst_buf }; for i in 0..len { - // Safety: InplaceIterable contract guarantees that for every element we read + // SAFETY: InplaceIterable contract guarantees that for every element we read // one slot in the underlying storage will have been freed up and we can immediately // write back the result. unsafe { diff --git a/library/alloc/src/vec/in_place_drop.rs b/library/alloc/src/vec/in_place_drop.rs index 5c3d598cdef0c..134dcb1059354 100644 --- a/library/alloc/src/vec/in_place_drop.rs +++ b/library/alloc/src/vec/in_place_drop.rs @@ -13,6 +13,7 @@ pub(super) struct InPlaceDrop { impl InPlaceDrop { fn len(&self) -> usize { + // ignore-tidy-undocumented-unsafe unsafe { self.dst.offset_from_unsigned(self.inner) } } } @@ -20,6 +21,7 @@ impl InPlaceDrop { impl Drop for InPlaceDrop { #[inline] fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { self.inner.cast_slice(self.len()).drop_in_place() } } } @@ -37,6 +39,7 @@ pub(super) struct InPlaceDstDataSrcBufDrop { impl Drop for InPlaceDstDataSrcBufDrop { #[inline] fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { let _drop_allocation = RawVec::::from_nonnull_in(self.ptr.cast::(), self.src_cap, Global); diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 4b25634326e16..46874ff76c093 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -21,10 +21,12 @@ use crate::raw_vec::RawVec; macro non_null { (mut $place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + // ignore-tidy-undocumented-unsafe unsafe { &mut *((&raw mut $place) as *mut NonNull<$t>) } }}, ($place:expr, $t:ident) => {{ #![allow(unused_unsafe)] // we're sometimes used within an unsafe block + // ignore-tidy-undocumented-unsafe unsafe { *((&raw const $place) as *const NonNull<$t>) } }}, } @@ -86,6 +88,7 @@ impl IntoIter { /// ``` #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_slice(&self) -> &[T] { + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) } } @@ -104,6 +107,7 @@ impl IntoIter { /// ``` #[stable(feature = "vec_into_iter_as_slice", since = "1.15.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { + // ignore-tidy-undocumented-unsafe unsafe { &mut *self.as_raw_mut_slice() } } @@ -153,6 +157,7 @@ impl IntoIter { // Dropping the remaining elements can panic, so this needs to be // done only after updating the other fields. + // ignore-tidy-undocumented-unsafe unsafe { ptr::drop_in_place(remaining); } @@ -196,12 +201,10 @@ impl IntoIter { /// memory if there are any remaining elements. #[inline] unsafe fn dealloc_only(&mut self) { - unsafe { - // SAFETY: our caller promises not to touch `*self` again - let alloc = ManuallyDrop::take(&mut self.alloc); - // RawVec handles deallocation - let _ = RawVec::from_nonnull_in(self.buf, self.cap, alloc); - } + // SAFETY: our caller promises not to touch `*self` again. + let alloc = unsafe { ManuallyDrop::take(&mut self.alloc) }; + // SAFETY: We're using this to deallocate a preexisting `RawVec`. + let _ = unsafe { RawVec::from_nonnull_in(self.buf, self.cap, alloc) }; } #[cfg(not(no_global_oom_handling))] @@ -263,9 +266,11 @@ impl Iterator for IntoIter { return None; } let old = self.ptr; + // ignore-tidy-undocumented-unsafe self.ptr = unsafe { old.add(1) }; old }; + // ignore-tidy-undocumented-unsafe Some(unsafe { ptr.read() }) } @@ -274,6 +279,7 @@ impl Iterator for IntoIter { let exact = if T::IS_ZST { self.end.addr().wrapping_sub(self.ptr.as_ptr().addr()) } else { + // ignore-tidy-undocumented-unsafe unsafe { non_null!(self.end, T).offset_from_unsigned(self.ptr) } }; (exact, Some(exact)) @@ -316,18 +322,18 @@ impl Iterator for IntoIter { if T::IS_ZST { if len < N { self.forget_remaining_elements(); - // Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct + // SAFETY: ZSTs can be conjured ex nihilo, only the amount has to be correct return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, 0..len) }); } self.end = self.end.wrapping_byte_sub(N); - // Safety: ditto + // SAFETY: ditto return Ok(unsafe { raw_ary.transpose().assume_init() }); } if len < N { - // Safety: `len` indicates that this many elements are available and we just checked that - // it fits into the array. + // SAFETY: `len` indicates that this many elements are available and we + // just checked that it fits into the array. unsafe { ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); self.forget_remaining_elements(); @@ -335,7 +341,7 @@ impl Iterator for IntoIter { } } - // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize + // SAFETY: `len` is larger than the array size. Copy a fixed amount here to fully initialize // the array. unsafe { ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, N); @@ -433,11 +439,13 @@ impl DoubleEndedIterator for IntoIter { // Note that even though this is next_back() we're reading from `self.ptr`, not // `self.end`. We track our length using the byte offset from `self.ptr` to `self.end`, // so the end pointer may not be suitably aligned for T. + // ignore-tidy-undocumented-unsafe Some(unsafe { ptr::read(self.ptr.as_ptr()) }) } else { if self.ptr == non_null!(self.end, T) { return None; } + // ignore-tidy-undocumented-unsafe unsafe { self.end = self.end.sub(1); Some(ptr::read(self.end)) @@ -454,18 +462,18 @@ impl DoubleEndedIterator for IntoIter { if T::IS_ZST { if len < N { self.forget_remaining_elements(); - // Safety: ZSTs can be conjured ex nihilo, only the amount has to be correct + // SAFETY: ZSTs can be conjured ex nihilo, only the amount has to be correct return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, N - len..N) }); } self.end = self.end.wrapping_byte_sub(N); - // Safety: ditto + // SAFETY: ditto return Ok(unsafe { MaybeUninit::array_assume_init(raw_ary) }); } if len < N { - // Safety: `len` indicates that this many elements are available and we just checked that - // it fits into the array. + // SAFETY: `len` indicates that this many elements are available + // and we just checked that it fits into the array. unsafe { ptr::copy_nonoverlapping(self.ptr.as_ptr(), raw_ary.as_mut_ptr() as *mut T, len); self.forget_remaining_elements(); @@ -473,7 +481,7 @@ impl DoubleEndedIterator for IntoIter { } } - // Safety: `len` is larger than the array size. Copy a fixed amount here to fully initialize + // SAFETY: `len` is larger than the array size. Copy a fixed amount here to fully initialize // the array. unsafe { ptr::copy_nonoverlapping( @@ -585,6 +593,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { impl Drop for DropGuard<'_, T, A> { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { self.0.dealloc_only(); } @@ -593,6 +602,7 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter { let guard = DropGuard(self); // destroy the remaining elements + // ignore-tidy-undocumented-unsafe unsafe { ptr::drop_in_place(guard.0.as_raw_mut_slice()); } diff --git a/library/alloc/src/vec/is_zero.rs b/library/alloc/src/vec/is_zero.rs index 04b50e5762986..bd016ef3ba283 100644 --- a/library/alloc/src/vec/is_zero.rs +++ b/library/alloc/src/vec/is_zero.rs @@ -153,6 +153,7 @@ macro_rules! impl_is_zero_option_of_int { #[inline] fn is_zero(&self) -> bool { const { + // SAFETY: All-zeroes is a valid bitpattern for these primitives. let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; assert!(none.is_none()); } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 7965a10c98289..120483356d81c 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -643,6 +643,7 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const unsafe fn from_raw_parts(ptr: *mut T, length: usize, capacity: usize) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_raw_parts_in(ptr, length, capacity, Global) } } @@ -742,6 +743,7 @@ impl Vec { #[stable(feature = "box_vec_non_null", since = "1.99.0")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] pub const unsafe fn from_parts(ptr: NonNull, length: usize, capacity: usize) -> Self { + // SAFETY: Upheld by caller. unsafe { Self::from_parts_in(ptr, length, capacity, Global) } } @@ -898,10 +900,13 @@ impl Vec { // which is why we instead return a new slice in this case. if self.capacity() == 0 || T::IS_ZST { let me = ManuallyDrop::new(self); + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts(NonNull::::dangling().as_ptr(), me.len) } } else { + // ignore-tidy-undocumented-unsafe unsafe { core::intrinsics::const_make_global(self.as_mut_ptr().cast()) }; let me = ManuallyDrop::new(self); + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts(me.as_ptr(), me.len) } } } @@ -1035,6 +1040,7 @@ const impl Vec { if len == self.buf.capacity() { self.buf.grow_one(); } + // ignore-tidy-undocumented-unsafe unsafe { let end = self.as_mut_ptr().add(len); ptr::write(end, value); @@ -1196,6 +1202,7 @@ impl Vec { "Vec::from_raw_parts_in requires that length <= capacity", (length: usize = length, capacity: usize = capacity) => length <= capacity ); + // SAFETY: Upheld by caller. unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } } } @@ -1311,6 +1318,7 @@ impl Vec { "Vec::from_parts_in requires that length <= capacity", (length: usize = length, capacity: usize = capacity) => length <= capacity ); + // SAFETY: Upheld by caller. unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } } } @@ -1359,6 +1367,7 @@ impl Vec { let len = me.len(); let capacity = me.capacity(); let ptr = me.as_mut_ptr(); + // ignore-tidy-undocumented-unsafe let alloc = unsafe { ptr::read(me.allocator()) }; (ptr, len, capacity, alloc) } @@ -1724,6 +1733,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] pub fn into_boxed_slice(mut self) -> Box<[T], A> { + // ignore-tidy-undocumented-unsafe unsafe { self.shrink_to_fit(); let me = ManuallyDrop::new(self); @@ -2264,6 +2274,7 @@ impl Vec { if index >= len { assert_failed(index, len); } + // ignore-tidy-undocumented-unsafe unsafe { // We replace self[index] with the last element. Note that if the // bounds check above succeeds there must be a last element (which @@ -2351,6 +2362,7 @@ impl Vec { self.buf.grow_one(); } + // ignore-tidy-undocumented-unsafe unsafe { // infallible // The spot to put the new value @@ -2437,6 +2449,7 @@ impl Vec { if index >= len { return None; } + // ignore-tidy-undocumented-unsafe unsafe { // infallible let ret; @@ -2700,13 +2713,15 @@ impl Vec { let mut first_duplicate_idx: usize = 1; let start = self.as_mut_ptr(); while first_duplicate_idx != len { - let found_duplicate = unsafe { - // SAFETY: first_duplicate always in range [1..len) + let found_duplicate = { + // SAFETY: first_duplicate always in range [1..len). // Note that we start iteration from 1 so we never overflow. - let prev = start.add(first_duplicate_idx.wrapping_sub(1)); - let current = start.add(first_duplicate_idx); + let prev = unsafe { start.add(first_duplicate_idx.wrapping_sub(1)) }; + // ignore-tidy-undocumented-unsafe + let current = unsafe { start.add(first_duplicate_idx) }; // We explicitly say in docs that references are reversed. - same_bucket(&mut *current, &mut *prev) + // ignore-tidy-undocumented-unsafe + unsafe { same_bucket(&mut *current, &mut *prev) } }; if found_duplicate { break; @@ -2736,9 +2751,9 @@ impl Vec { fn drop(&mut self) { /* This code gets executed when `same_bucket` panics */ - /* SAFETY: invariant guarantees that `read - write` - * and `len - read` never overflow and that the copy is always - * in-bounds. */ + // SAFETY: invariant guarantees that `read - write` + // and `len - read` never overflow and that the copy is always + // in-bounds. unsafe { let ptr = self.vec.as_mut_ptr(); let len = self.vec.len(); @@ -2771,14 +2786,14 @@ impl Vec { // Construct gap first and then drop item to avoid memory corruption if `T::drop` panics. let mut gap = FillGapOnDrop { read: first_duplicate_idx + 1, write: first_duplicate_idx, vec: self }; + // SAFETY: we checked that first_duplicate_idx in bounds before. + // If drop panics, `gap` would remove this item without drop. unsafe { - // SAFETY: we checked that first_duplicate_idx in bounds before. - // If drop panics, `gap` would remove this item without drop. ptr::drop_in_place(start.add(first_duplicate_idx)); } - /* SAFETY: Because of the invariant, read_ptr, prev_ptr and write_ptr - * are always in-bounds and read_ptr never aliases prev_ptr */ + // SAFETY: Because of the invariant, read_ptr, prev_ptr and write_ptr + // are always in-bounds and read_ptr never aliases prev_ptr unsafe { while gap.read < len { let read_ptr = start.add(gap.read); @@ -2855,14 +2870,14 @@ impl Vec { return Err(value); } - unsafe { - let end = self.as_mut_ptr().add(self.len); - ptr::write(end, value); - self.len += 1; + // ignore-tidy-undocumented-unsafe + let end = unsafe { self.as_mut_ptr().add(self.len) }; + // ignore-tidy-undocumented-unsafe + unsafe { ptr::write(end, value) }; + self.len += 1; - // SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference. - Ok(&mut *end) - } + // SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference. + Ok(unsafe { &mut *end }) } /// Removes the last element from a vector and returns it, or [`None`] if it @@ -2891,6 +2906,7 @@ impl Vec { if self.len == 0 { None } else { + // ignore-tidy-undocumented-unsafe unsafe { self.len -= 1; core::hint::assert_unchecked(self.len < self.capacity()); @@ -2965,6 +2981,7 @@ impl Vec { #[inline] #[stable(feature = "append", since = "1.4.0")] pub fn append(&mut self, other: &mut Self) { + // ignore-tidy-undocumented-unsafe unsafe { self.append_elements(other.as_slice() as _); other.set_len(0); @@ -2976,6 +2993,7 @@ impl Vec { #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { self.reserve(other.len()); + // ignore-tidy-undocumented-unsafe unsafe { self.append_elements_unreserved(other); } @@ -2985,6 +3003,7 @@ impl Vec { #[inline] unsafe fn try_append_elements(&mut self, other: *const [T]) -> Result<(), TryReserveError> { self.try_reserve(other.len())?; + // ignore-tidy-undocumented-unsafe unsafe { self.append_elements_unreserved(other); } @@ -2997,6 +3016,7 @@ impl Vec { let count = other.len(); let len = self.len(); if count > 0 { + // ignore-tidy-undocumented-unsafe unsafe { ptr::copy_nonoverlapping(other as *const T, self.as_mut_ptr().add(len), count) }; @@ -3054,6 +3074,7 @@ impl Vec { let len = self.len(); let Range { start, end } = slice::range(range, ..len); + // ignore-tidy-undocumented-unsafe unsafe { // set self.vec length's to start, to be safe in case Drain is leaked self.set_len(start); @@ -3193,6 +3214,7 @@ impl Vec { let mut other = Vec::with_capacity_in(other_len, self.allocator().clone()); // Unsafely `set_len` and copy items to `other`. + // ignore-tidy-undocumented-unsafe unsafe { self.set_len(at); other.set_len(other_len); @@ -3281,6 +3303,7 @@ impl Vec { A: 'a, { let mut me = ManuallyDrop::new(self); + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts_mut(me.as_mut_ptr(), me.len) } } @@ -3319,6 +3342,7 @@ impl Vec { // Note: // This method is not implemented in terms of `split_at_spare_mut`, // to prevent invalidation of pointers to the buffer. + // ignore-tidy-undocumented-unsafe unsafe { slice::from_raw_parts_mut( self.as_mut_ptr().add(self.len) as *mut MaybeUninit, @@ -3678,6 +3702,7 @@ impl Vec { &mut self, other: &[u8], ) -> Result<(), TryReserveError> { + // ignore-tidy-undocumented-unsafe unsafe { self.try_append_elements(other) } } } @@ -3734,6 +3759,7 @@ impl Vec { fn extend_with(&mut self, n: usize, value: T) { self.reserve(n); + // ignore-tidy-undocumented-unsafe unsafe { let mut ptr = self.as_mut_ptr().add(self.len()); // Use SetLenOnDrop to work around bug where compiler @@ -4041,6 +4067,7 @@ impl IntoIterator for Vec { /// ``` #[inline] fn into_iter(self) -> Self::IntoIter { + // ignore-tidy-undocumented-unsafe unsafe { let me = ManuallyDrop::new(self); let alloc = ManuallyDrop::new(ptr::read(me.allocator())); @@ -4124,6 +4151,7 @@ impl Vec { let (lower, _) = iterator.size_hint(); self.reserve(lower.saturating_add(1)); } + // ignore-tidy-undocumented-unsafe unsafe { ptr::write(self.as_mut_ptr().add(len), element); // Since next() executes user code which can panic we have to bump the length @@ -4147,6 +4175,7 @@ impl Vec { (low, high) ); self.reserve(additional); + // ignore-tidy-undocumented-unsafe unsafe { let ptr = self.as_mut_ptr(); let mut local_len = SetLenOnDrop::new(&mut self.len); @@ -4372,6 +4401,7 @@ const unsafe impl<#[may_dangle] T: [const] Destruct, A: [const] Allocator + [con for Vec { fn drop(&mut self) { + // ignore-tidy-undocumented-unsafe unsafe { // use drop for [T] // use a raw slice to refer to the elements of the vector as weakest necessary type; diff --git a/library/alloc/src/vec/spec_extend.rs b/library/alloc/src/vec/spec_extend.rs index b3fee7d094e20..3f9d4504bde4c 100644 --- a/library/alloc/src/vec/spec_extend.rs +++ b/library/alloc/src/vec/spec_extend.rs @@ -30,6 +30,7 @@ where impl SpecExtend> for Vec { fn spec_extend(&mut self, iterator: IntoIter) { + // ignore-tidy-undocumented-unsafe unsafe { self.append_elements(iterator.as_slice() as _); } @@ -53,6 +54,7 @@ where { fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) { let slice = iterator.as_slice(); + // ignore-tidy-undocumented-unsafe unsafe { self.append_elements(slice) }; } } diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs index 96d701e15d487..5389b3aa39eaf 100644 --- a/library/alloc/src/vec/spec_from_elem.rs +++ b/library/alloc/src/vec/spec_from_elem.rs @@ -36,6 +36,7 @@ impl SpecFromElem for i8 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } let mut v = Vec::with_capacity_in(n, alloc); + // ignore-tidy-undocumented-unsafe unsafe { ptr::write_bytes(v.as_mut_ptr(), elem as u8, n); v.set_len(n); @@ -51,6 +52,7 @@ impl SpecFromElem for u8 { return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }; } let mut v = Vec::with_capacity_in(n, alloc); + // ignore-tidy-undocumented-unsafe unsafe { ptr::write_bytes(v.as_mut_ptr(), elem, n); v.set_len(n); diff --git a/library/alloc/src/vec/spec_from_iter.rs b/library/alloc/src/vec/spec_from_iter.rs index ccbc2936fb4e8..3da17e4aebd57 100644 --- a/library/alloc/src/vec/spec_from_iter.rs +++ b/library/alloc/src/vec/spec_from_iter.rs @@ -46,6 +46,7 @@ impl SpecFromIter> for Vec { // But it is a conservative choice. let has_advanced = iterator.buf != iterator.ptr; if !has_advanced || iterator.len() >= iterator.cap / 2 { + // ignore-tidy-undocumented-unsafe unsafe { let it = ManuallyDrop::new(iterator); if has_advanced { diff --git a/library/alloc/src/vec/spec_from_iter_nested.rs b/library/alloc/src/vec/spec_from_iter_nested.rs index 77f7761d22f95..b211687f20730 100644 --- a/library/alloc/src/vec/spec_from_iter_nested.rs +++ b/library/alloc/src/vec/spec_from_iter_nested.rs @@ -28,8 +28,8 @@ where let initial_capacity = cmp::max(RawVec::::MIN_NON_ZERO_CAP, lower.saturating_add(1)); let mut vector = Vec::with_capacity(initial_capacity); + // SAFETY: We requested capacity at least 1 unsafe { - // SAFETY: We requested capacity at least 1 ptr::write(vector.as_mut_ptr(), element); vector.set_len(1); } diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 6436afd1ba12f..534db037c3bdc 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -61,6 +61,7 @@ impl Drop for Splice<'_, I, A> { // the ptr.offset_from_unsigned contract. self.drain.iter = [].iter(); + // ignore-tidy-undocumented-unsafe unsafe { if self.drain.tail_len == 0 { self.drain.vec.as_mut().extend(self.replace_with.by_ref()); @@ -104,6 +105,7 @@ impl Drain<'_, T, A> { /// Fill that range as much as possible with new elements from the `replace_with` iterator. /// Returns `true` if we filled the entire range. (`replace_with.next()` didn’t return `None`.) unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { + // SAFETY: Pointer is valid. let vec = unsafe { self.vec.as_mut() }; let range_start = vec.len; let range_end = self.tail_start; @@ -113,6 +115,7 @@ impl Drain<'_, T, A> { let Some(new_item) = replace_with.next() else { return false; }; + // ignore-tidy-undocumented-unsafe unsafe { vec.as_mut_ptr().add(idx).write(new_item) }; vec.len += 1; } @@ -121,11 +124,13 @@ impl Drain<'_, T, A> { /// Makes room for inserting more elements before the tail. unsafe fn move_tail(&mut self, additional: usize) { + // SAFETY: Pointer is valid. let vec = unsafe { self.vec.as_mut() }; let len = self.tail_start + self.tail_len; vec.buf.reserve(len, additional); let new_tail_start = self.tail_start + additional; + // ignore-tidy-undocumented-unsafe unsafe { let src = vec.as_ptr().add(self.tail_start); let dst = vec.as_mut_ptr().add(new_tail_start); diff --git a/library/alloc/src/vec/sve_retain.rs b/library/alloc/src/vec/sve_retain.rs index 31e0e79888744..85d86ae127b0d 100644 --- a/library/alloc/src/vec/sve_retain.rs +++ b/library/alloc/src/vec/sve_retain.rs @@ -43,6 +43,7 @@ impl Drop for PanicGuard<'_, T, A> { if self.mask[i] { // SAFETY: read + i < original_len (in-bounds). let src = unsafe { self.v.as_ptr().add(self.read + i) }; + // SAFETY: Continued from above. let dst_ptr = unsafe { self.v.as_mut_ptr().add(dst) }; // SAFETY: src and dst_ptr < original_len unsafe { ptr::copy(src, dst_ptr, 1) }; @@ -107,8 +108,9 @@ where } // Phase B: SVE compress. - // SAFETY: write <= read and the dispatch guarantees size_of::() matches the kernel lane width. let kept = match mem::size_of::() { + // SAFETY: write <= read and the dispatch guarantees size_of::() matches + // the kernel lane width. 1 => unsafe { compact8_kernel( guard.v.as_mut_ptr().add(guard.read), @@ -117,6 +119,7 @@ where chunk_len, ) }, + // SAFETY: Same as above. 2 => unsafe { compact16_kernel( guard.v.as_mut_ptr().add(guard.read), @@ -125,6 +128,7 @@ where chunk_len, ) }, + // SAFETY: Same as above. 4 => unsafe { compact32_kernel( guard.v.as_mut_ptr().add(guard.read), @@ -133,6 +137,7 @@ where chunk_len, ) }, + // SAFETY: Same as above. 8 => unsafe { compact64_kernel( guard.v.as_mut_ptr().add(guard.read), diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 0c817a2789c48..0f0930ca6efae 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -543,13 +543,16 @@ fn check_file_style(base_path: &Path, check: &mut RunningCheck, file: &Path, con err("Don't use magic numbers that spell things (consider 0x12345678)"); } } - // for now we just check libcore + // Only check core & alloc for now; to be expanded to (parts of) + // std in the future as well. if trimmed.contains("unsafe {") && !trimmed.starts_with("//") && !last_safety_comment && !is_test && base_path.ends_with("library") - && file.strip_prefix(base_path).is_ok_and(|rel| rel.starts_with("core")) + && file + .strip_prefix(base_path) + .is_ok_and(|rel| rel.starts_with("core") || rel.starts_with("alloc")) { suppressible_tidy_err!(err, ignore.undocumented_unsafe, "undocumented unsafe"); } From 483873e1e8630168cd3e25b9fdb42da520ef732a Mon Sep 17 00:00:00 2001 From: Nia Deckers Date: Thu, 20 Aug 2026 21:41:06 +0200 Subject: [PATCH 3/8] update some comments --- library/alloc/src/boxed.rs | 6 +++--- library/alloc/src/boxed/thin.rs | 4 ++-- library/alloc/src/ffi/c_str.rs | 5 +++-- library/alloc/src/raw_vec/mod.rs | 5 +++-- library/alloc/src/rc.rs | 3 +-- library/alloc/src/sync.rs | 8 +++++--- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index d8ea1de4da4b4..7d5c5176c952f 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -893,8 +893,8 @@ impl Box { (ptr, Some(DeallocDropGuard(layout, &alloc, ptr))) }; let ptr = ptr.as_ptr(); - // SAFETY: `*ptr` is newly allocated, correctly aligned to `align_of_val(src)`, - // and is valid for writes for `size_of_val(src)`. + // SAFETY: `*ptr` is newly allocated (or a ZST), correctly aligned to + // `align_of_val(src)`, and is valid for writes for `size_of_val(src)`. // If this panics, then `guard` will deallocate for us (if allocation occuured) unsafe { ::clone_to_uninit(src, ptr); @@ -2002,7 +2002,7 @@ impl Box { { // SAFETY: It's not possible to move or replace the insides of a // `Pin>` when `T: !Unpin`, so it's safe to pin it directly - // without any additional requirements. + // so long as the allocator promises to not break the pinning invariants. unsafe { Pin::new_unchecked(boxed) } } } diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index c78fed424c1c4..1e60107a1d15c 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -146,7 +146,7 @@ impl Deref for ThinBox { let value = self.data(); let metadata = self.meta(); let pointer = ptr::from_raw_parts(value as *const (), metadata); - // SAFETY: &ThinBox is also a valid pointer for T. + // SAFETY: &ThinBox points to a valid pointer for T. unsafe { &*pointer } } } @@ -157,7 +157,7 @@ impl DerefMut for ThinBox { let value = self.data(); let metadata = self.meta(); let pointer = ptr::from_raw_parts_mut::(value as *mut (), metadata); - // SAFETY: &mut ThinBox is also a valid pointer for T. + // SAFETY: &mut ThinBox points to a valid and unique pointer for T. unsafe { &mut *pointer } } } diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index b6b6f77a6951e..78158cad394c6 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -481,7 +481,7 @@ impl CString { pub fn into_string(self) -> Result { String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError { error: e.utf8_error(), - // SAFETY: Strings never contain null bytes. + // SAFETY: `CString`s never contain null bytes. inner: unsafe { Self::_from_vec_unchecked(e.into_bytes()) }, }) } @@ -604,7 +604,8 @@ impl CString { #[must_use = "`self` will be dropped if the result is not used"] #[stable(feature = "into_boxed_c_str", since = "1.20.0")] pub fn into_boxed_c_str(self) -> Box { - // SAFETY: Typecast of [u8] to CStr is valid and we know contents have no nulls. + // SAFETY: Typecast of [u8] to CStr is valid and we know contents have + // no nulls except for the terminating byte. unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) } } diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index 98444f06f65ae..250c666c70827 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -889,9 +889,10 @@ const impl RawVecInner { /// Ideally this function would take `self` by move, but it cannot because it exists to be /// called from a `Drop` impl. unsafe fn deallocate(&mut self, elem_layout: Layout) { - // ignore-tidy-undocumented-unsafe + // SAFETY: Caller ensures `elem_layout` is correct for `self`. if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } { - // SAFETY: Precondition passed to caller + // SAFETY: `current_memory` gives us a pointer with provenance for our allocation + // and a matching layout. Caller ensures we're not accessed again after deallocating. unsafe { self.alloc.deallocate(ptr, layout); } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2595dd2105e1a..09540183c488f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2315,12 +2315,11 @@ impl Rc { /// The contained value must be of type `T`. Calling this method /// with the incorrect type is *undefined behavior*. /// - /// /// [`downcast`]: Self::downcast #[inline] #[unstable(feature = "downcast_unchecked", issue = "90850")] pub unsafe fn downcast_unchecked(self) -> Rc { - // SAFETY: Check ensures typecast is correct. + // SAFETY: Caller ensures typecast is correct. unsafe { let (ptr, alloc) = Rc::into_inner_with_allocator(self); Rc::from_inner_in(ptr.cast(), alloc) diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index bd461f8b414b6..5754e48a41e0a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1065,9 +1065,11 @@ impl Arc { acquire!(this.inner().strong); let this = ManuallyDrop::new(this); - // SAFETY: Pointer is valid for reads. + // SAFETY: Pointer is valid for reads, contains initialised memory, + // and not dropped multiple times (we return it). let elem: T = unsafe { ptr::read(&this.ptr.as_ref().data) }; - // SAFETY: As above. + // SAFETY: As above, but we explicitly drop the allocator only once + // upon creating and dropping a weak pointer. let alloc: A = unsafe { ptr::read(&this.alloc) }; // copy the allocator // Make a weak pointer to clean up the implicit strong-weak reference @@ -2232,7 +2234,7 @@ impl Arc { fn inner(&self) -> &ArcInner { // SAFETY: While this arc is alive we're guaranteed // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` because the inner data is + // `ArcInner` structure itself is `Sync` if the inner data is // `Sync` as well, so we're ok loaning out an immutable pointer to these // contents. unsafe { self.ptr.as_ref() } From 28182a7e29ed8d3d6949794685fa15588ef46058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 29 Aug 2026 14:36:32 +0200 Subject: [PATCH 4/8] remove rustc_never_type_options attr remnants While fuzzing I noticed we would now ICE in two places when encountering an empty or filled `#![rustc_never_type_options()]` attr: compiler/rustc_passes/src/check_attr.rs:160:33: builtin attribute "rustc_never_type_options" not handled by `CheckAttrVisitor` compiler/rustc_attr_parsing/src/validate_attr.rs:42:17: assertion failed: lint_attrs.contains(name) So remove these two entries after which we will just error with `error: cannot find attribute` --- compiler/rustc_feature/src/builtin_attrs.rs | 2 -- compiler/rustc_span/src/symbol.rs | 1 - 2 files changed, 3 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 85a7c5aca0970..cc5b8ff2238ea 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -257,8 +257,6 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ sym::fundamental, sym::may_dangle, - sym::rustc_never_type_options, - // ========================================================================== // Internal attributes: Runtime related: // ========================================================================== diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d6a4e2648931e..a546ee5437683 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1842,7 +1842,6 @@ symbols! { rustc_must_implement_one_of, rustc_must_match_exhaustively, rustc_never_returns_null_ptr, - rustc_never_type_options, rustc_no_implicit_autorefs, rustc_no_implicit_bounds, rustc_no_mir_inline, From 40dc9019c8e4a521383946376db36af7e0fa8c5f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Aug 2026 19:39:41 +0200 Subject: [PATCH 5/8] rustdoc: Take into account edition information for keyword highlighting --- src/librustdoc/html/highlight.rs | 28 ++++++++----- src/librustdoc/html/highlight/tests.rs | 20 +++++++--- src/librustdoc/html/macro_expansion.rs | 40 ++++++++++++++++++- src/librustdoc/html/markdown.rs | 1 + src/librustdoc/html/sources.rs | 8 ++-- tests/rustdoc-html/doctest/editions.rs | 32 +++++++++++++++ .../macro-expansion/auxiliary/editions.rs | 8 ++++ .../rustdoc-html/macro-expansion/editions.rs | 27 +++++++++++++ 8 files changed, 143 insertions(+), 21 deletions(-) create mode 100644 tests/rustdoc-html/doctest/editions.rs create mode 100644 tests/rustdoc-html/macro-expansion/auxiliary/editions.rs create mode 100644 tests/rustdoc-html/macro-expansion/editions.rs diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 7fcc6cd9efe4e..89d50680c3a3b 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -58,10 +58,15 @@ pub(crate) fn render_example_with_highlighting( tooltip: Option<&Tooltip>, playground_button: Option<&str>, extra_classes: &[String], + edition: Edition, ) -> impl Display { fmt::from_fn(move |f| { write_header("rust-example-rendered", tooltip, extra_classes).fmt(f)?; - write_code(f, src, None, None, None); + let edition = match tooltip { + Some(Tooltip::Edition(edition)) => *edition, + _ => edition, + }; + write_code(f, src, None, None, edition, None); write_footer(playground_button).fmt(f) }) } @@ -553,6 +558,7 @@ pub(super) fn write_code( src: &str, href_context: Option>, decoration_info: Option<&DecorationInfo>, + edition: Edition, line_info: Option, ) { // This replace allows to fix how the code source with DOS backline characters is displayed. @@ -606,6 +612,7 @@ pub(super) fn write_code( &src, token_handler.href_context.as_ref().map_or(DUMMY_SP, |c| c.file_span), decoration_info, + edition, &mut |span, highlight| match highlight { Highlight::Token { text, class } => { token_handler.push_token(class, Cow::Borrowed(text)); @@ -892,6 +899,7 @@ fn classify<'src>( src: &'src str, file_span: Span, decoration_info: Option<&DecorationInfo>, + edition: Edition, sink: &mut dyn FnMut(Span, Highlight<'src>), ) { let offset = rustc_lexer::strip_shebang(src); @@ -901,7 +909,7 @@ fn classify<'src>( } let mut classifier = - Classifier::new(src, offset.unwrap_or_default(), file_span, decoration_info); + Classifier::new(src, offset.unwrap_or_default(), file_span, decoration_info, edition); loop { if let Some(decs) = classifier.decorations.as_mut() { @@ -946,6 +954,7 @@ struct Classifier<'src> { file_span: Span, src: &'src str, decorations: Option, + edition: Edition, } impl<'src> Classifier<'src> { @@ -956,6 +965,7 @@ impl<'src> Classifier<'src> { byte_pos: usize, file_span: Span, decoration_info: Option<&DecorationInfo>, + edition: Edition, ) -> Self { Classifier { tokens: PeekIter::new(TokenIter::new(&src[byte_pos..])), @@ -966,6 +976,7 @@ impl<'src> Classifier<'src> { file_span, src, decorations: decoration_info.map(Decorations::new), + edition, } } @@ -996,7 +1007,7 @@ impl<'src> Classifier<'src> { if let Some((TokenKind::Ident, text)) = self.tokens.peek_next_if(|(token, _)| token == TokenKind::Ident) && let symbol = Symbol::intern(text) - && (symbol.is_path_segment_keyword() || !is_keyword(symbol)) + && (symbol.is_path_segment_keyword() || !self.is_keyword(symbol)) { has_ident = true; nb_items += 1; @@ -1264,7 +1275,7 @@ impl<'src> Classifier<'src> { "self" | "Self" => Class::Self_(span()), "Option" | "Result" => Class::PreludeTy(span()), "Some" | "None" | "Ok" | "Err" => Class::PreludeVal(span()), - _ if self.is_weak_keyword(text) || is_keyword(Symbol::intern(text)) => { + _ if self.is_weak_keyword(text) || self.is_keyword(Symbol::intern(text)) => { // So if it's not a keyword which can be followed by a value (like `if` or // `return`) and the next non-whitespace token is a `!`, then we consider // it's a macro. @@ -1319,6 +1330,10 @@ impl<'src> Classifier<'src> { matches!(self.peek_non_trivia(), Some((TokenKind::Ident, text)) if matches(text)) } + fn is_keyword(&self, symbol: Symbol) -> bool { + symbol.is_reserved(|| self.edition) + } + fn peek(&mut self) -> Option { self.tokens.peek().map(|(kind, _)| kind) } @@ -1370,11 +1385,6 @@ impl<'src> Classifier<'src> { } } -fn is_keyword(symbol: Symbol) -> bool { - // FIXME(#148221): Don't hard-code the edition. The classifier should take it as an argument. - symbol.is_reserved(|| Edition::Edition2024) -} - fn generate_link_to_def( out: &mut impl Write, text_s: &str, diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 4d1bee9b3a1b3..d31aaecad3a9e 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -1,6 +1,7 @@ use expect_test::expect_file; use rustc_data_structures::fx::FxIndexMap; use rustc_span::create_default_session_globals_then; +use rustc_span::edition::Edition; use test::Bencher; use super::{DecorationInfo, write_code}; @@ -23,7 +24,7 @@ fn test_html_highlighting() { let src = include_str!("fixtures/sample.rs"); let html = { let mut out = String::new(); - write_code(&mut out, src, None, None, None); + write_code(&mut out, src, None, None, Edition::Edition2024, None); format!("{STYLE}
{out}
\n") }; expect_file!["fixtures/sample.html"].assert_eq(&html); @@ -37,7 +38,7 @@ fn test_dos_backline() { println!(\"foo\");\r\n\ }\r\n"; let mut html = String::new(); - write_code(&mut html, src, None, None, None); + write_code(&mut html, src, None, None, Edition::Edition2024, None); expect_file!["fixtures/dos_line.html"].assert_eq(&html); }); } @@ -51,7 +52,7 @@ let x = super::b::foo; let y = Self::whatever;"; let mut html = String::new(); - write_code(&mut html, src, None, None, None); + write_code(&mut html, src, None, None, Edition::Edition2024, None); expect_file!["fixtures/highlight.html"].assert_eq(&html); }); } @@ -61,7 +62,7 @@ fn test_union_highlighting() { create_default_session_globals_then(|| { let src = include_str!("fixtures/union.rs"); let mut html = String::new(); - write_code(&mut html, src, None, None, None); + write_code(&mut html, src, None, None, Edition::Edition2024, None); expect_file!["fixtures/union.html"].assert_eq(&html); }); } @@ -78,7 +79,14 @@ let a = 4;"; decorations.insert("example2", vec![(22, 32)]); let mut html = String::new(); - write_code(&mut html, src, None, Some(&DecorationInfo(decorations)), None); + write_code( + &mut html, + src, + None, + Some(&DecorationInfo(decorations)), + Edition::Edition2024, + None, + ); expect_file!["fixtures/decorations.html"].assert_eq(&html); }); } @@ -90,7 +98,7 @@ fn bench_html_highlighting(b: &mut Bencher) { create_default_session_globals_then(|| { b.iter(|| { let mut out = String::new(); - write_code(&mut out, src, None, None, None); + write_code(&mut out, src, None, None, Edition::Edition2024, None); out }); }); diff --git a/src/librustdoc/html/macro_expansion.rs b/src/librustdoc/html/macro_expansion.rs index 55ca93e601b70..a011947a1159f 100644 --- a/src/librustdoc/html/macro_expansion.rs +++ b/src/librustdoc/html/macro_expansion.rs @@ -107,12 +107,48 @@ impl<'ast> ExpandedCodeVisitor<'ast> { fn compute_expanded(mut self) -> FxHashMap> { self.expanded_codes.sort_unstable_by(|item1, item2| item1.span.cmp(&item2.span)); let mut expanded: FxHashMap> = FxHashMap::default(); - for ExpandedCodeInfo { span, code, .. } in self.expanded_codes { + for ExpandedCodeInfo { span, code, original_span, .. } in self.expanded_codes { if let Ok(lines) = self.source_map.span_to_lines(span) && !lines.lines.is_empty() { let mut out = String::new(); - super::highlight::write_code(&mut out, &code, None, None, None); + super::highlight::write_code( + &mut out, + &code, + None, + None, + // NOTE: This is only "an approximation" or "best effort" since the edition of + // individual tokens contained in the expansion can differ from the the edition + // of the entire expansion. And we can't fix that since code is just a `String` + // that was produced by `rustc_ast_pretty` meaning more precise edition + // information has been lost. + // + // Here is an example: + // + // ```edition2015 + // #[macro_export] + // macro_rules! generate { + // ($kw:ident) => { + // pub fn host() { + // let _ = $kw {}; + // } + // }; + // } + // ``` + // + // ```edition2024 + // dependency::generate!(async); + // ``` + // + // Here, the `async` keyword wouldn't be highlighted in the rendered expansion + // `let _ = async {}` since it uses the edition of the entire expansion (which + // is Rust 2015) but the `async` in the Rust 2015 expansion does actually refer + // to Rust 2024 `async` keyword and thus contains an `async` block, not a struct + // expression! That's because the keyword `async` originates from a Rust 2024 + // crate (root expansion). + original_span.edition(), + None, + ); let first = lines.lines.first().unwrap(); let end = lines.lines.last().unwrap(); expanded.entry(lines.file.start_pos).or_default().push(ExpandedCode { diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7d3e80fccb2c7..af1cd195266cb 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -352,6 +352,7 @@ impl<'a, I: Iterator>> Iterator for CodeBlocks<'_, 'a, I> { tooltip.as_ref(), playground_button.as_deref(), &added_classes, + edition, ) ); Some(Event::Html(s.into())) diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index dda9b7c55351c..ea52fe51c5358 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -329,7 +329,7 @@ pub(crate) fn print_src( mut writer: impl fmt::Write, s: &str, file_span: rustc_span::Span, - context: &Context<'_>, + cx: &Context<'_>, root_path: &str, decoration_info: &highlight::DecorationInfo, source_context: &SourceContext<'_>, @@ -349,20 +349,20 @@ pub(crate) fn print_src( let current_href = if let SourceContext::Embedded(info) = source_context { info.url.to_string() } else { - context - .href_from_span(clean::Span::new(file_span), false) + cx.href_from_span(clean::Span::new(file_span), false) .expect("only local crates should have sources emitted") }; highlight::write_code( fmt, s, Some(highlight::HrefContext { - context, + context: cx, file_span: file_span.into(), root_path, current_href, }), Some(decoration_info), + cx.tcx().sess.edition(), Some(line_info), ); Ok(()) diff --git a/tests/rustdoc-html/doctest/editions.rs b/tests/rustdoc-html/doctest/editions.rs new file mode 100644 index 0000000000000..a7f1c8d41b3d5 --- /dev/null +++ b/tests/rustdoc-html/doctest/editions.rs @@ -0,0 +1,32 @@ +// This test ensures that the syntax highlighting is correctly set on code examples +// if the `edition` attribute is used. +// Regression test for . + +//@ edition:2024 + +#![crate_name = "foo"] + +//@ has 'foo/fn.foo.html' +// There should be two spans: one for `kw` and one for `number`. None for `async` because +// in the 2015 edition, `async` is not a keyword. +//@ count - '//*[@class="rust rust-example-rendered"]/code/span' 2 +//@ matches - '//*[@class="rust rust-example-rendered"]/code/span[@class="kw"]' '^let $' +//@ matches - '//*[@class="rust rust-example-rendered"]/code/span[@class="number"]' '^2$' +//@ has - '//*[@class="rust rust-example-rendered"]/code' 'let async = 2;' + +/// ```edition2015 +/// let async = 2; +/// ``` +pub async fn foo() {} + +//@ has 'foo/fn.another.html' +// There should be one span: `kw` (which includes all items at once). `async` is a keyword +// here since there is no edition specified for the code block, so it inherits the crate's. +//@ count - '//*[@class="rust rust-example-rendered"]/code/span' 1 +//@ matches - '//*[@class="rust rust-example-rendered"]/code/span[@class="kw"]' '^async fn $' +//@ has - '//*[@class="rust rust-example-rendered"]/code' 'async fn bar() {}' + +/// ``` +/// async fn bar() {} +/// ``` +pub fn another() {} diff --git a/tests/rustdoc-html/macro-expansion/auxiliary/editions.rs b/tests/rustdoc-html/macro-expansion/auxiliary/editions.rs new file mode 100644 index 0000000000000..0bf130d99911f --- /dev/null +++ b/tests/rustdoc-html/macro-expansion/auxiliary/editions.rs @@ -0,0 +1,8 @@ +//@ edition:2015 + +#[macro_export] +macro_rules! tadam { + () => { + let async = 2; + } +} diff --git a/tests/rustdoc-html/macro-expansion/editions.rs b/tests/rustdoc-html/macro-expansion/editions.rs new file mode 100644 index 0000000000000..b61921ca196f6 --- /dev/null +++ b/tests/rustdoc-html/macro-expansion/editions.rs @@ -0,0 +1,27 @@ +// This test ensures that the syntax highlighting is correctly set on the expanded +// macro to match the original macro's crate's edition. +// Regression test for . + +//@ edition:2024 +//@ aux-build:editions.rs +//@ compile-flags: -Zunstable-options --generate-macro-expansion + +#![crate_name = "foo"] + +#[macro_use] +extern crate editions; + +//@ has 'src/foo/editions.rs.html' + +// There should be one span: `kw` (which includes all items at once). `async` is a keyword +// here since it's the 2024 edition. +//@ matches - '//pre[@class="rust"]/code/span[@class="kw"]' '^async fn $' +async fn foo() { + // There should be two spans: one for `kw` and one for `number`. None for `async` because + // in the 2015 edition, `async` is not a keyword. + //@ count - '//code/*[@class="expansion"]/*[@class="expanded"]/span' 2 + //@ matches - '//code/*[@class="expansion"]/*[@class="expanded"]/span[@class="kw"]' '^let $' + //@ matches - '//code/*[@class="expansion"]/*[@class="expanded"]/span[@class="number"]' '^2$' + //@ has - '//code/*[@class="expansion"]/*[@class="expanded"]' 'let async = 2;' + tadam!(); +} From 303dafdc110da965b9b4b376dfd91f34d95b82da Mon Sep 17 00:00:00 2001 From: Matthew Demidoff Date: Sat, 29 Aug 2026 14:58:34 +0200 Subject: [PATCH 6/8] Abort instead of unwinding out of an inconsistent BTreeMap::split_off After the first move_suffix in Root::split_off, the source and result trees share values through two temporarily invalid structures. If a later key comparison panics, unwinding leaves the map with a stale length over a partially detached tree, and consuming iteration then double-frees the shared values, reachable from safe code. Guard the descent loop with a mem::DropGuard that aborts on unwind, armed before the first move_suffix and dismissed once the borders are fixed. --- .../alloc/src/collections/btree/map/tests.rs | 25 +++++++++++++++ library/alloc/src/collections/btree/split.rs | 31 +++++++++++++++---- library/alloctests/lib.rs | 1 + 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 64348745aa07d..a920254872e19 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -2428,6 +2428,31 @@ fn test_split_off_large_random_sorted() { assert!(right.into_iter().eq(data.into_iter().filter(|x| x.0 >= key))); } +// Regression test for #158165: a comparator that panics partway through +// `split_off`, after a suffix has already been moved into the new right-hand +// tree, used to leave `self` with a tree structure inconsistent with its own +// recorded length. Iterating or dropping the map afterwards could then double +// free values that had already been moved into the right-hand tree. +// `split_off` now aborts the process instead of unwinding out of that +// inconsistent state; the abort can't be observed from within a single +// process, so this test only checks that ordinary multi-level splits remain +// correct. See the reproducer on the issue for the double free. +#[test] +fn test_split_off_multi_level_panic_guard_happy_path() { + // MIN_INSERTS_HEIGHT_2 consecutive keys guarantee a 3-level tree, so + // `split_off` walks down and moves a suffix at more than one level for + // most of these split points. + let n = MIN_INSERTS_HEIGHT_2; + for split_at in [0, 1, 2, n / 2, n - 2, n - 1, n] { + let mut map = BTreeMap::from_iter((0..n).map(|i| (i, i))); + let right = map.split_off(&split_at); + map.check(); + right.check(); + assert!(map.keys().copied().eq(0..split_at)); + assert!(right.keys().copied().eq(split_at..n)); + } +} + #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_into_iter_drop_leak_height_0() { diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index 87a79e6cf3f93..ee40b2df74226 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -1,5 +1,6 @@ use core::alloc::Allocator; use core::borrow::Borrow; +use core::{intrinsics, mem}; use super::node::ForceResult::*; use super::node::Root; @@ -44,13 +45,25 @@ impl Root { let mut left_node = left_root.borrow_mut(); let mut right_node = right_root.borrow_mut(); - loop { - let mut split_edge = match left_node.search_node(key) { - // key is going to the right tree - Found(kv) => kv.left_edge(), - GoDown(edge) => edge, - }; + // The first search runs before anything has moved, so a panic from the + // caller's `Ord`/`Borrow` impl here can unwind safely: `self` is + // untouched and the new right tree is still empty. + let mut split_edge = match left_node.search_node(key) { + // key is going to the right tree + Found(kv) => kv.left_edge(), + GoDown(edge) => edge, + }; + + // From the first `move_suffix` on, `left_root` and `right_root` share + // key-value pairs through two temporarily invalid tree structures, and + // neither is independently droppable until `fix_right_border` / + // `fix_left_border` repair them and the caller recomputes both lengths. + // A panic from a later `search_node` comparison would unwind out of + // that state and double-free the shared values (#158165), so abort + // instead of exposing it. + let guard = mem::DropGuard::new((), |()| intrinsics::abort()); + loop { split_edge.move_suffix(&mut right_node); match (split_edge.force(), right_node.force()) { @@ -61,10 +74,16 @@ impl Root { (Leaf(_), Leaf(_)) => break, _ => unreachable!(), } + + split_edge = match left_node.search_node(key) { + Found(kv) => kv.left_edge(), + GoDown(edge) => edge, + }; } left_root.fix_right_border(alloc.clone()); right_root.fix_left_border(alloc); + mem::DropGuard::dismiss(guard); right_root } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index eca8444812521..77e56d3ac54f4 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -28,6 +28,7 @@ #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] +#![feature(drop_guard)] #![feature(exact_size_is_empty)] #![feature(extend_one)] #![feature(extend_one_unchecked)] From d44d82780b034cc339410f3a1dc4368a59e5f57a Mon Sep 17 00:00:00 2001 From: Kevin Valerio Date: Tue, 16 Jun 2026 15:29:33 +0200 Subject: [PATCH 7/8] fix(typeck): preserve ambiguous subtrait pick lint --- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- .../ambiguous-glob-imported-subtrait.rs | 36 +++++++++++++++++++ .../ambiguous-glob-imported-subtrait.stderr | 20 +++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.rs create mode 100644 tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.stderr diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e88a3eacf8906..f5b8b9d6a1e6f 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -2468,7 +2468,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - let lint_ambiguous = match probes[0].0.kind { + let lint_ambiguous = match child_candidate.kind { TraitCandidate(_, lint) => lint, _ => false, }; diff --git a/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.rs b/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.rs new file mode 100644 index 0000000000000..61d1c16c8305b --- /dev/null +++ b/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.rs @@ -0,0 +1,36 @@ +// issue: + +#![feature(supertrait_item_shadowing)] +#![deny(ambiguous_glob_imported_traits)] + +trait DefaultPolicy { + fn allow_action(&self) -> bool { + false + } +} + +mod first_policy { + pub trait Role: crate::DefaultPolicy { + fn allow_action(&self) -> bool { + true + } + } + + impl crate::DefaultPolicy for u8 {} + impl Role for u8 {} +} + +mod second_policy { + pub trait Role: crate::DefaultPolicy { + fn audit_only(&self) {} + } +} + +use first_policy::*; +use second_policy::*; + +fn main() { + assert!(0u8.allow_action()); + //~^ ERROR Use of ambiguously glob imported trait `Role` + //~| WARN this was previously accepted by the compiler but is being phased out +} diff --git a/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.stderr b/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.stderr new file mode 100644 index 0000000000000..71d2c38c37ca6 --- /dev/null +++ b/tests/ui/supertrait-shadowing/ambiguous-glob-imported-subtrait.stderr @@ -0,0 +1,20 @@ +error: Use of ambiguously glob imported trait `Role` + --> $DIR/ambiguous-glob-imported-subtrait.rs:33:17 + | +LL | use first_policy::*; + | ------------ `Role` imported ambiguously here +... +LL | assert!(0u8.allow_action()); + | ^^^^^^^^^^^^ + | + = help: Import `Role` explicitly + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #152822 +note: the lint level is defined here + --> $DIR/ambiguous-glob-imported-subtrait.rs:4:9 + | +LL | #![deny(ambiguous_glob_imported_traits)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + From 0feecd60a585d86a900f72488de91fd5daa057c2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Aug 2026 15:49:32 +0200 Subject: [PATCH 8/8] Move rustdoc ui doctests into the right testsuite --- tests/rustdoc-html/doctest/doctest-runtool.rs | 13 ------------- .../doctest/auxiliary/doctest-runtool.rs | 0 .../doctest/auxiliary/empty.rs | 0 .../doctest/doctest-cfg-feature-30252.rs | 2 ++ .../doctest/doctest-cfg-feature-30252.stdout | 6 ++++++ .../doctest/doctest-crate-attributes-38129.rs | 4 +++- .../doctest/doctest-crate-attributes-38129.stdout | 10 ++++++++++ .../doctest/doctest-hide-empty-line-23106.rs | 2 ++ .../doctest/doctest-hide-empty-line-23106.stdout | 6 ++++++ .../doctest/doctest-ignore-32556.rs | 4 ++++ .../rustdoc-ui/doctest/doctest-ignore-32556.stdout | 6 ++++++ .../doctest/doctest-include-43153.rs | 2 ++ .../rustdoc-ui/doctest/doctest-include-43153.stdout | 6 ++++++ .../doctest/doctest-macro-38219.rs | 1 + .../doctest/doctest-manual-crate-name.rs | 2 ++ .../doctest/doctest-manual-crate-name.stdout | 6 ++++++ .../doctest/doctest-markdown-inline-parse-23744.rs | 4 +++- .../doctest-markdown-inline-parse-23744.stdout | 7 +++++++ .../doctest-markdown-trailing-docblock-48377.rs | 4 +++- .../doctest-markdown-trailing-docblock-48377.stdout | 7 +++++++ .../doctest-multi-line-string-literal-25944.rs | 2 ++ .../doctest-multi-line-string-literal-25944.stdout | 6 ++++++ 22 files changed, 84 insertions(+), 16 deletions(-) delete mode 100644 tests/rustdoc-html/doctest/doctest-runtool.rs rename tests/{rustdoc-html => rustdoc-ui}/doctest/auxiliary/doctest-runtool.rs (100%) rename tests/{rustdoc-html => rustdoc-ui}/doctest/auxiliary/empty.rs (100%) rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-cfg-feature-30252.rs (70%) create mode 100644 tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-crate-attributes-38129.rs (95%) create mode 100644 tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-hide-empty-line-23106.rs (63%) create mode 100644 tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-ignore-32556.rs (63%) create mode 100644 tests/rustdoc-ui/doctest/doctest-ignore-32556.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-include-43153.rs (76%) create mode 100644 tests/rustdoc-ui/doctest/doctest-include-43153.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-macro-38219.rs (70%) rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-manual-crate-name.rs (51%) create mode 100644 tests/rustdoc-ui/doctest/doctest-manual-crate-name.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-markdown-inline-parse-23744.rs (69%) create mode 100644 tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-markdown-trailing-docblock-48377.rs (59%) create mode 100644 tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.stdout rename tests/{rustdoc-html => rustdoc-ui}/doctest/doctest-multi-line-string-literal-25944.rs (72%) create mode 100644 tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.stdout diff --git a/tests/rustdoc-html/doctest/doctest-runtool.rs b/tests/rustdoc-html/doctest/doctest-runtool.rs deleted file mode 100644 index c4fb02e5228cc..0000000000000 --- a/tests/rustdoc-html/doctest/doctest-runtool.rs +++ /dev/null @@ -1,13 +0,0 @@ -// Tests that the --test-runtool argument works. - -//@ ignore-cross-compile -//@ aux-bin: doctest-runtool.rs -//@ compile-flags: --test -//@ compile-flags: --test-runtool=auxiliary/bin/doctest-runtool -//@ compile-flags: --test-runtool-arg=arg1 --test-runtool-arg -//@ compile-flags: 'arg2 with space' - -/// ``` -/// assert_eq!(std::env::var("DOCTEST_RUNTOOL_CHECK"), Ok("xyz".to_string())); -/// ``` -pub fn main() {} diff --git a/tests/rustdoc-html/doctest/auxiliary/doctest-runtool.rs b/tests/rustdoc-ui/doctest/auxiliary/doctest-runtool.rs similarity index 100% rename from tests/rustdoc-html/doctest/auxiliary/doctest-runtool.rs rename to tests/rustdoc-ui/doctest/auxiliary/doctest-runtool.rs diff --git a/tests/rustdoc-html/doctest/auxiliary/empty.rs b/tests/rustdoc-ui/doctest/auxiliary/empty.rs similarity index 100% rename from tests/rustdoc-html/doctest/auxiliary/empty.rs rename to tests/rustdoc-ui/doctest/auxiliary/empty.rs diff --git a/tests/rustdoc-html/doctest/doctest-cfg-feature-30252.rs b/tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.rs similarity index 70% rename from tests/rustdoc-html/doctest/doctest-cfg-feature-30252.rs rename to tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.rs index 0a2e3f3cf9589..eae5077783c82 100644 --- a/tests/rustdoc-html/doctest/doctest-cfg-feature-30252.rs +++ b/tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test --cfg feature="bar" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // https://github.com/rust-lang/rust/issues/30252 #![crate_name="issue_30252"] diff --git a/tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.stdout b/tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.stdout new file mode 100644 index 0000000000000..24dc3daaab3dc --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-cfg-feature-30252.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-cfg-feature-30252.rs - foo (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-crate-attributes-38129.rs b/tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.rs similarity index 95% rename from tests/rustdoc-html/doctest/doctest-crate-attributes-38129.rs rename to tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.rs index b9c837188325d..b08eb194bb880 100644 --- a/tests/rustdoc-html/doctest/doctest-crate-attributes-38129.rs +++ b/tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // This file tests the source-partitioning behavior of rustdoc. // Each test contains some code that should be put into the generated diff --git a/tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.stdout b/tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.stdout new file mode 100644 index 0000000000000..e6c8c688ba10a --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-crate-attributes-38129.stdout @@ -0,0 +1,10 @@ + +running 5 tests +test $DIR/doctest-crate-attributes-38129.rs - both_attrs (line 49) ... ok +test $DIR/doctest-crate-attributes-38129.rs - both_attrs_reverse (line 76) ... ok +test $DIR/doctest-crate-attributes-38129.rs - feature_attr (line 43) ... ok +test $DIR/doctest-crate-attributes-38129.rs - non_feature_attr (line 17) ... ok +test $DIR/doctest-crate-attributes-38129.rs - simple (line 12) ... ok + +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-hide-empty-line-23106.rs b/tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.rs similarity index 63% rename from tests/rustdoc-html/doctest/doctest-hide-empty-line-23106.rs rename to tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.rs index 2e8a8f3f1e2b6..dc39eb328b3c6 100644 --- a/tests/rustdoc-html/doctest/doctest-hide-empty-line-23106.rs +++ b/tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // https://github.com/rust-lang/rust/issues/23106 #![crate_name="issue_23106"] diff --git a/tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.stdout b/tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.stdout new file mode 100644 index 0000000000000..9dfbad9388c6f --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-hide-empty-line-23106.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-hide-empty-line-23106.rs - main (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-ignore-32556.rs b/tests/rustdoc-ui/doctest/doctest-ignore-32556.rs similarity index 63% rename from tests/rustdoc-html/doctest/doctest-ignore-32556.rs rename to tests/rustdoc-ui/doctest/doctest-ignore-32556.rs index 99da9358bd6ba..8f0b2ea2ee152 100644 --- a/tests/rustdoc-html/doctest/doctest-ignore-32556.rs +++ b/tests/rustdoc-ui/doctest/doctest-ignore-32556.rs @@ -1,4 +1,8 @@ // https://github.com/rust-lang/rust/issues/32556 +//@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + #![crate_name="issue_32556"] /// Blah blah blah diff --git a/tests/rustdoc-ui/doctest/doctest-ignore-32556.stdout b/tests/rustdoc-ui/doctest/doctest-ignore-32556.stdout new file mode 100644 index 0000000000000..e5d188b4e98e2 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-ignore-32556.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-ignore-32556.rs - foo (line 9) ... ignored + +test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-include-43153.rs b/tests/rustdoc-ui/doctest/doctest-include-43153.rs similarity index 76% rename from tests/rustdoc-html/doctest/doctest-include-43153.rs rename to tests/rustdoc-ui/doctest/doctest-include-43153.rs index 0f63c84de39b9..3cfe76fd7c323 100644 --- a/tests/rustdoc-html/doctest/doctest-include-43153.rs +++ b/tests/rustdoc-ui/doctest/doctest-include-43153.rs @@ -4,6 +4,8 @@ // which the test is declared. //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass /// ```rust /// include!("auxiliary/empty.rs"); diff --git a/tests/rustdoc-ui/doctest/doctest-include-43153.stdout b/tests/rustdoc-ui/doctest/doctest-include-43153.stdout new file mode 100644 index 0000000000000..d79c698517e4d --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-include-43153.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-include-43153.rs - Foo (line 10) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-macro-38219.rs b/tests/rustdoc-ui/doctest/doctest-macro-38219.rs similarity index 70% rename from tests/rustdoc-html/doctest/doctest-macro-38219.rs rename to tests/rustdoc-ui/doctest/doctest-macro-38219.rs index 574e84523783e..197efdbe389bb 100644 --- a/tests/rustdoc-html/doctest/doctest-macro-38219.rs +++ b/tests/rustdoc-ui/doctest/doctest-macro-38219.rs @@ -1,6 +1,7 @@ // https://github.com/rust-lang/rust/issues/38219 //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ should-fail /// ``` diff --git a/tests/rustdoc-html/doctest/doctest-manual-crate-name.rs b/tests/rustdoc-ui/doctest/doctest-manual-crate-name.rs similarity index 51% rename from tests/rustdoc-html/doctest/doctest-manual-crate-name.rs rename to tests/rustdoc-ui/doctest/doctest-manual-crate-name.rs index 8d526959fe1ef..a2e65323a2e81 100644 --- a/tests/rustdoc-html/doctest/doctest-manual-crate-name.rs +++ b/tests/rustdoc-ui/doctest/doctest-manual-crate-name.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass //! ``` //! #![crate_name="asdf"] diff --git a/tests/rustdoc-ui/doctest/doctest-manual-crate-name.stdout b/tests/rustdoc-ui/doctest/doctest-manual-crate-name.stdout new file mode 100644 index 0000000000000..b30c95680c930 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-manual-crate-name.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-manual-crate-name.rs - (line 5) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-markdown-inline-parse-23744.rs b/tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.rs similarity index 69% rename from tests/rustdoc-html/doctest/doctest-markdown-inline-parse-23744.rs rename to tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.rs index 2ba9176ae88c2..72976d03c8730 100644 --- a/tests/rustdoc-html/doctest/doctest-markdown-inline-parse-23744.rs +++ b/tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // https://github.com/rust-lang/rust/issues/23744 #![crate_name="issue_23744"] diff --git a/tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.stdout b/tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.stdout new file mode 100644 index 0000000000000..46bd27f8bcdd0 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-markdown-inline-parse-23744.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/doctest-markdown-inline-parse-23744.rs - foo (line 10) ... ok +test $DIR/doctest-markdown-inline-parse-23744.rs - foo (line 14) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-markdown-trailing-docblock-48377.rs b/tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.rs similarity index 59% rename from tests/rustdoc-html/doctest/doctest-markdown-trailing-docblock-48377.rs rename to tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.rs index 74c1a9d245971..7f6883e3ef027 100644 --- a/tests/rustdoc-html/doctest/doctest-markdown-trailing-docblock-48377.rs +++ b/tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.rs @@ -1,4 +1,6 @@ -//@ compile-flags:--test +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // https://github.com/rust-lang/rust/issues/48377 diff --git a/tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.stdout b/tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.stdout new file mode 100644 index 0000000000000..e99a0047b2a91 --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-markdown-trailing-docblock-48377.stdout @@ -0,0 +1,7 @@ + +running 2 tests +test $DIR/doctest-markdown-trailing-docblock-48377.rs - (line 14) ... ok +test $DIR/doctest-markdown-trailing-docblock-48377.rs - (line 9) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-html/doctest/doctest-multi-line-string-literal-25944.rs b/tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.rs similarity index 72% rename from tests/rustdoc-html/doctest/doctest-multi-line-string-literal-25944.rs rename to tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.rs index 2b9c6119c67db..78420e780bc2f 100644 --- a/tests/rustdoc-html/doctest/doctest-multi-line-string-literal-25944.rs +++ b/tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.rs @@ -1,4 +1,6 @@ //@ compile-flags:--test +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass // https://github.com/rust-lang/rust/issues/25944 #![crate_name="issue_25944"] diff --git a/tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.stdout b/tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.stdout new file mode 100644 index 0000000000000..4cc88ece099da --- /dev/null +++ b/tests/rustdoc-ui/doctest/doctest-multi-line-string-literal-25944.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/doctest-multi-line-string-literal-25944.rs - main (line 8) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +