Auto merge of #118732 - matthiaskrgr:rollup-ub9pgjm, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #118505 (Elaborate on ip_addr bit conversion endianness)
 - #118581 (OnceLock: Add note about drop and statics)
 - #118677 ([rustdoc] Fix display of features)
 - #118690 (coverage: Avoid unnecessary macros in unit tests)
 - #118693 (Tell MirUsedCollector that the pointer alignment checks calls its panic symbol)
 - #118695 (coverage: Merge refined spans in a separate final pass)
 - #118709 (fix jobserver GLOBAL_CLIENT_CHECKED uninitialized before use)
 - #118722 (rustdoc: remove unused parameter `reversed` from onEach(Lazy))

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-12-08 07:12:26 +00:00
commit 8043f62258
20 changed files with 216 additions and 194 deletions

View file

@ -795,10 +795,6 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "coverage_test_macros"
version = "0.0.0"
[[package]]
name = "cpufeatures"
version = "0.2.8"
@ -4266,7 +4262,6 @@ dependencies = [
name = "rustc_mir_transform"
version = "0.0.0"
dependencies = [
"coverage_test_macros",
"either",
"itertools",
"rustc_arena",

View file

@ -52,7 +52,7 @@ fn default_client() -> Client {
static GLOBAL_CLIENT_CHECKED: OnceLock<Client> = OnceLock::new();
pub fn check(report_warning: impl FnOnce(&'static str)) {
pub fn initialize_checked(report_warning: impl FnOnce(&'static str)) {
let client_checked = match &*GLOBAL_CLIENT {
Ok(client) => client.clone(),
Err(e) => {

View file

@ -316,6 +316,10 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
// Set parallel mode before thread pool creation, which will create `Lock`s.
rustc_data_structures::sync::set_dyn_thread_safe_mode(config.opts.unstable_opts.threads > 1);
// Check jobserver before run_in_thread_pool_with_globals, which call jobserver::acquire_thread
let early_handler = EarlyErrorHandler::new(config.opts.error_format);
early_handler.initialize_checked_jobserver();
util::run_in_thread_pool_with_globals(
config.opts.edition,
config.opts.unstable_opts.threads,

View file

@ -27,6 +27,8 @@ use std::sync::Arc;
fn mk_session(matches: getopts::Matches) -> (Session, Cfg) {
let mut early_handler = EarlyErrorHandler::new(ErrorOutputType::default());
early_handler.initialize_checked_jobserver();
let registry = registry::Registry::new(&[]);
let sessopts = build_session_options(&mut early_handler, &matches);
let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);

View file

@ -27,8 +27,3 @@ rustc_trait_selection = { path = "../rustc_trait_selection" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
# tidy-alphabetical-end
[dev-dependencies]
# tidy-alphabetical-start
coverage_test_macros = { path = "src/coverage/test_macros" }
# tidy-alphabetical-end

View file

@ -89,10 +89,10 @@ impl CoverageSpan {
}
}
pub fn merge_from(&mut self, mut other: CoverageSpan) {
debug_assert!(self.is_mergeable(&other));
pub fn merge_from(&mut self, other: &Self) {
debug_assert!(self.is_mergeable(other));
self.span = self.span.to(other.span);
self.merged_spans.append(&mut other.merged_spans);
self.merged_spans.extend_from_slice(&other.merged_spans);
}
pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
@ -267,7 +267,7 @@ impl<'a> CoverageSpansGenerator<'a> {
if curr.is_mergeable(prev) {
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
let prev = self.take_prev();
self.curr_mut().merge_from(prev);
self.curr_mut().merge_from(&prev);
self.maybe_push_macro_name_span();
// Note that curr.span may now differ from curr_original_span
} else if prev.span.hi() <= curr.span.lo() {
@ -275,7 +275,7 @@ impl<'a> CoverageSpansGenerator<'a> {
" different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}",
);
let prev = self.take_prev();
self.push_refined_span(prev);
self.refined_spans.push(prev);
self.maybe_push_macro_name_span();
} else if prev.is_closure {
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
@ -322,11 +322,10 @@ impl<'a> CoverageSpansGenerator<'a> {
let prev = self.take_prev();
debug!(" AT END, adding last prev={prev:?}");
// Take `pending_dups` so that we can drain it while calling self methods.
// It is never used as a field after this point.
for dup in std::mem::take(&mut self.pending_dups) {
// Drain any remaining dups into the output.
for dup in self.pending_dups.drain(..) {
debug!(" ...adding at least one pending dup={:?}", dup);
self.push_refined_span(dup);
self.refined_spans.push(dup);
}
// Async functions wrap a closure that implements the body to be executed. The enclosing
@ -343,9 +342,20 @@ impl<'a> CoverageSpansGenerator<'a> {
};
if !body_ends_with_closure {
self.push_refined_span(prev);
self.refined_spans.push(prev);
}
// Do one last merge pass, to simplify the output.
self.refined_spans.dedup_by(|b, a| {
if a.is_mergeable(b) {
debug!(?a, ?b, "merging list-adjacent refined spans");
a.merge_from(b);
true
} else {
false
}
});
// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
// regions for the current function leave room for the closure's own coverage regions
// (injected separately, from the closure's own MIR).
@ -353,18 +363,6 @@ impl<'a> CoverageSpansGenerator<'a> {
self.refined_spans
}
fn push_refined_span(&mut self, covspan: CoverageSpan) {
if let Some(last) = self.refined_spans.last_mut()
&& last.is_mergeable(&covspan)
{
// Instead of pushing the new span, merge it with the last refined span.
debug!(?last, ?covspan, "merging new refined span with last refined span");
last.merge_from(covspan);
} else {
self.refined_spans.push(covspan);
}
}
/// If `curr` is part of a new macro expansion, carve out and push a separate
/// span that ends just after the macro name and its subsequent `!`.
fn maybe_push_macro_name_span(&mut self) {
@ -397,7 +395,7 @@ impl<'a> CoverageSpansGenerator<'a> {
" and curr starts a new macro expansion, so add a new span just for \
the macro `{visible_macro}!`, new span={macro_name_cov:?}",
);
self.push_refined_span(macro_name_cov);
self.refined_spans.push(macro_name_cov);
}
fn curr(&self) -> &CoverageSpan {
@ -454,19 +452,14 @@ impl<'a> CoverageSpansGenerator<'a> {
previous iteration, or prev started a new disjoint span"
);
if last_dup.span.hi() <= self.curr().span.lo() {
// Temporarily steal `pending_dups` into a local, so that we can
// drain it while calling other self methods.
let mut pending_dups = std::mem::take(&mut self.pending_dups);
for dup in pending_dups.drain(..) {
for dup in self.pending_dups.drain(..) {
debug!(" ...adding at least one pending={:?}", dup);
self.push_refined_span(dup);
self.refined_spans.push(dup);
}
// The list of dups is now empty, but we can recycle its capacity.
assert!(pending_dups.is_empty() && self.pending_dups.is_empty());
self.pending_dups = pending_dups;
} else {
self.pending_dups.clear();
}
assert!(self.pending_dups.is_empty());
}
/// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
@ -513,22 +506,18 @@ impl<'a> CoverageSpansGenerator<'a> {
let has_pre_closure_span = prev.span.lo() < right_cutoff;
let has_post_closure_span = prev.span.hi() > right_cutoff;
// Temporarily steal `pending_dups` into a local, so that we can
// mutate and/or drain it while calling other self methods.
let mut pending_dups = std::mem::take(&mut self.pending_dups);
if has_pre_closure_span {
let mut pre_closure = self.prev().clone();
pre_closure.span = pre_closure.span.with_hi(left_cutoff);
debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
if !pending_dups.is_empty() {
for mut dup in pending_dups.iter().cloned() {
dup.span = dup.span.with_hi(left_cutoff);
debug!(" ...and at least one pre_closure dup={:?}", dup);
self.push_refined_span(dup);
}
for mut dup in self.pending_dups.iter().cloned() {
dup.span = dup.span.with_hi(left_cutoff);
debug!(" ...and at least one pre_closure dup={:?}", dup);
self.refined_spans.push(dup);
}
self.push_refined_span(pre_closure);
self.refined_spans.push(pre_closure);
}
if has_post_closure_span {
@ -537,19 +526,17 @@ impl<'a> CoverageSpansGenerator<'a> {
// about how the `CoverageSpan`s are ordered.)
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev());
for dup in pending_dups.iter_mut() {
for dup in &mut self.pending_dups {
debug!(" ...and at least one overlapping dup={:?}", dup);
dup.span = dup.span.with_lo(right_cutoff);
}
let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev.
self.push_refined_span(closure_covspan); // since self.prev() was already updated
} else {
pending_dups.clear();
}
// Restore the modified post-closure spans, or the empty vector's capacity.
assert!(self.pending_dups.is_empty());
self.pending_dups = pending_dups;
let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev.
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
} else {
self.pending_dups.clear();
}
}
/// Called if `curr.span` equals `prev_original_span` (and potentially equal to all
@ -645,7 +632,7 @@ impl<'a> CoverageSpansGenerator<'a> {
} else {
debug!(" ... adding modified prev={:?}", self.prev());
let prev = self.take_prev();
self.push_refined_span(prev);
self.refined_spans.push(prev);
}
} else {
// with `pending_dups`, `prev` cannot have any statements that don't overlap

View file

@ -1,7 +0,0 @@
[package]
name = "coverage_test_macros"
version = "0.0.0"
edition = "2021"
[lib]
proc-macro = true

View file

@ -1,6 +0,0 @@
use proc_macro::TokenStream;
#[proc_macro]
pub fn let_bcb(item: TokenStream) -> TokenStream {
format!("let bcb{item} = graph::BasicCoverageBlock::from_usize({item});").parse().unwrap()
}

View file

@ -27,8 +27,6 @@
use super::counters;
use super::graph::{self, BasicCoverageBlock};
use coverage_test_macros::let_bcb;
use itertools::Itertools;
use rustc_data_structures::graph::WithNumNodes;
use rustc_data_structures::graph::WithSuccessors;
@ -37,6 +35,10 @@ use rustc_middle::mir::*;
use rustc_middle::ty;
use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
fn bcb(index: u32) -> BasicCoverageBlock {
BasicCoverageBlock::from_u32(index)
}
// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
const TEMP_BLOCK: BasicBlock = BasicBlock::MAX;
@ -300,12 +302,15 @@ fn goto_switchint<'a>() -> Body<'a> {
mir_body
}
macro_rules! assert_successors {
($basic_coverage_blocks:ident, $i:ident, [$($successor:ident),*]) => {
let mut successors = $basic_coverage_blocks.successors[$i].clone();
successors.sort_unstable();
assert_eq!(successors, vec![$($successor),*]);
}
#[track_caller]
fn assert_successors(
basic_coverage_blocks: &graph::CoverageGraph,
bcb: BasicCoverageBlock,
expected_successors: &[BasicCoverageBlock],
) {
let mut successors = basic_coverage_blocks.successors[bcb].clone();
successors.sort_unstable();
assert_eq!(successors, expected_successors);
}
#[test]
@ -334,13 +339,9 @@ fn test_covgraph_goto_switchint() {
basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
);
let_bcb!(0);
let_bcb!(1);
let_bcb!(2);
assert_successors!(basic_coverage_blocks, bcb0, [bcb1, bcb2]);
assert_successors!(basic_coverage_blocks, bcb1, []);
assert_successors!(basic_coverage_blocks, bcb2, []);
assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1), bcb(2)]);
assert_successors(&basic_coverage_blocks, bcb(1), &[]);
assert_successors(&basic_coverage_blocks, bcb(2), &[]);
}
/// Create a mock `Body` with a loop.
@ -418,15 +419,10 @@ fn test_covgraph_switchint_then_loop_else_return() {
basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
);
let_bcb!(0);
let_bcb!(1);
let_bcb!(2);
let_bcb!(3);
assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
assert_successors!(basic_coverage_blocks, bcb2, []);
assert_successors!(basic_coverage_blocks, bcb3, [bcb1]);
assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1)]);
assert_successors(&basic_coverage_blocks, bcb(1), &[bcb(2), bcb(3)]);
assert_successors(&basic_coverage_blocks, bcb(2), &[]);
assert_successors(&basic_coverage_blocks, bcb(3), &[bcb(1)]);
}
/// Create a mock `Body` with nested loops.
@ -546,21 +542,13 @@ fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
);
let_bcb!(0);
let_bcb!(1);
let_bcb!(2);
let_bcb!(3);
let_bcb!(4);
let_bcb!(5);
let_bcb!(6);
assert_successors!(basic_coverage_blocks, bcb0, [bcb1]);
assert_successors!(basic_coverage_blocks, bcb1, [bcb2, bcb3]);
assert_successors!(basic_coverage_blocks, bcb2, []);
assert_successors!(basic_coverage_blocks, bcb3, [bcb4]);
assert_successors!(basic_coverage_blocks, bcb4, [bcb5, bcb6]);
assert_successors!(basic_coverage_blocks, bcb5, [bcb1]);
assert_successors!(basic_coverage_blocks, bcb6, [bcb4]);
assert_successors(&basic_coverage_blocks, bcb(0), &[bcb(1)]);
assert_successors(&basic_coverage_blocks, bcb(1), &[bcb(2), bcb(3)]);
assert_successors(&basic_coverage_blocks, bcb(2), &[]);
assert_successors(&basic_coverage_blocks, bcb(3), &[bcb(4)]);
assert_successors(&basic_coverage_blocks, bcb(4), &[bcb(5), bcb(6)]);
assert_successors(&basic_coverage_blocks, bcb(5), &[bcb(1)]);
assert_successors(&basic_coverage_blocks, bcb(6), &[bcb(4)]);
}
#[test]
@ -595,10 +583,7 @@ fn test_find_loop_backedges_one() {
backedges
);
let_bcb!(1);
let_bcb!(3);
assert_eq!(backedges[bcb1], vec![bcb3]);
assert_eq!(backedges[bcb(1)], &[bcb(3)]);
}
#[test]
@ -613,13 +598,8 @@ fn test_find_loop_backedges_two() {
backedges
);
let_bcb!(1);
let_bcb!(4);
let_bcb!(5);
let_bcb!(6);
assert_eq!(backedges[bcb1], vec![bcb5]);
assert_eq!(backedges[bcb4], vec![bcb6]);
assert_eq!(backedges[bcb(1)], &[bcb(5)]);
assert_eq!(backedges[bcb(4)], &[bcb(6)]);
}
#[test]
@ -632,13 +612,11 @@ fn test_traverse_coverage_with_loops() {
traversed_in_order.push(bcb);
}
let_bcb!(6);
// bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
// bcb6 are inside the first loop.
assert_eq!(
*traversed_in_order.last().expect("should have elements"),
bcb6,
bcb(6),
"bcb6 should not be visited until all nodes inside the first loop have been visited"
);
}
@ -656,20 +634,18 @@ fn test_make_bcb_counters() {
coverage_counters.make_bcb_counters(&basic_coverage_blocks, bcb_has_coverage_spans);
assert_eq!(coverage_counters.num_expressions(), 0);
let_bcb!(1);
assert_eq!(
0, // bcb1 has a `Counter` with id = 0
match coverage_counters.bcb_counter(bcb1).expect("should have a counter") {
match coverage_counters.bcb_counter(bcb(1)).expect("should have a counter") {
counters::BcbCounter::Counter { id, .. } => id,
_ => panic!("expected a Counter"),
}
.as_u32()
);
let_bcb!(2);
assert_eq!(
1, // bcb2 has a `Counter` with id = 1
match coverage_counters.bcb_counter(bcb2).expect("should have a counter") {
match coverage_counters.bcb_counter(bcb(2)).expect("should have a counter") {
counters::BcbCounter::Counter { id, .. } => id,
_ => panic!("expected a Counter"),
}

View file

@ -844,6 +844,9 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
mir::TerminatorKind::Assert { ref msg, .. } => {
let lang_item = match &**msg {
mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck,
mir::AssertKind::MisalignedPointerDereference { .. } => {
LangItem::PanicMisalignedPointerDereference
}
_ => LangItem::Panic,
};
push_mono_lang_item(self, lang_item);

View file

@ -1474,17 +1474,6 @@ pub fn build_session(
let asm_arch =
if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None };
// Check jobserver before getting `jobserver::client`.
jobserver::check(|err| {
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
parse_sess
.span_diagnostic
.struct_warn(err)
.note("the build environment is likely misconfigured")
.emit()
});
let sess = Session {
target: target_cfg,
host,
@ -1792,6 +1781,18 @@ impl EarlyErrorHandler {
pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
self.handler.struct_warn(msg).emit()
}
pub fn initialize_checked_jobserver(&self) {
// initialize jobserver before getting `jobserver::client` and `build_session`.
jobserver::initialize_checked(|err| {
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
self.handler
.struct_warn(err)
.note("the build environment is likely misconfigured")
.emit()
});
}
}
fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {

View file

@ -468,7 +468,13 @@ impl Ipv4Addr {
#[unstable(feature = "ip_bits", issue = "113744")]
pub const BITS: u32 = 32;
/// Converts an IPv4 address into host byte order `u32`.
/// Converts an IPv4 address into a `u32` representation using native byte order.
///
/// Although IPv4 addresses are big-endian, the `u32` value will use the target platform's
/// native byte order. That is, the `u32` value is an integer representation of the IPv4
/// address and not an integer interpretation of the IPv4 address's big-endian bitstring. This
/// means that the `u32` value masked with `0xffffff00` will set the last octet in the address
/// to 0, regardless of the target platform's endianness.
///
/// # Examples
///
@ -479,6 +485,16 @@ impl Ipv4Addr {
/// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
/// assert_eq!(0x12345678, addr.to_bits());
/// ```
///
/// ```
/// #![feature(ip_bits)]
/// use std::net::Ipv4Addr;
///
/// let addr = Ipv4Addr::new(0x12, 0x34, 0x56, 0x78);
/// let addr_bits = addr.to_bits() & 0xffffff00;
/// assert_eq!(Ipv4Addr::new(0x12, 0x34, 0x56, 0x00), Ipv4Addr::from_bits(addr_bits));
///
/// ```
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
#[unstable(feature = "ip_bits", issue = "113744")]
#[must_use]
@ -487,7 +503,9 @@ impl Ipv4Addr {
u32::from_be_bytes(self.octets)
}
/// Converts a host byte order `u32` into an IPv4 address.
/// Converts a native byte order `u32` into an IPv4 address.
///
/// See [`Ipv4Addr::to_bits`] for an explanation on endianness.
///
/// # Examples
///
@ -1224,7 +1242,13 @@ impl Ipv6Addr {
#[unstable(feature = "ip_bits", issue = "113744")]
pub const BITS: u32 = 128;
/// Converts an IPv6 address into host byte order `u128`.
/// Converts an IPv6 address into a `u128` representation using native byte order.
///
/// Although IPv6 addresses are big-endian, the `u128` value will use the target platform's
/// native byte order. That is, the `u128` value is an integer representation of the IPv6
/// address and not an integer interpretation of the IPv6 address's big-endian bitstring. This
/// means that the `u128` value masked with `0xffffffffffffffffffffffffffff0000_u128` will set
/// the last segment in the address to 0, regardless of the target platform's endianness.
///
/// # Examples
///
@ -1238,6 +1262,24 @@ impl Ipv6Addr {
/// );
/// assert_eq!(0x102030405060708090A0B0C0D0E0F00D_u128, u128::from(addr));
/// ```
///
/// ```
/// #![feature(ip_bits)]
/// use std::net::Ipv6Addr;
///
/// let addr = Ipv6Addr::new(
/// 0x1020, 0x3040, 0x5060, 0x7080,
/// 0x90A0, 0xB0C0, 0xD0E0, 0xF00D,
/// );
/// let addr_bits = addr.to_bits() & 0xffffffffffffffffffffffffffff0000_u128;
/// assert_eq!(
/// Ipv6Addr::new(
/// 0x1020, 0x3040, 0x5060, 0x7080,
/// 0x90A0, 0xB0C0, 0xD0E0, 0x0000,
/// ),
/// Ipv6Addr::from_bits(addr_bits));
///
/// ```
#[rustc_const_unstable(feature = "ip_bits", issue = "113744")]
#[unstable(feature = "ip_bits", issue = "113744")]
#[must_use]
@ -1246,7 +1288,9 @@ impl Ipv6Addr {
u128::from_be_bytes(self.octets)
}
/// Converts a host byte order `u128` into an IPv6 address.
/// Converts a native byte order `u128` into an IPv6 address.
///
/// See [`Ipv6Addr::to_bits`] for an explanation on endianness.
///
/// # Examples
///

View file

@ -17,25 +17,36 @@ use crate::sync::Once;
/// lazy static or memoizing):
///
/// ```
/// use std::collections::HashMap;
/// use std::sync::OnceLock;
///
/// fn hash_map() -> &'static HashMap<u32, char> {
/// static HASHMAP: OnceLock<HashMap<u32, char>> = OnceLock::new();
/// HASHMAP.get_or_init(|| {
/// let mut m = HashMap::new();
/// m.insert(0, 'a');
/// m.insert(1, 'b');
/// m.insert(2, 'c');
/// m
/// })
/// struct DeepThought {
/// answer: String,
/// }
///
/// // The `HashMap` is built, stored in the `OnceLock`, and returned.
/// let _ = hash_map();
/// impl DeepThought {
/// # fn great_question() -> String {
/// # "42".to_string()
/// # }
/// #
/// fn new() -> Self {
/// Self {
/// // M3 Ultra takes about 16 million years in --release config
/// answer: Self::great_question(),
/// }
/// }
/// }
///
/// // The `HashMap` is retrieved from the `OnceLock` and returned.
/// let _ = hash_map();
/// fn computation() -> &'static DeepThought {
/// // n.b. static items do not call [`Drop`] on program termination, so if
/// // [`DeepThought`] impls Drop, that will not be used for this instance.
/// static COMPUTATION: OnceLock<DeepThought> = OnceLock::new();
/// COMPUTATION.get_or_init(|| DeepThought::new())
/// }
///
/// // The `DeepThought` is built, stored in the `OnceLock`, and returned.
/// let _ = computation().answer;
/// // The `DeepThought` is retrieved from the `OnceLock` and returned.
/// let _ = computation().answer;
/// ```
///
/// Writing to a `OnceLock` from a separate thread:

View file

@ -1081,15 +1081,9 @@ so that we can apply CSS-filters to change the arrow color in themes */
}
.item-info .stab {
/* This min-height is needed to unify the height of the stab elements because some of them
have emojis.
*/
min-height: 36px;
display: flex;
display: block;
padding: 3px;
margin-bottom: 5px;
align-items: center;
vertical-align: text-bottom;
}
.item-name .stab {
margin-left: 0.3125em;
@ -1112,17 +1106,26 @@ so that we can apply CSS-filters to change the arrow color in themes */
color: var(--stab-code-color);
}
.stab .emoji {
.stab .emoji, .item-info .stab::before {
font-size: 1.25rem;
}
.stab .emoji {
margin-right: 0.3rem;
}
.item-info .stab::before {
/* ensure badges with emoji and without it have same height */
content: "\0";
width: 0;
display: inline-block;
color: transparent;
}
/* Black one-pixel outline around emoji shapes */
.emoji {
text-shadow:
1px 0 0 black,
-1px 0 0 black,
0 1px 0 black,
0 1px 0 black,
0 -1px 0 black;
}

View file

@ -51,22 +51,11 @@ function removeClass(elem, className) {
* Run a callback for every element of an Array.
* @param {Array<?>} arr - The array to iterate over
* @param {function(?)} func - The callback
* @param {boolean} [reversed] - Whether to iterate in reverse
*/
function onEach(arr, func, reversed) {
if (arr && arr.length > 0) {
if (reversed) {
for (let i = arr.length - 1; i >= 0; --i) {
if (func(arr[i])) {
return true;
}
}
} else {
for (const elem of arr) {
if (func(elem)) {
return true;
}
}
function onEach(arr, func) {
for (const elem of arr) {
if (func(elem)) {
return true;
}
}
return false;
@ -80,14 +69,12 @@ function onEach(arr, func, reversed) {
* https://developer.mozilla.org/en-US/docs/Web/API/NodeList
* @param {NodeList<?>|HTMLCollection<?>} lazyArray - An array to iterate over
* @param {function(?)} func - The callback
* @param {boolean} [reversed] - Whether to iterate in reverse
*/
// eslint-disable-next-line no-unused-vars
function onEachLazy(lazyArray, func, reversed) {
function onEachLazy(lazyArray, func) {
return onEach(
Array.prototype.slice.call(lazyArray),
func,
reversed);
func);
}
function updateLocalStorage(name, value) {

View file

@ -4,5 +4,3 @@ warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--
error: no input filename given
warning: 1 warning emitted

View file

@ -2,5 +2,3 @@ warning: failed to connect to jobserver from environment variable `MAKEFLAGS="--
|
= note: the build environment is likely misconfigured
warning: 1 warning emitted

View file

@ -8,7 +8,22 @@ assert-size: (".item-info", {"width": 840})
assert-size: (".item-info .stab", {"width": 289})
assert-position: (".item-info .stab", {"x": 245})
// We check that the display of the feature elements is not broken. It serves as regression
// test for <https://github.com/rust-lang/rust/issues/118615>.
set-window-size: (850, 800)
store-position: (
"//*[@class='stab portability']//code[text()='Win32_System']",
{"x": first_line_x, "y": first_line_y},
)
store-position: (
"//*[@class='stab portability']//code[text()='Win32_System_Diagnostics']",
{"x": second_line_x, "y": second_line_y},
)
assert: |first_line_x| != |second_line_x| && |first_line_x| == 516 && |second_line_x| == 272
assert: |first_line_y| != |second_line_y| && |first_line_y| == 688 && |second_line_y| == 711
// Now we ensure that they're not rendered on the same line.
set-window-size: (1100, 800)
go-to: "file://" + |DOC_PATH| + "/lib2/trait.Trait.html"
// We first ensure that there are two item info on the trait.
assert-count: ("#main-content > .item-info .stab", 2)

View file

@ -6,6 +6,13 @@ edition = "2018"
[lib]
path = "lib.rs"
[features]
Win32 = ["Win32_System"]
Win32_System = ["Win32_System_Diagnostics"]
Win32_System_Diagnostics = ["Win32_System_Diagnostics_Debug"]
Win32_System_Diagnostics_Debug = []
default = ["Win32"]
[dependencies]
implementors = { path = "./implementors" }
http = { path = "./http" }

View file

@ -1,6 +1,7 @@
// ignore-tidy-linelength
#![feature(doc_cfg)]
#![feature(doc_auto_cfg)]
pub mod another_folder;
pub mod another_mod;
@ -28,6 +29,14 @@ impl Foo {
/// Some documentation
/// # A Heading
pub fn a_method(&self) {}
#[cfg(all(
feature = "Win32",
feature = "Win32_System",
feature = "Win32_System_Diagnostics",
feature = "Win32_System_Diagnostics_Debug"
))]
pub fn lot_of_features() {}
}
#[doc(cfg(feature = "foo-method"))]