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:
commit
7ac0330c6d
36 changed files with 848 additions and 536 deletions
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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 → \
|
||||
> *[basic-type]* \
|
||||
|
|
@ -722,6 +723,7 @@ A *placeholder* may occur in circumstances where a type or const value is not re
|
|||
> | *[mut-ptr-type]* \
|
||||
> | *[fn-type]* \
|
||||
> | *[dyn-trait-type]* \
|
||||
> | *[pattern-type]* \
|
||||
> | *[path]* \
|
||||
> | *[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> → \
|
||||
> *[range-pattern-kind]* \
|
||||
> *[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.
|
|||
> | *[mut-ptr-type]* \
|
||||
> | *[fn-type]* \
|
||||
> | *[dyn-trait-type]* \
|
||||
> | *[pattern-type]* \
|
||||
> | *[path]* \
|
||||
> | *[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] → \
|
||||
> *[range-pattern-kind]* \
|
||||
> *[or-pattern-kind]*
|
||||
>
|
||||
> [range-pattern-kind] -> `R` *[const]* *[const]* \
|
||||
> [or-pattern-kind] -> `O` *[pattern-kind]* `E` \
|
||||
>
|
||||
> [namespace] → *[lower]* | *[upper]*
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
@ -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() {}
|
||||
|
|
|
|||
5
tests/rustdoc/auxiliary/ext-repr.rs
Normal file
5
tests/rustdoc/auxiliary/ext-repr.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#[repr(i8)]
|
||||
pub enum ReprI8 {
|
||||
Var0,
|
||||
Var1,
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
54
tests/rustdoc/jump-to-def-assoc-items.rs
Normal file
54
tests/rustdoc/jump-to-def-assoc-items.rs
Normal 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,
|
||||
}
|
||||
20
tests/rustdoc/jump-to-def-ice-assoc-types.rs
Normal file
20
tests/rustdoc/jump-to-def-ice-assoc-types.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
24
tests/rustdoc/jump-to-def-ice.rs
Normal file
24
tests/rustdoc/jump-to-def-ice.rs
Normal 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);
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue