Auto merge of #147037 - matthiaskrgr:rollup-xtgqzuu, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - rust-lang/rust#116882 (rustdoc: hide `#[repr]` if it isn't part of the public ABI)
 - rust-lang/rust#135771 ([rustdoc] Add support for associated items in "jump to def" feature)
 - rust-lang/rust#141032 (avoid violating `slice::from_raw_parts` safety contract in `Vec::extract_if`)
 - rust-lang/rust#142401 (Add proper name mangling for pattern types)
 - rust-lang/rust#146293 (feat: non-panicking `Vec::try_remove`)
 - rust-lang/rust#146859 (BTreeMap: Don't leak allocators when initializing nodes)
 - rust-lang/rust#146924 (Add doc for `NonZero*` const creation)
 - rust-lang/rust#146933 (Make `render_example_with_highlighting` return an `impl fmt::Display`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-09-25 20:35:49 +00:00
commit 7ac0330c6d
36 changed files with 848 additions and 536 deletions

View file

@ -262,15 +262,16 @@ impl<'tcx> V0SymbolMangler<'tcx> {
fn print_pat(&mut self, pat: ty::Pattern<'tcx>) -> Result<(), std::fmt::Error> {
Ok(match *pat {
ty::PatternKind::Range { start, end } => {
let consts = [start, end];
for ct in consts {
Ty::new_array_with_const_len(self.tcx, self.tcx.types.unit, ct).print(self)?;
}
self.push("R");
self.print_const(start)?;
self.print_const(end)?;
}
ty::PatternKind::Or(patterns) => {
self.push("O");
for pat in patterns {
self.print_pat(pat)?;
}
self.push("E");
}
})
}
@ -498,12 +499,9 @@ impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
}
ty::Pat(ty, pat) => {
// HACK: Represent as tuple until we have something better.
// HACK: constants are used in arrays, even if the types don't match.
self.push("T");
self.push("W");
ty.print(self)?;
self.print_pat(pat)?;
self.push("E");
}
ty::Array(ty, len) => {

View file

@ -194,6 +194,9 @@ pub struct BTreeMap<
root: Option<Root<K, V>>,
length: usize,
/// `ManuallyDrop` to control drop order (needs to be dropped after all the nodes).
// Although some of the accessory types store a copy of the allocator, the nodes do not.
// Because allocations will remain live as long as any copy (like this one) of the allocator
// is live, it's unnecessary to store the allocator in each node.
pub(super) alloc: ManuallyDrop<A>,
// For dropck; the `Box` avoids making the `Unpin` impl more strict than before
_marker: PhantomData<crate::boxed::Box<(K, V), A>>,

View file

@ -225,7 +225,11 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Leaf> {
}
fn from_new_leaf<A: Allocator + Clone>(leaf: Box<LeafNode<K, V>, A>) -> Self {
NodeRef { height: 0, node: NonNull::from(Box::leak(leaf)), _marker: PhantomData }
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (leaf, _alloc) = Box::into_raw_with_allocator(leaf);
// SAFETY: the node was just allocated.
let node = unsafe { NonNull::new_unchecked(leaf) };
NodeRef { height: 0, node, _marker: PhantomData }
}
}
@ -243,7 +247,11 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
height: usize,
) -> Self {
debug_assert!(height > 0);
let node = NonNull::from(Box::leak(internal)).cast();
// The allocator must be dropped, not leaked. See also `BTreeMap::alloc`.
let (internal, _alloc) = Box::into_raw_with_allocator(internal);
// SAFETY: the node was just allocated.
let internal = unsafe { NonNull::new_unchecked(internal) };
let node = internal.cast();
let mut this = NodeRef { height, node, _marker: PhantomData };
this.borrow_mut().correct_all_childrens_parent_links();
this

View file

@ -64,27 +64,37 @@ where
type Item = T;
fn next(&mut self) -> Option<T> {
unsafe {
while self.idx < self.end {
let i = self.idx;
let v = slice::from_raw_parts_mut(self.vec.as_mut_ptr(), self.old_len);
let drained = (self.pred)(&mut v[i]);
// Update the index *after* the predicate is called. If the index
// is updated prior and the predicate panics, the element at this
// index would be leaked.
self.idx += 1;
if drained {
self.del += 1;
return Some(ptr::read(&v[i]));
} else if self.del > 0 {
let del = self.del;
let src: *const T = &v[i];
let dst: *mut T = &mut v[i - del];
ptr::copy_nonoverlapping(src, dst, 1);
while self.idx < self.end {
let i = self.idx;
// 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`.
//
// Additionally, the i-th element is valid because each element is visited at most once
// and it is the first time we access vec[i].
//
// Note: we can't use `vec.get_unchecked_mut(i)` here since the precondition for that
// function is that i < vec.len(), but we've set vec's length to zero.
let cur = unsafe { &mut *self.vec.as_mut_ptr().add(i) };
let drained = (self.pred)(cur);
// Update the index *after* the predicate is called. If the index
// is updated prior and the predicate panics, the element at this
// index would be leaked.
self.idx += 1;
if drained {
self.del += 1;
// SAFETY: We never touch this element again after returning it.
return Some(unsafe { ptr::read(cur) });
} else if self.del > 0 {
// SAFETY: `self.del` > 0, so the hole slot must not overlap with current element.
// We use copy for move, and never touch this element again.
unsafe {
let hole_slot = self.vec.as_mut_ptr().add(i - self.del);
ptr::copy_nonoverlapping(cur, hole_slot, 1);
}
}
None
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
@ -95,14 +105,18 @@ where
#[stable(feature = "extract_if", since = "1.87.0")]
impl<T, F, A: Allocator> Drop for ExtractIf<'_, T, F, A> {
fn drop(&mut self) {
unsafe {
if self.idx < self.old_len && self.del > 0 {
let ptr = self.vec.as_mut_ptr();
let src = ptr.add(self.idx);
let dst = src.sub(self.del);
let tail_len = self.old_len - self.idx;
src.copy_to(dst, tail_len);
if self.del > 0 {
// SAFETY: Trailing unchecked items must be valid since we never touch them.
unsafe {
ptr::copy(
self.vec.as_ptr().add(self.idx),
self.vec.as_mut_ptr().add(self.idx - self.del),
self.old_len - self.idx,
);
}
}
// SAFETY: After filling holes, all items are in contiguous memory.
unsafe {
self.vec.set_len(self.old_len - self.del);
}
}

View file

@ -2173,9 +2173,37 @@ impl<T, A: Allocator> Vec<T, A> {
panic!("removal index (is {index}) should be < len (is {len})");
}
match self.try_remove(index) {
Some(elem) => elem,
None => assert_failed(index, self.len()),
}
}
/// Remove and return the element at position `index` within the vector,
/// shifting all elements after it to the left, or [`None`] if it does not
/// exist.
///
/// Note: Because this shifts over the remaining elements, it has a
/// worst-case performance of *O*(*n*). If you'd like to remove
/// elements from the beginning of the `Vec`, consider using
/// [`VecDeque::pop_front`] instead.
///
/// [`VecDeque::pop_front`]: crate::collections::VecDeque::pop_front
///
/// # Examples
///
/// ```
/// #![feature(vec_try_remove)]
/// let mut v = vec![1, 2, 3];
/// assert_eq!(v.try_remove(0), Some(1));
/// assert_eq!(v.try_remove(2), None);
/// ```
#[unstable(feature = "vec_try_remove", issue = "146954")]
#[rustc_confusables("delete", "take", "remove")]
pub fn try_remove(&mut self, index: usize) -> Option<T> {
let len = self.len();
if index >= len {
assert_failed(index, len);
return None;
}
unsafe {
// infallible
@ -2191,7 +2219,7 @@ impl<T, A: Allocator> Vec<T, A> {
ptr::copy(ptr.add(1), ptr, len - index - 1);
}
self.set_len(len - 1);
ret
Some(ret)
}
}

View file

@ -41,6 +41,7 @@
#![feature(unique_rc_arc)]
#![feature(macro_metavar_expr_concat)]
#![feature(vec_peek_mut)]
#![feature(vec_try_remove)]
#![allow(internal_features)]
#![deny(fuzzy_provenance_casts)]
#![deny(unsafe_op_in_unsafe_fn)]

View file

@ -630,6 +630,21 @@ fn test_swap_remove_empty() {
vec.swap_remove(0);
}
#[test]
fn test_try_remove() {
let mut vec = vec![1, 2, 3];
// We are attempting to remove vec[0] which contains 1
assert_eq!(vec.try_remove(0), Some(1));
// Now `vec` looks like: [2, 3]
// We will now try to remove vec[2] which does not exist
// This should return `None`
assert_eq!(vec.try_remove(2), None);
// We will try the same thing with an empty vector
let mut v: Vec<u8> = vec![];
assert!(v.try_remove(0).is_none());
}
#[test]
fn test_move_items() {
let vec = vec![1, 2, 3];

View file

@ -548,6 +548,18 @@ macro_rules! nonzero_integer {
#[doc = concat!("assert_eq!(align_of::<", stringify!($Ty), ">(), align_of::<Option<", stringify!($Ty), ">>());")]
/// ```
///
/// # Compile-time creation
///
/// Since both [`Option::unwrap()`] and [`Option::expect()`] are `const`, it is possible to
/// define a new
#[doc = concat!("`", stringify!($Ty), "`")]
/// at compile time via:
/// ```
#[doc = concat!("use std::num::", stringify!($Ty), ";")]
///
#[doc = concat!("const TEN: ", stringify!($Ty), " = ", stringify!($Ty) , r#"::new(10).expect("ten is non-zero");"#)]
/// ```
///
/// [null pointer optimization]: crate::option#representation
#[$stability]
pub type $Ty = NonZero<$Int>;

View file

@ -710,6 +710,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
[mut-ptr-type]: #mut-ptr-type
[fn-type]: #fn-type
[dyn-trait-type]: #dyn-trait-type
[pattern-type]: #pattern-type
> type → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[basic-type]* \
@ -722,6 +723,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
> &nbsp;&nbsp; | *[mut-ptr-type]* \
> &nbsp;&nbsp; | *[fn-type]* \
> &nbsp;&nbsp; | *[dyn-trait-type]* \
> &nbsp;&nbsp; | *[pattern-type]* \
> &nbsp;&nbsp; | *[path]* \
> &nbsp;&nbsp; | *[backref]*
@ -830,6 +832,23 @@ Remaining primitives are encoded as a crate production, e.g. `C4f128`.
[fn-sig]: #fn-sig
[abi]: #abi
* `W` — A [pattern-type][pattern-tpye] `u32 is 0..100`.
> <span id="pattern-type">pattern-type</span>`W` *[pattern-kind]*
>
> <span id="pattern-kind">pattern-kind</span> → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[range-pattern-kind]* \
> &nbsp;&nbsp; *[or-pattern-kind]*
>
> <span id="range-pattern-kind">range-pattern-kind</span>`R` *[const]* *[const]*
>
> <span id="or-pattern-kind">or-pattern-kind</span>`O` *[pattern-kind]* `E`
While or patterns can be nested in theory, in practice this does not happen and they are instead flattened.
Range patterns have a start and end constant that are both included in the range.
The end must be larger than the start (there can be no wraparound). To emulate wraparound,
you need to use an or pattern of the two ranges to the upper limit and from the lower limit.
* `D` — A [trait object][reference-trait-object] `dyn Trait<Assoc=X> + Send + 'a`.
> <span id="dyn-trait-type">dyn-trait-type</span>`D` *[dyn-bounds]* *[lifetime]*
@ -1139,6 +1158,7 @@ The following is a summary of all of the productions of the symbol grammar.
> &nbsp;&nbsp; | *[mut-ptr-type]* \
> &nbsp;&nbsp; | *[fn-type]* \
> &nbsp;&nbsp; | *[dyn-trait-type]* \
> &nbsp;&nbsp; | *[pattern-type]* \
> &nbsp;&nbsp; | *[path]* \
> &nbsp;&nbsp; | *[backref]*
>
@ -1152,6 +1172,14 @@ The following is a summary of all of the productions of the symbol grammar.
> [mut-ptr-type] → `O` *[type]* \
> [fn-type] → `F` *[fn-sig]* \
> [dyn-trait-type] → `D` *[dyn-bounds]* *[lifetime]*
> [pattern-type] → `W` *[pattern-kind]*
>
> [pattern-kind] → \
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *[range-pattern-kind]* \
> &nbsp;&nbsp; *[or-pattern-kind]*
>
> [range-pattern-kind] -> `R` *[const]* *[const]* \
> [or-pattern-kind] -> `O` *[pattern-kind]* `E` \
>
> [namespace] → *[lower]* | *[upper]*
>

View file

@ -89,20 +89,30 @@ https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true
This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL
to automatically go to the first result.
## `#[repr(transparent)]`: Documenting the transparent representation
## `#[repr(...)]`: Documenting the representation of a type
Generally, rustdoc only displays the representation of a given type if none of its variants are
`#[doc(hidden)]` and if all of its fields are public and not `#[doc(hidden)]` since it's likely
not meant to be considered part of the public ABI otherwise.
Note that there's no way to overwrite that heuristic and force rustdoc to show the representation
regardless.
### `#[repr(transparent)]`
You can read more about `#[repr(transparent)]` itself in the [Rust Reference][repr-trans-ref] and
in the [Rustonomicon][repr-trans-nomicon].
Since this representation is only considered part of the public ABI if the single field with non-trivial
size or alignment is public and if the documentation does not state otherwise, Rustdoc helpfully displays
the attribute if and only if the non-1-ZST field is public or at least one field is public in case all
fields are 1-ZST fields. The term *1-ZST* refers to types that are one-aligned and zero-sized.
size or alignment is public and if the documentation does not state otherwise, rustdoc helpfully displays
the attribute if and only if the non-1-ZST field is public and not `#[doc(hidden)]` or
in case all fields are 1-ZST fields — at least one field is public and not `#[doc(hidden)]`.
The term *1-ZST* refers to types that are one-aligned and zero-sized.
It would seem that one can manually hide the attribute with `#[cfg_attr(not(doc), repr(transparent))]`
if one wishes to declare the representation as private even if the non-1-ZST field is public.
However, due to [current limitations][cross-crate-cfg-doc], this method is not always guaranteed to work.
Therefore, if you would like to do so, you should always write it down in prose independently of whether
Therefore, if you would like to do so, you should always write that down in prose independently of whether
you use `cfg_attr` or not.
[repr-trans-ref]: https://doc.rust-lang.org/reference/type-layout.html#the-transparent-representation

View file

@ -794,50 +794,6 @@ impl Item {
Some(tcx.visibility(def_id))
}
/// Get a list of attributes excluding `#[repr]` to display.
///
/// Only used by the HTML output-format.
fn attributes_without_repr(&self) -> Vec<String> {
self.attrs
.other_attrs
.iter()
.filter_map(|attr| match attr {
hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => {
Some(format!("#[unsafe(link_section = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => {
Some("#[unsafe(no_mangle)]".to_string())
}
hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => {
Some(format!("#[unsafe(export_name = \"{name}\")]"))
}
hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => {
Some("#[non_exhaustive]".to_string())
}
_ => None,
})
.collect()
}
/// Get a list of attributes to display on this item.
///
/// Only used by the HTML output-format.
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Vec<String> {
let mut attrs = self.attributes_without_repr();
if let Some(repr_attr) = self.repr(tcx, cache) {
attrs.push(repr_attr);
}
attrs
}
/// Returns a stringified `#[repr(...)]` attribute.
///
/// Only used by the HTML output-format.
pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Option<String> {
repr_attributes(tcx, cache, self.def_id()?, self.type_())
}
pub fn is_doc_hidden(&self) -> bool {
self.attrs.is_doc_hidden()
}
@ -847,74 +803,6 @@ impl Item {
}
}
/// Return a string representing the `#[repr]` attribute if present.
///
/// Only used by the HTML output-format.
pub(crate) fn repr_attributes(
tcx: TyCtxt<'_>,
cache: &Cache,
def_id: DefId,
item_type: ItemType,
) -> Option<String> {
use rustc_abi::IntegerType;
if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) {
return None;
}
let adt = tcx.adt_def(def_id);
let repr = adt.repr();
let mut out = Vec::new();
if repr.c() {
out.push("C");
}
if repr.transparent() {
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
// field is public in case all fields are 1-ZST fields.
let render_transparent = cache.document_private
|| adt
.all_fields()
.find(|field| {
let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty))
.is_ok_and(|layout| !layout.is_1zst())
})
.map_or_else(
|| adt.all_fields().any(|field| field.vis.is_public()),
|field| field.vis.is_public(),
);
if render_transparent {
out.push("transparent");
}
}
if repr.simd() {
out.push("simd");
}
let pack_s;
if let Some(pack) = repr.pack {
pack_s = format!("packed({})", pack.bytes());
out.push(&pack_s);
}
let align_s;
if let Some(align) = repr.align {
align_s = format!("align({})", align.bytes());
out.push(&align_s);
}
let int_s;
if let Some(int) = repr.int {
int_s = match int {
IntegerType::Pointer(is_signed) => {
format!("{}size", if is_signed { 'i' } else { 'u' })
}
IntegerType::Fixed(size, is_signed) => {
format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8)
}
};
out.push(&int_s);
}
if !out.is_empty() { Some(format!("#[repr({})]", out.join(", "))) } else { None }
}
#[derive(Clone, Debug)]
pub(crate) enum ItemKind {
ExternCrateItem {

View file

@ -8,6 +8,7 @@
use std::borrow::Cow;
use std::collections::VecDeque;
use std::fmt::{self, Display, Write};
use std::iter;
use rustc_data_structures::fx::FxIndexMap;
use rustc_lexer::{Cursor, FrontmatterAllowed, LiteralKind, TokenKind};
@ -15,8 +16,9 @@ use rustc_span::edition::Edition;
use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, DUMMY_SP, Span};
use super::format::{self, write_str};
use super::format;
use crate::clean::PrimitiveType;
use crate::display::Joined as _;
use crate::html::escape::EscapeBodyText;
use crate::html::macro_expansion::ExpandedCode;
use crate::html::render::{Context, LinkFromSrc};
@ -45,92 +47,72 @@ pub(crate) enum Tooltip {
CompileFail,
ShouldPanic,
Edition(Edition),
None,
}
/// Highlights `src` as an inline example, returning the HTML output.
pub(crate) fn render_example_with_highlighting(
src: &str,
out: &mut String,
tooltip: Tooltip,
tooltip: Option<&Tooltip>,
playground_button: Option<&str>,
extra_classes: &[String],
) {
write_header(out, "rust-example-rendered", None, tooltip, extra_classes);
write_code(out, src, None, None, None);
write_footer(out, playground_button);
) -> impl Display {
fmt::from_fn(move |f| {
write_header("rust-example-rendered", tooltip, extra_classes).fmt(f)?;
write_code(f, src, None, None, None);
write_footer(playground_button).fmt(f)
})
}
fn write_header(
out: &mut String,
class: &str,
extra_content: Option<&str>,
tooltip: Tooltip,
extra_classes: &[String],
) {
write_str(
out,
format_args!(
fn write_header(class: &str, tooltip: Option<&Tooltip>, extra_classes: &[String]) -> impl Display {
fmt::from_fn(move |f| {
write!(
f,
"<div class=\"example-wrap{}\">",
match tooltip {
Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore",
Tooltip::CompileFail => " compile_fail",
Tooltip::ShouldPanic => " should_panic",
Tooltip::Edition(_) => " edition",
Tooltip::None => "",
}
),
);
tooltip
.map(|tooltip| match tooltip {
Tooltip::IgnoreAll | Tooltip::IgnoreSome(_) => " ignore",
Tooltip::CompileFail => " compile_fail",
Tooltip::ShouldPanic => " should_panic",
Tooltip::Edition(_) => " edition",
})
.unwrap_or_default()
)?;
if tooltip != Tooltip::None {
let tooltip = fmt::from_fn(|f| match &tooltip {
Tooltip::IgnoreAll => f.write_str("This example is not tested"),
Tooltip::IgnoreSome(platforms) => {
f.write_str("This example is not tested on ")?;
match &platforms[..] {
[] => unreachable!(),
[platform] => f.write_str(platform)?,
[first, second] => write!(f, "{first} or {second}")?,
[platforms @ .., last] => {
for platform in platforms {
write!(f, "{platform}, ")?;
if let Some(tooltip) = tooltip {
let tooltip = fmt::from_fn(|f| match tooltip {
Tooltip::IgnoreAll => f.write_str("This example is not tested"),
Tooltip::IgnoreSome(platforms) => {
f.write_str("This example is not tested on ")?;
match &platforms[..] {
[] => unreachable!(),
[platform] => f.write_str(platform)?,
[first, second] => write!(f, "{first} or {second}")?,
[platforms @ .., last] => {
for platform in platforms {
write!(f, "{platform}, ")?;
}
write!(f, "or {last}")?;
}
write!(f, "or {last}")?;
}
Ok(())
}
Ok(())
}
Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"),
Tooltip::ShouldPanic => f.write_str("This example panics"),
Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"),
Tooltip::None => unreachable!(),
});
write_str(out, format_args!("<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>"));
}
Tooltip::CompileFail => f.write_str("This example deliberately fails to compile"),
Tooltip::ShouldPanic => f.write_str("This example panics"),
Tooltip::Edition(edition) => write!(f, "This example runs with edition {edition}"),
});
if let Some(extra) = extra_content {
out.push_str(extra);
}
if class.is_empty() {
write_str(
out,
format_args!(
"<pre class=\"rust{}{}\">",
if extra_classes.is_empty() { "" } else { " " },
extra_classes.join(" ")
),
);
} else {
write_str(
out,
format_args!(
"<pre class=\"rust {class}{}{}\">",
if extra_classes.is_empty() { "" } else { " " },
extra_classes.join(" ")
),
);
}
write_str(out, format_args!("<code>"));
write!(f, "<a href=\"#\" class=\"tooltip\" title=\"{tooltip}\">ⓘ</a>")?;
}
let classes = fmt::from_fn(|f| {
iter::once("rust")
.chain(Some(class).filter(|class| !class.is_empty()))
.chain(extra_classes.iter().map(String::as_str))
.joined(" ", f)
});
write!(f, "<pre class=\"{classes}\"><code>")
})
}
/// Check if two `Class` can be merged together. In the following rules, "unclassified" means `None`
@ -577,8 +559,8 @@ pub(super) fn write_code(
});
}
fn write_footer(out: &mut String, playground_button: Option<&str>) {
write_str(out, format_args!("</code></pre>{}</div>", playground_button.unwrap_or_default()));
fn write_footer(playground_button: Option<&str>) -> impl Display {
fmt::from_fn(move |f| write!(f, "</code></pre>{}</div>", playground_button.unwrap_or_default()))
}
/// How a span of text is classified. Mostly corresponds to token kinds.
@ -1262,6 +1244,64 @@ fn string<W: Write>(
}
}
fn generate_link_to_def(
out: &mut impl Write,
text_s: &str,
klass: Class,
href_context: &Option<HrefContext<'_, '_>>,
def_span: Span,
open_tag: bool,
) -> bool {
if let Some(href_context) = href_context
&& let Some(href) =
href_context.context.shared.span_correspondence_map.get(&def_span).and_then(|href| {
let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition.
// FIXME: currently, external items only generate a link to their documentation,
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => {
context.href_from_span_relative(*span, &href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(url, _, _)| url)
}
LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim],
context,
Some(href_context.root_path),
)
.ok()
.map(|(url, _, _)| url),
LinkFromSrc::Doc(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(doc_link, _, _)| doc_link)
}
}
})
{
if !open_tag {
// We're already inside an element which has the same klass, no need to give it
// again.
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
let klass_s = klass.as_html();
if klass_s.is_empty() {
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
write!(out, "<a class=\"{klass_s}\" href=\"{href}\">{text_s}").unwrap();
}
}
return true;
}
false
}
/// This function writes `text` into `out` with some modifications depending on `klass`:
///
/// * If `klass` is `None`, `text` is written into `out` with no modification.
@ -1291,10 +1331,14 @@ fn string_without_closing_tag<T: Display>(
return Some("</span>");
};
let mut added_links = false;
let mut text_s = text.to_string();
if text_s.contains("::") {
let mut span = def_span.with_hi(def_span.lo());
text_s = text_s.split("::").intersperse("::").fold(String::new(), |mut path, t| {
span = span.with_hi(span.hi() + BytePos(t.len() as _));
match t {
"::" => write!(&mut path, "::"),
"self" | "Self" => write!(
&mut path,
"<span class=\"{klass}\">{t}</span>",
@ -1307,58 +1351,24 @@ fn string_without_closing_tag<T: Display>(
klass = Class::KeyWord.as_html(),
)
}
t => write!(&mut path, "{t}"),
t => {
if !t.is_empty()
&& generate_link_to_def(&mut path, t, klass, href_context, span, open_tag)
{
added_links = true;
write!(&mut path, "</a>")
} else {
write!(&mut path, "{t}")
}
}
}
.expect("Failed to build source HTML path");
span = span.with_lo(span.lo() + BytePos(t.len() as _));
path
});
}
if let Some(href_context) = href_context
&& let Some(href) = href_context.context.shared.span_correspondence_map.get(&def_span)
&& let Some(href) = {
let context = href_context.context;
// FIXME: later on, it'd be nice to provide two links (if possible) for all items:
// one to the documentation page and one to the source definition.
// FIXME: currently, external items only generate a link to their documentation,
// a link to their definition can be generated using this:
// https://github.com/rust-lang/rust/blob/60f1a2fc4b535ead9c85ce085fdce49b1b097531/src/librustdoc/html/render/context.rs#L315-L338
match href {
LinkFromSrc::Local(span) => {
context.href_from_span_relative(*span, &href_context.current_href)
}
LinkFromSrc::External(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(url, _, _)| url)
}
LinkFromSrc::Primitive(prim) => format::href_with_root_path(
PrimitiveType::primitive_locations(context.tcx())[prim],
context,
Some(href_context.root_path),
)
.ok()
.map(|(url, _, _)| url),
LinkFromSrc::Doc(def_id) => {
format::href_with_root_path(*def_id, context, Some(href_context.root_path))
.ok()
.map(|(doc_link, _, _)| doc_link)
}
}
}
{
if !open_tag {
// We're already inside an element which has the same klass, no need to give it
// again.
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
let klass_s = klass.as_html();
if klass_s.is_empty() {
write!(out, "<a href=\"{href}\">{text_s}").unwrap();
} else {
write!(out, "<a class=\"{klass_s}\" href=\"{href}\">{text_s}").unwrap();
}
}
if !added_links && generate_link_to_def(out, &text_s, klass, href_context, def_span, open_tag) {
return Some("</a>");
}
if !open_tag {

View file

@ -321,31 +321,34 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
))
});
let tooltip = if ignore == Ignore::All {
highlight::Tooltip::IgnoreAll
} else if let Ignore::Some(platforms) = ignore {
highlight::Tooltip::IgnoreSome(platforms)
} else if compile_fail {
highlight::Tooltip::CompileFail
} else if should_panic {
highlight::Tooltip::ShouldPanic
} else if explicit_edition {
highlight::Tooltip::Edition(edition)
} else {
highlight::Tooltip::None
let tooltip = {
use highlight::Tooltip::*;
if ignore == Ignore::All {
Some(IgnoreAll)
} else if let Ignore::Some(platforms) = ignore {
Some(IgnoreSome(platforms))
} else if compile_fail {
Some(CompileFail)
} else if should_panic {
Some(ShouldPanic)
} else if explicit_edition {
Some(Edition(edition))
} else {
None
}
};
// insert newline to clearly separate it from the
// previous block so we can shorten the html output
let mut s = String::new();
s.push('\n');
highlight::render_example_with_highlighting(
&text,
&mut s,
tooltip,
playground_button.as_deref(),
&added_classes,
let s = format!(
"\n{}",
highlight::render_example_with_highlighting(
&text,
tooltip.as_ref(),
playground_button.as_deref(),
&added_classes,
)
);
Some(Event::Html(s.into()))
}

View file

@ -51,7 +51,9 @@ use askama::Template;
use itertools::Either;
use rustc_ast::join_path_syms;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
use rustc_hir as hir;
use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::{ConstStability, Mutability, RustcVersion, StabilityLevel, StableSince};
use rustc_middle::ty::print::PrintTraitRefExt;
@ -1310,43 +1312,6 @@ fn render_assoc_item(
})
}
struct CodeAttribute(String);
fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) {
write!(
w,
"<div class=\"code-attribute\">{prefix}{attr}</div>",
prefix = prefix,
attr = code_attr.0
)
.unwrap();
}
// When an attribute is rendered inside a <code> tag, it is formatted using
// a div to produce a newline after it.
fn render_attributes_in_code(
w: &mut impl fmt::Write,
it: &clean::Item,
prefix: &str,
cx: &Context<'_>,
) {
for attr in it.attributes(cx.tcx(), cx.cache()) {
render_code_attribute(prefix, CodeAttribute(attr), w);
}
}
/// used for type aliases to only render their `repr` attribute.
fn render_repr_attributes_in_code(
w: &mut impl fmt::Write,
cx: &Context<'_>,
def_id: DefId,
item_type: ItemType,
) {
if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) {
render_code_attribute("", CodeAttribute(repr), w);
}
}
#[derive(Copy, Clone)]
enum AssocItemLink<'a> {
Anchor(Option<&'a str>),
@ -2959,3 +2924,142 @@ fn render_call_locations<W: fmt::Write>(
w.write_str("</div>")
}
fn render_attributes_in_code(
w: &mut impl fmt::Write,
item: &clean::Item,
prefix: &str,
cx: &Context<'_>,
) {
for attr in &item.attrs.other_attrs {
let hir::Attribute::Parsed(kind) = attr else { continue };
let attr = match kind {
AttributeKind::LinkSection { name, .. } => {
Cow::Owned(format!("#[unsafe(link_section = {})]", Escape(&format!("{name:?}"))))
}
AttributeKind::NoMangle(..) => Cow::Borrowed("#[unsafe(no_mangle)]"),
AttributeKind::ExportName { name, .. } => {
Cow::Owned(format!("#[unsafe(export_name = {})]", Escape(&format!("{name:?}"))))
}
AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
_ => continue,
};
render_code_attribute(prefix, attr.as_ref(), w);
}
if let Some(def_id) = item.def_id()
&& let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id)
{
render_code_attribute(prefix, &repr, w);
}
}
fn render_repr_attribute_in_code(w: &mut impl fmt::Write, cx: &Context<'_>, def_id: DefId) {
if let Some(repr) = repr_attribute(cx.tcx(), cx.cache(), def_id) {
render_code_attribute("", &repr, w);
}
}
fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) {
write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>").unwrap();
}
/// Compute the *public* `#[repr]` of the item given by `DefId`.
///
/// Read more about it here:
/// <https://doc.rust-lang.org/nightly/rustdoc/advanced-features.html#repr-documenting-the-representation-of-a-type>.
fn repr_attribute<'tcx>(
tcx: TyCtxt<'tcx>,
cache: &Cache,
def_id: DefId,
) -> Option<Cow<'static, str>> {
let adt = match tcx.def_kind(def_id) {
DefKind::Struct | DefKind::Enum | DefKind::Union => tcx.adt_def(def_id),
_ => return None,
};
let repr = adt.repr();
let is_visible = |def_id| cache.document_hidden || !tcx.is_doc_hidden(def_id);
let is_public_field = |field: &ty::FieldDef| {
(cache.document_private || field.vis.is_public()) && is_visible(field.did)
};
if repr.transparent() {
// The transparent repr is public iff the non-1-ZST field is public and visible or
// in case all fields are 1-ZST fields — at least one field is public and visible.
let is_public = 'is_public: {
// `#[repr(transparent)]` can only be applied to structs and single-variant enums.
let var = adt.variant(rustc_abi::FIRST_VARIANT); // the first and only variant
if !is_visible(var.def_id) {
break 'is_public false;
}
// Side note: There can only ever be one or zero non-1-ZST fields.
let non_1zst_field = var.fields.iter().find(|field| {
let ty = ty::TypingEnv::post_analysis(tcx, field.did)
.as_query_input(tcx.type_of(field.did).instantiate_identity());
tcx.layout_of(ty).is_ok_and(|layout| !layout.is_1zst())
});
match non_1zst_field {
Some(field) => is_public_field(field),
None => var.fields.is_empty() || var.fields.iter().any(is_public_field),
}
};
// Since the transparent repr can't have any other reprs or
// repr modifiers beside it, we can safely return early here.
return is_public.then(|| "#[repr(transparent)]".into());
}
// Fast path which avoids looking through the variants and fields in
// the common case of no `#[repr]` or in the case of `#[repr(Rust)]`.
// FIXME: This check is not very robust / forward compatible!
if !repr.c()
&& !repr.simd()
&& repr.int.is_none()
&& repr.pack.is_none()
&& repr.align.is_none()
{
return None;
}
// The repr is public iff all components are public and visible.
let is_public = adt
.variants()
.iter()
.all(|variant| is_visible(variant.def_id) && variant.fields.iter().all(is_public_field));
if !is_public {
return None;
}
let mut result = Vec::<Cow<'_, _>>::new();
if repr.c() {
result.push("C".into());
}
if repr.simd() {
result.push("simd".into());
}
if let Some(int) = repr.int {
let prefix = if int.is_signed() { 'i' } else { 'u' };
let int = match int {
rustc_abi::IntegerType::Pointer(_) => format!("{prefix}size"),
rustc_abi::IntegerType::Fixed(int, _) => {
format!("{prefix}{}", int.size().bytes() * 8)
}
};
result.push(int.into());
}
// Render modifiers last.
if let Some(pack) = repr.pack {
result.push(format!("packed({})", pack.bytes()).into());
}
if let Some(align) = repr.align {
result.push(format!("align({})", align.bytes()).into());
}
(!result.is_empty()).then(|| format!("#[repr({})]", result.join(", ")).into())
}

View file

@ -21,7 +21,7 @@ use super::{
collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl,
render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
render_repr_attribute_in_code, render_rightside, render_stability_since_raw,
render_stability_since_raw_with_extra, write_section_heading,
};
use crate::clean;
@ -1555,7 +1555,7 @@ impl<'clean> DisplayEnum<'clean> {
wrap_item(w, |w| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum);
render_repr_attribute_in_code(w, cx, self.def_id);
} else {
render_attributes_in_code(w, it, "", cx);
}
@ -2017,7 +2017,7 @@ impl<'a> DisplayStruct<'a> {
wrap_item(w, |w| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct);
render_repr_attribute_in_code(w, cx, self.def_id);
} else {
render_attributes_in_code(w, it, "", cx);
}
@ -2371,7 +2371,7 @@ fn render_union(
fmt::from_fn(move |mut f| {
if is_type_alias {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(f, cx, def_id, ItemType::Union);
render_repr_attribute_in_code(f, cx, def_id);
} else {
render_attributes_in_code(f, it, "", cx);
}

View file

@ -2,11 +2,9 @@ use std::path::{Path, PathBuf};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{
ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath,
};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::hygiene::MacroKind;
@ -67,7 +65,7 @@ struct SpanMapVisitor<'tcx> {
impl SpanMapVisitor<'_> {
/// This function is where we handle `hir::Path` elements and add them into the "span map".
fn handle_path(&mut self, path: &rustc_hir::Path<'_>) {
fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) {
match path.res {
// FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
// Would be nice to support them too alongside the other `DefKind`
@ -79,24 +77,36 @@ impl SpanMapVisitor<'_> {
LinkFromSrc::External(def_id)
};
// In case the path ends with generics, we remove them from the span.
let span = path
.segments
.last()
.map(|last| {
// In `use` statements, the included item is not in the path segments.
// However, it doesn't matter because you can't have generics on `use`
// statements.
if path.span.contains(last.ident.span) {
path.span.with_hi(last.ident.span.hi())
} else {
path.span
}
})
.unwrap_or(path.span);
let span = if only_use_last_segment
&& let Some(path_span) = path.segments.last().map(|segment| segment.ident.span)
{
path_span
} else {
path.segments
.last()
.map(|last| {
// In `use` statements, the included item is not in the path segments.
// However, it doesn't matter because you can't have generics on `use`
// statements.
if path.span.contains(last.ident.span) {
path.span.with_hi(last.ident.span.hi())
} else {
path.span
}
})
.unwrap_or(path.span)
};
self.matches.insert(span, link);
}
Res::Local(_) if let Some(span) = self.tcx.hir_res_span(path.res) => {
self.matches.insert(path.span, LinkFromSrc::Local(clean::Span::new(span)));
let path_span = if only_use_last_segment
&& let Some(path_span) = path.segments.last().map(|segment| segment.ident.span)
{
path_span
} else {
path.span
};
self.matches.insert(path_span, LinkFromSrc::Local(clean::Span::new(span)));
}
Res::PrimTy(p) => {
// FIXME: Doesn't handle "path-like" primitives like arrays or tuples.
@ -189,31 +199,23 @@ impl SpanMapVisitor<'_> {
self.matches.insert(span, link);
}
}
}
fn handle_pat(&mut self, p: &Pat<'_>) {
let mut check_qpath = |qpath, hir_id| match qpath {
QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => {
self.infer_id(path.hir_id, Some(hir_id), qpath.span());
}
QPath::Resolved(_, path) => self.handle_path(path),
_ => {}
};
match p.kind {
PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p),
PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => {
check_qpath(qpath, p.hir_id)
}
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => {
check_qpath(*qpath, *hir_id)
}
PatKind::Or(pats) => {
for pat in pats {
self.handle_pat(pat);
}
}
_ => {}
// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without
// panicking.
fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<LocalDefId> {
for (_, node) in tcx.hir_parent_iter(hir_id) {
// FIXME: associated type impl items don't have an associated body, so we don't handle
// them currently.
if let Node::ImplItem(impl_item) = node
&& matches!(impl_item.kind, rustc_hir::ImplItemKind::Type(_))
{
return None;
} else if let Some((def_id, _)) = node.associated_body() {
return Some(def_id);
}
}
None
}
impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
@ -227,12 +229,42 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
if self.handle_macro(path.span) {
return;
}
self.handle_path(path);
self.handle_path(path, false);
intravisit::walk_path(self, path);
}
fn visit_pat(&mut self, p: &Pat<'tcx>) {
self.handle_pat(p);
fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: Span) {
match *qpath {
QPath::TypeRelative(qself, path) => {
if matches!(path.res, Res::Err) {
let tcx = self.tcx;
if let Some(body_id) = hir_enclosing_body_owner(tcx, id) {
let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id());
let path = rustc_hir::Path {
// We change the span to not include parens.
span: path.ident.span,
res: typeck_results.qpath_res(qpath, id),
segments: &[],
};
self.handle_path(&path, false);
}
} else {
self.infer_id(path.hir_id, Some(id), path.ident.span);
}
rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself));
self.visit_path_segment(path);
}
QPath::Resolved(maybe_qself, path) => {
self.handle_path(path, true);
rustc_ast::visit::visit_opt!(self, visit_ty_unambig, maybe_qself);
if !self.handle_macro(path.span) {
intravisit::walk_path(self, path);
}
}
_ => {}
}
}
fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {

View file

@ -169,6 +169,15 @@ fn miri_issue_2759() {
input.replace_range(0..0, "0");
}
/// This was skirting the edge of UB, let's make sure it remains on the sound side.
/// Context: <https://github.com/rust-lang/rust/pull/141032>.
fn extract_if() {
let mut v = vec![Box::new(0u64), Box::new(1u64)];
for item in v.extract_if(.., |x| **x == 0) {
drop(item);
}
}
fn main() {
assert_eq!(vec_reallocate().len(), 5);
@ -199,4 +208,5 @@ fn main() {
swap_remove();
reverse();
miri_issue_2759();
extract_if();
}

View file

@ -16,7 +16,7 @@ pub fn bar() {
// CHECK: call pattern_type_symbols::foo::<u32>
// CHECK: call void @_RINvC[[CRATE_IDENT:[a-zA-Z0-9]{12}]]_20pattern_type_symbols3foomEB2_
foo::<u32>();
// CHECK: call pattern_type_symbols::foo::<(u32, [(); 0], [(); 999999999])>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooTmAum0_Aum3b9ac9ff_EEB2_
// CHECK: call pattern_type_symbols::foo::<u32 is 0..=999999999>
// CHECK: call void @_RINvC[[CRATE_IDENT]]_20pattern_type_symbols3fooWmRm0_m3b9ac9ff_EB2_
foo::<NanoU32>();
}

View file

@ -459,10 +459,10 @@ pub fn safe_fn() {}
#[repr(C)]
pub struct WithGenerics<T: TraitWithNoDocblocks, S = String, E = WhoLetTheDogOut, P = i8> {
s: S,
t: T,
e: E,
p: P,
pub s: S,
pub t: T,
pub e: E,
pub p: P,
}
pub struct StructWithPublicUndocumentedFields {

View file

@ -1,8 +0,0 @@
#![crate_name = "foo"]
//@ has 'foo/fn.f.html'
//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]'
//@ has - //*[@'class="rust item-decl"]' 'pub fn f()'
#[unsafe(export_name = "\
f")]
pub fn f() {}

View file

@ -9,6 +9,18 @@ pub extern "C" fn f() {}
#[unsafe(export_name = "bar")]
pub extern "C" fn g() {}
//@ has foo/fn.escape_special.html '//*[@class="code-attribute"]' \
// '#[unsafe(export_name = "\n\"\n")]'
#[unsafe(export_name = "\n\"
")]
pub extern "C" fn escape_special() {}
// issue: <https://github.com/rust-lang/rust/issues/142835>
//@ has foo/fn.escape_html.html '//*[@class="code-attribute"]' \
// '#[unsafe(export_name = "<script>alert()</script>")]'
#[unsafe(export_name = "<script>alert()</script>")]
pub extern "C" fn escape_html() {}
//@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]'
#[unsafe(link_section = ".text")]
pub extern "C" fn example() {}

View file

@ -0,0 +1,5 @@
#[repr(i8)]
pub enum ReprI8 {
Var0,
Var1,
}

View file

@ -1,7 +1,20 @@
// Ensure that we render attributes on inlined cross-crate re-exported items.
// issue: <https://github.com/rust-lang/rust/issues/144004>
//@ aux-crate:attributes=attributes.rs
//@ edition:2021
#![crate_name = "user"]
//@ has 'user/struct.NonExhaustive.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[non_exhaustive]'
//@ has 'user/fn.no_mangle.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use attributes::no_mangle;
//@ has 'user/fn.link_section.html' '//pre[@class="rust item-decl"]' \
// '#[unsafe(link_section = ".here")]'
pub use attributes::link_section;
//@ has 'user/fn.export_name.html' '//pre[@class="rust item-decl"]' \
// '#[unsafe(export_name = "exonym")]'
pub use attributes::export_name;
//@ has 'user/struct.NonExhaustive.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]'
pub use attributes::NonExhaustive;

View file

@ -1,2 +1,11 @@
#[unsafe(no_mangle)]
pub fn no_mangle() {}
#[unsafe(link_section = ".here")]
pub fn link_section() {}
#[unsafe(export_name = "exonym")]
pub fn export_name() {}
#[non_exhaustive]
pub struct NonExhaustive;

View file

@ -1,42 +0,0 @@
#![feature(repr_simd)]
#[repr(C, align(8))]
pub struct ReprC {
field: u8,
}
#[repr(simd, packed(2))]
pub struct ReprSimd {
field: [u8; 1],
}
#[repr(transparent)]
pub struct ReprTransparent {
pub field: u8,
}
#[repr(isize)]
pub enum ReprIsize {
Bla,
}
#[repr(u8)]
pub enum ReprU8 {
Bla,
}
#[repr(transparent)] // private
pub struct ReprTransparentPrivField {
field: u32, // non-1-ZST field
}
#[repr(transparent)] // public
pub struct ReprTransparentPriv1ZstFields {
marker0: Marker,
pub main: u64, // non-1-ZST field
marker1: Marker,
}
#[repr(transparent)] // private
pub struct ReprTransparentPrivFieldPub1ZstFields {
main: [u16; 0], // non-1-ZST field
pub marker: Marker,
}
pub struct Marker; // 1-ZST

View file

@ -1,40 +0,0 @@
// Regression test for <https://github.com/rust-lang/rust/issues/110698>.
// This test ensures that the re-exported items still have the `#[repr(...)]` attribute.
//@ aux-build:repr.rs
#![crate_name = "foo"]
extern crate repr;
//@ has 'foo/struct.ReprC.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C, align(8))]'
pub use repr::ReprC;
//@ has 'foo/struct.ReprSimd.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]'
pub use repr::ReprSimd;
//@ has 'foo/struct.ReprTransparent.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparent;
//@ has 'foo/enum.ReprIsize.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]'
pub use repr::ReprIsize;
//@ has 'foo/enum.ReprU8.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u8)]'
pub use repr::ReprU8;
// Regression test for <https://github.com/rust-lang/rust/issues/90435>.
// Check that we show `#[repr(transparent)]` iff the non-1-ZST field is public or at least one
// field is public in case all fields are 1-ZST fields.
//@ has 'foo/struct.ReprTransparentPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPrivField;
//@ has 'foo/struct.ReprTransparentPriv1ZstFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPriv1ZstFields;
//@ has 'foo/struct.ReprTransparentPrivFieldPub1ZstFields.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
pub use repr::ReprTransparentPrivFieldPub1ZstFields;

View file

@ -0,0 +1,54 @@
// This test ensures that patterns also get a link generated.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-assoc-items.rs.html'
pub trait Trait {
type T;
}
pub trait Another {
type T;
const X: u32;
}
pub struct Foo;
impl Foo {
pub fn new() -> Self { Foo }
}
pub struct C;
impl C {
pub fn wat() {}
}
pub struct Bar;
impl Trait for Bar {
type T = Foo;
}
impl Another for Bar {
type T = C;
const X: u32 = 12;
}
pub fn bar() {
//@ has - '//a[@href="#20"]' 'new'
<Bar as Trait>::T::new();
//@ has - '//a[@href="#26"]' 'wat'
<Bar as Another>::T::wat();
match 12u32 {
//@ has - '//a[@href="#14"]' 'X'
<Bar as Another>::X => {}
_ => {}
}
}
pub struct Far {
//@ has - '//a[@href="#10"]' 'T'
x: <Bar as Trait>::T,
}

View file

@ -0,0 +1,20 @@
// This test ensures that associated types don't crash rustdoc jump to def.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-ice-assoc-types.rs.html'
pub trait Trait {
type Node;
}
pub fn y<G: Trait>() {
struct X<G>(G);
impl<G: Trait> Trait for X<G> {
type Node = G::Node;
}
}

View file

@ -0,0 +1,24 @@
// This test ensures that items with no body don't panic when generating
// jump to def links.
//@ compile-flags: -Zunstable-options --generate-link-to-definition
#![crate_name = "foo"]
//@ has 'src/foo/jump-to-def-ice.rs.html'
pub trait A {
type T;
type U;
}
impl A for () {
type T = Self::U;
type U = ();
}
pub trait C {
type X;
}
pub struct F<T: C>(pub T::X);

View file

@ -8,7 +8,7 @@
pub struct Bar;
impl std::default::Default for Bar {
//@ has - '//a[@href="#20-22"]' 'Self::new'
//@ has - '//a[@href="#20-22"]' 'new'
fn default() -> Self {
Self::new()
}
@ -16,7 +16,7 @@ impl std::default::Default for Bar {
//@ has - '//a[@href="#8"]' 'Bar'
impl Bar {
//@ has - '//a[@href="#24-26"]' 'Self::bar'
//@ has - '//a[@href="#24-26"]' 'bar'
pub fn new()-> Self {
Self::bar()
}

View file

@ -30,13 +30,13 @@ pub fn foo() -> Result<(), ()> {
impl<T, E> fmt::Display for MyEnum<T, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
//@ has - '//a[@href="#12"]' 'Self::Ok'
//@ has - '//a[@href="#12"]' 'Ok'
Self::Ok(_) => f.write_str("MyEnum::Ok"),
//@ has - '//a[@href="#13"]' 'MyEnum::Err'
//@ has - '//a[@href="#13"]' 'Err'
MyEnum::Err(_) => f.write_str("MyEnum::Err"),
//@ has - '//a[@href="#14"]' 'Self::Some'
//@ has - '//a[@href="#14"]' 'Some'
Self::Some(_) => f.write_str("MyEnum::Some"),
//@ has - '//a[@href="#15"]' 'Self::None'
//@ has - '//a[@href="#15"]' 'None'
Self::None => f.write_str("MyEnum::None"),
}
}
@ -45,7 +45,7 @@ impl<T, E> fmt::Display for MyEnum<T, E> {
impl X {
fn p(&self) -> &str {
match self {
//@ has - '//a[@href="#19"]' 'Self::A'
//@ has - '//a[@href="#19"]' 'A'
Self::A => "X::A",
}
}

View file

@ -21,9 +21,10 @@ pub fn bar2<T: Read>(readable: T) {
}
pub fn bar() {
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'AtomicIsize::new'
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html"]' 'AtomicIsize'
//@ has - '//a[@href="{{channel}}/core/sync/atomic/struct.AtomicIsize.html#method.new"]' 'new'
let _ = AtomicIsize::new(0);
//@ has - '//a[@href="#48"]' 'local_private'
//@ has - '//a[@href="#49"]' 'local_private'
local_private();
}
@ -39,7 +40,7 @@ pub fn macro_call() -> Result<(), ()> {
}
pub fn variant() {
//@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Ordering::Less'
//@ has - '//a[@href="{{channel}}/core/cmp/enum.Ordering.html#variant.Less"]' 'Less'
let _ = Ordering::Less;
//@ has - '//a[@href="{{channel}}/core/marker/struct.PhantomData.html"]' 'PhantomData'
let _: PhantomData::<usize> = PhantomData;

View file

@ -1,14 +0,0 @@
#[unsafe(no_mangle)]
pub fn f0() {}
#[unsafe(link_section = ".here")]
pub fn f1() {}
#[unsafe(export_name = "f2export")]
pub fn f2() {}
#[repr(u8)]
pub enum T0 { V1 }
#[non_exhaustive]
pub enum T1 {}

View file

@ -1,20 +0,0 @@
//@ aux-build: reexports-attrs.rs
#![crate_name = "foo"]
extern crate reexports_attrs;
//@ has 'foo/fn.f0.html' '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
pub use reexports_attrs::f0;
//@ has 'foo/fn.f1.html' '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".here")]'
pub use reexports_attrs::f1;
//@ has 'foo/fn.f2.html' '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "f2export")]'
pub use reexports_attrs::f2;
//@ has 'foo/enum.T0.html' '//pre[@class="rust item-decl"]' '#[repr(u8)]'
pub use reexports_attrs::T0;
//@ has 'foo/enum.T1.html' '//pre[@class="rust item-decl"]' '#[non_exhaustive]'
pub use reexports_attrs::T1;

View file

@ -1,29 +1,163 @@
// Regression test for <https://github.com/rust-lang/rust/issues/90435>.
// Check that we show `#[repr(transparent)]` iff the non-1-ZST field is public or at least one
// field is public in case all fields are 1-ZST fields.
// Test the rendering of `#[repr]` on ADTs.
#![feature(repr_simd)] // only used for the `ReprSimd` test case
// Check the "local case" (HIR cleaning) //
// Don't render the default repr which is `Rust`.
//@ has 'repr/struct.ReprDefault.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]'
pub struct ReprDefault;
// Don't render the `Rust` repr — even if given explicitly — since it's the default.
//@ has 'repr/struct.ReprRust.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(Rust)]'
#[repr(Rust)] // omitted
pub struct ReprRust;
//@ has 'repr/struct.ReprCPubFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // public
pub struct ReprCPubFields {
pub a: u32,
pub b: u32,
}
//@ has 'repr/struct.ReprCPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // private...
pub struct ReprCPrivField {
a: u32, // ...since this is private
pub b: u32,
}
//@ has 'repr/enum.ReprIsize.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]'
#[repr(isize)] // public
pub enum ReprIsize {
Bla,
}
//@ has 'repr/enum.ReprU32HiddenVariant.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32)]'
#[repr(u32)] // private...
pub enum ReprU32HiddenVariant {
#[doc(hidden)]
Hidden, // ...since this is hidden
Public,
}
//@ has 'repr/struct.ReprAlignHiddenField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(align(4))]'
#[repr(align(4))] // private...
pub struct ReprAlignHiddenField {
#[doc(hidden)]
pub hidden: i16, // ...since this field is hidden
}
//@ has 'repr/struct.ReprSimd.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]'
#[repr(simd, packed(2))] // public
pub struct ReprSimd {
pub field: [u8; 1],
}
//@ has 'repr/enum.ReprU32Align.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u32, align(8))]'
#[repr(u32, align(8))] // public
pub enum ReprU32Align {
Variant(u16),
}
//@ has 'repr/enum.ReprCHiddenVariantField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]'
#[repr(C)] // private...
pub enum ReprCHiddenVariantField {
Variant { #[doc(hidden)] field: () }, //...since this field is hidden
}
//@ has 'repr/struct.ReprTransparentPrivField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private
#[repr(transparent)] // private...
pub struct ReprTransparentPrivField {
field: u32, // non-1-ZST field
field: u32, // ...since the non-1-ZST field is private
}
//@ has 'repr/struct.ReprTransparentPriv1ZstFields.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
#[repr(transparent)] // public...
pub struct ReprTransparentPriv1ZstFields {
marker0: Marker,
pub main: u64, // non-1-ZST field
pub main: u64, // ...since the non-1-ZST field is public and visible
marker1: Marker,
} // the two private 1-ZST fields don't matter
//@ has 'repr/struct.ReprTransparentPrivFieldPub1ZstField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private...
pub struct ReprTransparentPrivFieldPub1ZstField {
main: [u16; 0], // ...since the non-1-ZST field is private
pub marker: Marker, // this public 1-ZST field doesn't matter
}
//@ has 'repr/struct.ReprTransparentPub1ZstField.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
#[repr(transparent)] // public...
pub struct ReprTransparentPub1ZstField {
marker0: Marker,
pub marker1: Marker,
marker0: Marker, // ...since we don't have a non-1-ZST field...
pub marker1: Marker, // ...and this field is public and visible
}
//@ has 'repr/struct.ReprTransparentUnitStruct.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
pub struct ReprTransparentUnitStruct;
//@ has 'repr/enum.ReprTransparentEnumUnitVariant.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public
pub enum ReprTransparentEnumUnitVariant {
Variant,
}
//@ has 'repr/enum.ReprTransparentEnumHiddenUnitVariant.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private
pub enum ReprTransparentEnumHiddenUnitVariant {
#[doc(hidden)] Variant(u32),
}
//@ has 'repr/enum.ReprTransparentEnumPub1ZstField.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // public...
pub enum ReprTransparentEnumPub1ZstField {
Variant {
field: u64, // ...since the non-1-ZST field is public
#[doc(hidden)]
marker: Marker, // this hidden 1-ZST field doesn't matter
},
}
//@ has 'repr/enum.ReprTransparentEnumHidden1ZstField.html'
//@ !has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]'
#[repr(transparent)] // private...
pub enum ReprTransparentEnumHidden1ZstField {
Variant {
#[doc(hidden)]
field: u64, // ...since the non-1-ZST field is public
},
}
struct Marker; // 1-ZST
// Check the "extern case" (middle cleaning) //
// Internally, HIR and middle cleaning share `#[repr]` rendering.
// Thus we'll only test the very basics in this section.
//@ aux-build: ext-repr.rs
extern crate ext_repr as ext;
// Regression test for <https://github.com/rust-lang/rust/issues/110698>.
//@ has 'repr/enum.ReprI8.html'
//@ has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(i8)]'
pub use ext::ReprI8;

View file

@ -34,7 +34,7 @@ fn babar() {}
// The 5 links to line 23 and the line 23 itself.
//@ count - '//pre[@class="rust"]//a[@href="#23"]' 6
//@ has - '//pre[@class="rust"]//a[@href="../../source_code/struct.SourceCode.html"]' \
// 'source_code::SourceCode'
// 'SourceCode'
pub fn foo(a: u32, b: &str, c: String, d: Foo, e: bar::Bar, f: source_code::SourceCode) {
let x = 12;
let y: Foo = Foo;