Auto merge of #118975 - GuillaumeGomez:rollup-0emhjx0, r=GuillaumeGomez

Rollup of 4 pull requests

Successful merges:

 - #113091 (Don't merge cfg and doc(cfg) attributes for re-exports)
 - #115660 (rustdoc: allow resizing the sidebar / hiding the top bar)
 - #118863 (rustc_mir_build: Enforce `rustc::potential_query_instability` lint)
 - #118909 (Some cleanup and improvement for invalid ref casting impl)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2023-12-15 12:49:36 +00:00
commit 4d1bd0db7f
27 changed files with 820 additions and 146 deletions

View File

@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
return;
};
if let Some((e, pat)) = borrow_or_assign(cx, expr) {
if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign) {
let init = cx.expr_or_init(e);
let init = cx.expr_or_init(e);
let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init) else {
return;
};
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
let Some(ty_has_interior_mutability) = is_cast_from_const_to_mut(cx, init) else {
return;
};
let orig_cast = if init.span != e.span { Some(init.span) } else { None };
let ty_has_interior_mutability = ty_has_interior_mutability.then_some(());
cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
if is_assignment {
InvalidReferenceCastingDiag::AssignToRef { orig_cast, ty_has_interior_mutability }
} else {
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast, ty_has_interior_mutability }
},
);
cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
if pat == PatternKind::Assign {
InvalidReferenceCastingDiag::AssignToRef {
orig_cast,
ty_has_interior_mutability,
}
} else {
InvalidReferenceCastingDiag::BorrowAsMut {
orig_cast,
ty_has_interior_mutability,
}
},
);
}
}
}
}
fn is_operation_we_care_about<'tcx>(
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PatternKind {
Borrow { mutbl: Mutability },
Assign,
}
fn borrow_or_assign<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
) -> Option<(bool, &'tcx Expr<'tcx>)> {
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
// &mut <expr>
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
expr
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
fn deref_assign_or_addr_of<'tcx>(
expr: &'tcx Expr<'tcx>,
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
// &(mut) <expr>
let (inner, pat) = if let ExprKind::AddrOf(_, mutbl, expr) = expr.kind {
(expr, PatternKind::Borrow { mutbl })
// <expr> = ...
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
expr
(expr, PatternKind::Assign)
// <expr> += ...
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
expr
(expr, PatternKind::Assign)
} else {
return None;
};
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
} else {
None
}
// *<inner>
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
return None;
};
Some((e, pat))
}
fn ptr_write<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
) -> Option<(bool, &'tcx Expr<'tcx>)> {
) -> Option<(&'tcx Expr<'tcx>, PatternKind)> {
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
&& let ExprKind::Path(ref qpath) = path.kind
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
@ -98,7 +112,7 @@ fn is_operation_we_care_about<'tcx>(
Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned)
)
{
Some((true, arg_ptr))
Some((arg_ptr, PatternKind::Assign))
} else {
None
}
@ -107,13 +121,10 @@ fn is_operation_we_care_about<'tcx>(
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
}
fn is_cast_from_const_to_mut<'tcx>(
fn is_cast_from_ref_to_mut_ptr<'tcx>(
cx: &LateContext<'tcx>,
orig_expr: &'tcx Expr<'tcx>,
) -> Option<bool> {
let mut need_check_freeze = false;
let mut e = orig_expr;
let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
// Bail out early if the end type is **not** a mutable pointer.
@ -121,6 +132,28 @@ fn is_cast_from_const_to_mut<'tcx>(
return None;
}
let (e, need_check_freeze) = peel_casts(cx, orig_expr);
let start_ty = cx.typeck_results().node_type(e.hir_id);
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
// If an UnsafeCell method is involved, we need to additionally check the
// inner type for the presence of the Freeze trait (ie does NOT contain
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
//
// Except on the presence of non concrete skeleton types (ie generics)
// since there is no way to make it safe for arbitrary types.
let inner_ty_has_interior_mutability =
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
(!need_check_freeze || !inner_ty_has_interior_mutability)
.then_some(inner_ty_has_interior_mutability)
} else {
None
}
}
fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
let mut gone_trough_unsafe_cell_raw_get = false;
loop {
e = e.peel_blocks();
// <expr> as ...
@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>(
)
{
if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
need_check_freeze = true;
gone_trough_unsafe_cell_raw_get = true;
}
arg
} else {
break;
let init = cx.expr_or_init(e);
if init.hir_id != e.hir_id {
init
} else {
break;
}
};
}
let start_ty = cx.typeck_results().node_type(e.hir_id);
if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
// If an UnsafeCell method is involved we need to additionally check the
// inner type for the presence of the Freeze trait (ie does NOT contain
// an UnsafeCell), since in that case we would incorrectly lint on valid casts.
//
// We also consider non concrete skeleton types (ie generics)
// to be an issue since there is no way to make it safe for abitrary types.
let inner_ty_has_interior_mutability =
!inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton();
(!need_check_freeze || !inner_ty_has_interior_mutability)
.then_some(inner_ty_has_interior_mutability)
} else {
None
}
(e, gone_trough_unsafe_cell_raw_get)
}

View File

@ -1,7 +1,6 @@
//! Construction of MIR from HIR.
//!
//! This crate also contains the match exhaustiveness and usefulness checking.
#![allow(rustc::potential_query_instability)]
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]

View File

@ -9,7 +9,7 @@ use crate::errors::*;
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::fx::FxIndexSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
@ -948,7 +948,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
err.note(format!("the matched value is of type `{}`", scrut_ty));
if !is_empty_match {
let mut non_exhaustive_tys = FxHashSet::default();
let mut non_exhaustive_tys = FxIndexSet::default();
// Look at the first witness.
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
@ -1104,7 +1104,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(
fn collect_non_exhaustive_tys<'tcx>(
cx: &MatchCheckCtxt<'_, 'tcx>,
pat: &WitnessPat<'tcx>,
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
) {
if matches!(pat.ctor(), Constructor::NonExhaustive) {
non_exhaustive_tys.insert(pat.ty());

View File

@ -2646,6 +2646,40 @@ fn filter_tokens_from_list(
tokens
}
fn filter_doc_attr_ident(ident: Symbol, is_inline: bool) -> bool {
if is_inline {
ident == sym::hidden || ident == sym::inline || ident == sym::no_inline
} else {
ident == sym::cfg
}
}
/// Remove attributes from `normal` that should not be inherited by `use` re-export.
/// Before calling this function, make sure `normal` is a `#[doc]` attribute.
fn filter_doc_attr(normal: &mut ast::NormalAttr, is_inline: bool) {
match normal.item.args {
ast::AttrArgs::Delimited(ref mut args) => {
let tokens = filter_tokens_from_list(&args.tokens, |token| {
!matches!(
token,
TokenTree::Token(
Token {
kind: TokenKind::Ident(
ident,
_,
),
..
},
_,
) if filter_doc_attr_ident(*ident, is_inline),
)
});
args.tokens = TokenStream::new(tokens);
}
ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {}
}
}
/// When inlining items, we merge their attributes (and all the reexports attributes too) with the
/// final reexport. For example:
///
@ -2672,13 +2706,6 @@ fn add_without_unwanted_attributes<'hir>(
is_inline: bool,
import_parent: Option<DefId>,
) {
// If it's not `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
if !is_inline {
for attr in new_attrs {
attrs.push((Cow::Borrowed(attr), import_parent));
}
return;
}
for attr in new_attrs {
if matches!(attr.kind, ast::AttrKind::DocComment(..)) {
attrs.push((Cow::Borrowed(attr), import_parent));
@ -2687,33 +2714,14 @@ fn add_without_unwanted_attributes<'hir>(
let mut attr = attr.clone();
match attr.kind {
ast::AttrKind::Normal(ref mut normal) => {
if let [ident] = &*normal.item.path.segments
&& let ident = ident.ident.name
&& ident == sym::doc
{
match normal.item.args {
ast::AttrArgs::Delimited(ref mut args) => {
let tokens = filter_tokens_from_list(&args.tokens, |token| {
!matches!(
token,
TokenTree::Token(
Token {
kind: TokenKind::Ident(
sym::hidden | sym::inline | sym::no_inline,
_,
),
..
},
_,
),
)
});
args.tokens = TokenStream::new(tokens);
attrs.push((Cow::Owned(attr), import_parent));
}
ast::AttrArgs::Empty | ast::AttrArgs::Eq(..) => {
attrs.push((Cow::Owned(attr), import_parent));
}
if let [ident] = &*normal.item.path.segments {
let ident = ident.ident.name;
if ident == sym::doc {
filter_doc_attr(normal, is_inline);
attrs.push((Cow::Owned(attr), import_parent));
} else if ident != sym::cfg {
// If it's not a `cfg()` attribute, we keep it.
attrs.push((Cow::Owned(attr), import_parent));
}
}
}

View File

@ -2013,6 +2013,7 @@ fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
map.insert("themeStyle".into(), 1);
map.insert("settings-menu".into(), 1);
map.insert("help-button".into(), 1);
map.insert("sidebar-button".into(), 1);
map.insert("main-content".into(), 1);
map.insert("toggle-all-docs".into(), 1);
map.insert("all-types".into(), 1);

View File

@ -1,5 +1,3 @@
use clean::AttributesExt;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
@ -465,16 +463,9 @@ fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items:
clean::ImportItem(ref import) => {
let stab_tags = if let Some(import_def_id) = import.source.did {
let ast_attrs = tcx.get_attrs_unchecked(import_def_id);
let import_attrs = Box::new(clean::Attributes::from_ast(ast_attrs));
// Just need an item with the correct def_id and attrs
let import_item = clean::Item {
item_id: import_def_id.into(),
attrs: import_attrs,
cfg: ast_attrs.cfg(tcx, &cx.cache().hidden_cfg),
..myitem.clone()
};
let import_item =
clean::Item { item_id: import_def_id.into(), ..myitem.clone() };
let stab_tags = Some(extra_info_tags(&import_item, item, tcx).to_string());
stab_tags

View File

@ -9,7 +9,7 @@ rules.
margin-left: 0 !important;
}
#copy-path {
#copy-path, #sidebar-button, .sidebar-resizer {
/* It requires JS to work so no need to display it in this case. */
display: none;
}
@ -132,6 +132,8 @@ nav.sub {
--scrape-example-help-hover-color: #000;
--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
--sidebar-resizer-hover: hsl(207, 90%, 66%);
--sidebar-resizer-active: hsl(207, 90%, 54%);
}
/* End theme: light */
@ -238,6 +240,8 @@ nav.sub {
--scrape-example-help-hover-color: #fff;
--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
--sidebar-resizer-hover: hsl(207, 30%, 54%);
--sidebar-resizer-active: hsl(207, 90%, 54%);
}
/* End theme: dark */
}

View File

@ -9,6 +9,11 @@
:root {
--nav-sub-mobile-padding: 8px;
--search-typename-width: 6.75rem;
/* DEFAULT_SIDEBAR_WIDTH
see main.js for information on these values
and on the RUSTDOC_MOBILE_BREAKPOINT */
--desktop-sidebar-width: 200px;
--src-sidebar-width: 300px;
}
/* See FiraSans-LICENSE.txt for the Fira Sans license. */
@ -383,13 +388,15 @@ img {
.sidebar {
font-size: 0.875rem;
flex: 0 0 200px;
flex: 0 0 var(--desktop-sidebar-width);
width: var(--desktop-sidebar-width);
overflow-y: scroll;
overscroll-behavior: contain;
position: sticky;
height: 100vh;
top: 0;
left: 0;
z-index: 100;
}
.rustdoc.src .sidebar {
@ -398,7 +405,95 @@ img {
overflow-x: hidden;
/* The sidebar is by default hidden */
overflow-y: hidden;
z-index: 1;
}
.hide-sidebar .sidebar,
.hide-sidebar .sidebar-resizer {
display: none;
}
.sidebar-resizer {
touch-action: none;
width: 9px;
cursor: col-resize;
z-index: 200;
position: fixed;
height: 100%;
/* make sure there's a 1px gap between the scrollbar and resize handle */
left: calc(var(--desktop-sidebar-width) + 1px);
}
.rustdoc.src .sidebar-resizer {
/* when closed, place resizer glow on top of the normal src sidebar border (no need to worry
about sidebar) */
left: 49px;
}
.src-sidebar-expanded .rustdoc.src .sidebar-resizer {
/* for src sidebar, gap is already provided by 1px border on sidebar itself, so place resizer
to right of it */
left: var(--src-sidebar-width);
}
.sidebar-resizing {
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.sidebar-resizing * {
cursor: col-resize !important;
}
.sidebar-resizing .sidebar {
position: fixed;
z-index: 100;
}
.sidebar-resizing > body {
padding-left: var(--resizing-sidebar-width);
}
.sidebar-resizer:hover,
.sidebar-resizer:active,
.sidebar-resizer:focus,
.sidebar-resizer.active {
width: 10px;
margin: 0;
/* when active or hovered, place resizer glow on top of the sidebar (right next to, or even
on top of, the scrollbar) */
left: var(--desktop-sidebar-width);
border-left: solid 1px var(--sidebar-resizer-hover);
}
.src-sidebar-expanded .rustdoc.src .sidebar-resizer:hover,
.src-sidebar-expanded .rustdoc.src .sidebar-resizer:active,
.src-sidebar-expanded .rustdoc.src .sidebar-resizer:focus,
.src-sidebar-expanded .rustdoc.src .sidebar-resizer.active {
/* when active or hovered, place resizer glow on top of the normal src sidebar border */
left: calc(var(--src-sidebar-width) - 1px);
}
@media (pointer: coarse) {
.sidebar-resizer {
/* too easy to hit the resizer while trying to hit the [-] toggle */
display: none !important;
}
}
.sidebar-resizer.active {
/* make the resize tool bigger when actually resizing, to avoid :hover styles on other stuff
while resizing */
padding: 0 140px;
width: 2px;
margin-left: -140px;
border-left: none;
}
.sidebar-resizer.active:before {
border-left: solid 2px var(--sidebar-resizer-active);
display: block;
height: 100%;
content: "";
}
.sidebar, .mobile-topbar, .sidebar-menu-toggle,
@ -416,7 +511,8 @@ img {
.src-sidebar-expanded .src .sidebar {
overflow-y: auto;
flex-basis: 300px;
flex-basis: var(--src-sidebar-width);
width: var(--src-sidebar-width);
}
.src-sidebar-expanded .src .sidebar > *:not(#src-sidebar-toggle) {
@ -477,6 +573,7 @@ ul.block, .block li {
display: block;
padding: 0.25rem; /* 4px */
margin-left: -0.25rem;
margin-right: 0.25rem;
}
.sidebar h2 {
@ -775,7 +872,7 @@ h2.section-header > .anchor {
text-decoration: underline;
}
.crate.block a.current { font-weight: 500; }
.crate.block li.current a { font-weight: 500; }
/* In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap
as much as needed on mobile (see
@ -1478,7 +1575,20 @@ a.tooltip:hover::after {
margin-left: 4px;
display: flex;
}
#settings-menu > a, #help-button > a {
#sidebar-button {
display: none;
}
.hide-sidebar #sidebar-button {
display: flex;
margin-right: 4px;
position: fixed;
left: 6px;
height: 34px;
width: 34px;
background-color: var(--main-background-color);
z-index: 1;
}
#settings-menu > a, #help-button > a, #sidebar-button > a {
display: flex;
align-items: center;
justify-content: center;
@ -1493,10 +1603,21 @@ a.tooltip:hover::after {
}
#settings-menu > a:hover, #settings-menu > a:focus,
#help-button > a:hover, #help-button > a:focus {
#help-button > a:hover, #help-button > a:focus,
#sidebar-button > a:hover, #sidebar-button > a:focus {
border-color: var(--settings-button-border-focus);
}
#sidebar-button > a:before {
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22" \
fill="none" stroke="black">\
<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5"/>\
<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\
<path d="m7.6121 3v16 M5.375 7.625h-2 m2 3h-2 m2 3h-2" stroke-width="1.25"/></svg>');
width: 22px;
height: 22px;
}
#copy-path {
color: var(--copy-path-button-color);
background: var(--main-background-color);
@ -1711,7 +1832,7 @@ However, it's not needed with smaller screen width because the doc/code block is
/*
WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
If you update this line, then you also need to update the line with the same warning
in src-script.js
in src-script.js and main.js
*/
@media (max-width: 700px) {
/* When linking to an item with an `id` (for instance, by clicking a link in the sidebar,
@ -1722,6 +1843,10 @@ in src-script.js
scroll-margin-top: 45px;
}
.hide-sidebar #sidebar-button {
position: static;
}
.rustdoc {
/* Sidebar should overlay main content, rather than pushing main content to the right.
Turn off `display: flex` on the body element. */
@ -1750,7 +1875,8 @@ in src-script.js
/* Hide the logo and item name from the sidebar. Those are displayed
in the mobile-topbar instead. */
.sidebar .logo-container,
.sidebar .location {
.sidebar .location,
.sidebar-resizer {
display: none;
}
@ -1818,6 +1944,10 @@ in src-script.js
top: 0;
}
.hide-sidebar .mobile-topbar {
display: none;
}
.sidebar-menu-toggle {
width: 45px;
/* Rare exception to specifying font sizes in rem. Since this is acting
@ -1827,6 +1957,10 @@ in src-script.js
color: var(--main-color);
}
.hide-sidebar .sidebar-menu-toggle {
display: none;
}
.sidebar-elems {
margin-top: 1em;
}
@ -1870,6 +2004,17 @@ in src-script.js
display: none;
}
/* sidebar button becomes topbar button */
#sidebar-button > a:before {
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
viewBox="0 0 22 22" fill="none" stroke="black">\
<rect x="1" y="1" width="20" height="20" ry="1.5" stroke-width="1.5"/>\
<circle cx="4.375" cy="4.375" r="1" stroke-width=".75"/>\
<path d="m3 7.375h16m0-3h-4" stroke-width="1.25"/></svg>');
width: 22px;
height: 22px;
}
/* Display an alternating layout on tablets and phones */
.item-table, .item-row, .item-table > li, .item-table > li > div,
.search-results > a, .search-results > a > div {
@ -2274,6 +2419,8 @@ in src-script.js
--scrape-example-help-hover-color: #000;
--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
--sidebar-resizer-hover: hsl(207, 90%, 66%);
--sidebar-resizer-active: hsl(207, 90%, 54%);
}
/* End theme: light */
@ -2379,6 +2526,8 @@ in src-script.js
--scrape-example-help-hover-color: #fff;
--scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
--scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
--sidebar-resizer-hover: hsl(207, 30%, 54%);
--sidebar-resizer-active: hsl(207, 90%, 54%);
}
/* End theme: dark */
@ -2488,6 +2637,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
--scrape-example-help-hover-color: #fff;
--scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
--scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
--sidebar-resizer-hover: hsl(34, 50%, 33%);
--sidebar-resizer-active: hsl(34, 100%, 66%);
}
:root[data-theme="ayu"] h1,
@ -2519,6 +2670,7 @@ Original by Dempfi (https://github.com/dempfi/ayu)
}
:root[data-theme="ayu"] .sidebar .current,
:root[data-theme="ayu"] .sidebar .current a,
:root[data-theme="ayu"] .sidebar a:hover,
:root[data-theme="ayu"] #src-sidebar div.files > a:hover,
:root[data-theme="ayu"] details.dir-entry summary:hover,
@ -2569,7 +2721,8 @@ Original by Dempfi (https://github.com/dempfi/ayu)
border-bottom: 1px solid rgba(242, 151, 24, 0.3);
}
:root[data-theme="ayu"] #settings-menu > a img {
:root[data-theme="ayu"] #settings-menu > a img,
:root[data-theme="ayu"] #sidebar-button > a:before {
filter: invert(100);
}
/* End theme: ayu */

View File

@ -1,5 +1,5 @@
// Local js definitions:
/* global addClass, getSettingValue, hasClass, searchState */
/* global addClass, getSettingValue, hasClass, searchState, updateLocalStorage */
/* global onEach, onEachLazy, removeClass, getVar */
"use strict";
@ -495,7 +495,7 @@ function preLoadCss(cssUrl) {
}
const link = document.createElement("a");
link.href = path;
if (link.href === current_page) {
if (path === current_page) {
link.className = "current";
}
link.textContent = name;
@ -857,12 +857,12 @@ function preLoadCss(cssUrl) {
for (const crate of window.ALL_CRATES) {
const link = document.createElement("a");
link.href = window.rootPath + crate + "/index.html";
if (window.rootPath !== "./" && crate === window.currentCrate) {
link.className = "current";
}
link.textContent = crate;
const li = document.createElement("li");
if (window.rootPath !== "./" && crate === window.currentCrate) {
li.className = "current";
}
li.appendChild(link);
ul.appendChild(li);
}
@ -1473,6 +1473,264 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm
searchState.setup();
}());
// Hide, show, and resize the sidebar
//
// The body class and CSS variable are initially set up in storage.js,
// but in this file, we implement:
//
// * the show sidebar button, which appears if the sidebar is hidden
// and, by clicking on it, will bring it back
// * the sidebar resize handle, which appears only on large viewports
// with a [fine precision pointer] to allow the user to change
// the size of the sidebar
//
// [fine precision pointer]: https://developer.mozilla.org/en-US/docs/Web/CSS/@media/pointer
(function() {
// 100 is the size of the logo
// don't let the sidebar get smaller than that, or it'll get squished
const SIDEBAR_MIN = 100;
// Don't let the sidebar get bigger than this
const SIDEBAR_MAX = 500;
// Don't let the body (including the gutter) get smaller than this
//
// WARNING: RUSTDOC_MOBILE_BREAKPOINT MEDIA QUERY
// Acceptable values for BODY_MIN are constrained by the mobile breakpoint
// (which is the minimum size of the whole page where the sidebar exists)
// and the default sidebar width:
//
// BODY_MIN <= RUSTDOC_MOBILE_BREAKPOINT - DEFAULT_SIDEBAR_WIDTH
//
// At the time of this writing, the DEFAULT_SIDEBAR_WIDTH on src pages is
// 300px, and the RUSTDOC_MOBILE_BREAKPOINT is 700px, so BODY_MIN must be
// at most 400px. Otherwise, it would start out at the default size, then
// grabbing the resize handle would suddenly cause it to jank to
// its contraint-generated maximum.
const RUSTDOC_MOBILE_BREAKPOINT = 700;
const BODY_MIN = 400;
// At half-way past the minimum size, vanish the sidebar entirely
const SIDEBAR_VANISH_THRESHOLD = SIDEBAR_MIN / 2;
// Toolbar button to show the sidebar.
//
// On small, "mobile-sized" viewports, it's not persistent and it
// can only be activated by going into Settings and hiding the nav bar.
// On larger, "desktop-sized" viewports (though that includes many
// tablets), it's fixed-position, appears in the left side margin,
// and it can be activated by resizing the sidebar into nothing.
const sidebarButton = document.getElementById("sidebar-button");
if (sidebarButton) {
sidebarButton.addEventListener("click", e => {
removeClass(document.documentElement, "hide-sidebar");
updateLocalStorage("hide-sidebar", "false");
e.preventDefault();
});
}
// Pointer capture.
//
// Resizing is a single-pointer gesture. Any secondary pointer is ignored
let currentPointerId = null;
// "Desired" sidebar size.
//
// This is stashed here for window resizing. If the sidebar gets
// shrunk to maintain BODY_MIN, and then the user grows the window again,
// it gets the sidebar to restore its size.
let desiredSidebarSize = null;
// Sidebar resize debouncer.
//
// The sidebar itself is resized instantly, but the body HTML can be too
// big for that, causing reflow jank. To reduce this, we queue up a separate
// animation frame and throttle it.
let pendingSidebarResizingFrame = false;
// If this page has no sidebar at all, bail out.
const resizer = document.querySelector(".sidebar-resizer");
const sidebar = document.querySelector(".sidebar");
if (!resizer || !sidebar) {
return;
}
// src page and docs page use different variables, because the contents of
// the sidebar are so different that it's reasonable to thing the user
// would want them to have different sizes
const isSrcPage = hasClass(document.body, "src");
// Call this function to hide the sidebar when using the resize handle
//
// This function also nulls out the sidebar width CSS variable and setting,
// causing it to return to its default. This does not happen if you do it
// from settings.js, which uses a separate function. It's done here because
// the minimum sidebar size is rather uncomfortable, and it must pass
// through that size when using the shrink-to-nothing gesture.
function hideSidebar() {
if (isSrcPage) {
window.rustdocCloseSourceSidebar();
updateLocalStorage("src-sidebar-width", null);
// [RUSTDOCIMPL] CSS variable fast path
//
// The sidebar width variable is attached to the <html> element by
// storage.js, because the sidebar and resizer don't exist yet.
// But the resize code, in `resize()`, sets the property on the
// sidebar and resizer elements (which are the only elements that
// use the variable) to avoid recalculating CSS on the entire
// document on every frame.
//
// So, to clear it, we need to clear all three.
document.documentElement.style.removeProperty("--src-sidebar-width");
sidebar.style.removeProperty("--src-sidebar-width");
resizer.style.removeProperty("--src-sidebar-width");
} else {
addClass(document.documentElement, "hide-sidebar");
updateLocalStorage("hide-sidebar", "true");
updateLocalStorage("desktop-sidebar-width", null);
document.documentElement.style.removeProperty("--desktop-sidebar-width");
sidebar.style.removeProperty("--desktop-sidebar-width");
resizer.style.removeProperty("--desktop-sidebar-width");
}
}
// Call this function to show the sidebar from the resize handle.
// On docs pages, this can only happen if the user has grabbed the resize
// handle, shrunk the sidebar down to nothing, and then pulls back into
// the visible range without releasing it. You can, however, grab the
// resize handle on a source page with the sidebar closed, because it
// remains visible all the time on there.
function showSidebar() {
if (isSrcPage) {
window.rustdocShowSourceSidebar();
} else {
removeClass(document.documentElement, "hide-sidebar");
updateLocalStorage("hide-sidebar", "false");
}
}
/**
* Call this to set the correct CSS variable and setting.
* This function doesn't enforce size constraints. Do that before calling it!
*
* @param {number} size - CSS px width of the sidebar.
*/
function changeSidebarSize(size) {
if (isSrcPage) {
updateLocalStorage("src-sidebar-width", size);
// [RUSTDOCIMPL] CSS variable fast path
//
// While this property is set on the HTML element at load time,
// because the sidebar isn't actually loaded yet,
// we scope this update to the sidebar to avoid hitting a slow
// path in WebKit.
sidebar.style.setProperty("--src-sidebar-width", size + "px");
resizer.style.setProperty("--src-sidebar-width", size + "px");
} else {
updateLocalStorage("desktop-sidebar-width", size);
sidebar.style.setProperty("--desktop-sidebar-width", size + "px");
resizer.style.setProperty("--desktop-sidebar-width", size + "px");
}
}
// Check if the sidebar is hidden. Since src pages and doc pages have
// different settings, this function has to check that.
function isSidebarHidden() {
return isSrcPage ?
!hasClass(document.documentElement, "src-sidebar-expanded") :
hasClass(document.documentElement, "hide-sidebar");
}
// Respond to the resize handle event.
// This function enforces size constraints, and implements the
// shrink-to-nothing gesture based on thresholds defined above.
function resize(e) {
if (currentPointerId === null || currentPointerId !== e.pointerId) {
return;
}
e.preventDefault();
const pos = e.clientX - sidebar.offsetLeft - 3;
if (pos < SIDEBAR_VANISH_THRESHOLD) {
hideSidebar();
} else if (pos >= SIDEBAR_MIN) {
if (isSidebarHidden()) {
showSidebar();
}
// don't let the sidebar get wider than SIDEBAR_MAX, or the body narrower
// than BODY_MIN
const constrainedPos = Math.min(pos, window.innerWidth - BODY_MIN, SIDEBAR_MAX);
changeSidebarSize(constrainedPos);
desiredSidebarSize = constrainedPos;
if (pendingSidebarResizingFrame !== false) {
clearTimeout(pendingSidebarResizingFrame);
}
pendingSidebarResizingFrame = setTimeout(() => {
if (currentPointerId === null || pendingSidebarResizingFrame === false) {
return;
}
pendingSidebarResizingFrame = false;
document.documentElement.style.setProperty(
"--resizing-sidebar-width",
desiredSidebarSize + "px"
);
}, 100);
}
}
// Respond to the window resize event.
window.addEventListener("resize", () => {
if (window.innerWidth < RUSTDOC_MOBILE_BREAKPOINT) {
return;
}
stopResize();
if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) {
changeSidebarSize(window.innerWidth - BODY_MIN);
} else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) {
changeSidebarSize(desiredSidebarSize);
}
});
function stopResize(e) {
if (currentPointerId === null) {
return;
}
if (e) {
e.preventDefault();
}
desiredSidebarSize = sidebar.getBoundingClientRect().width;
removeClass(resizer, "active");
window.removeEventListener("pointermove", resize, false);
window.removeEventListener("pointerup", stopResize, false);
removeClass(document.documentElement, "sidebar-resizing");
document.documentElement.style.removeProperty( "--resizing-sidebar-width");
if (resizer.releasePointerCapture) {
resizer.releasePointerCapture(currentPointerId);
currentPointerId = null;
}
}
function initResize(e) {
if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) {
return;
}
if (resizer.setPointerCapture) {
resizer.setPointerCapture(e.pointerId);
if (!resizer.hasPointerCapture(e.pointerId)) {
// unable to capture pointer; something else has it
// on iOS, this usually means you long-clicked a link instead
resizer.releasePointerCapture(e.pointerId);
return;
}
currentPointerId = e.pointerId;
}
e.preventDefault();
window.addEventListener("pointermove", resize, false);
window.addEventListener("pointercancel", stopResize, false);
window.addEventListener("pointerup", stopResize, false);
addClass(resizer, "active");
addClass(document.documentElement, "sidebar-resizing");
const pos = e.clientX - sidebar.offsetLeft - 3;
document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px");
desiredSidebarSize = null;
}
resizer.addEventListener("pointerdown", initResize, false);
}());
// This section handles the copy button that appears next to the path breadcrumbs
(function() {
let reset_button_timeout = null;

View File

@ -29,6 +29,13 @@
window.rustdoc_remove_line_numbers_from_examples();
}
break;
case "hide-sidebar":
if (value === true) {
addClass(document.documentElement, "hide-sidebar");
} else {
removeClass(document.documentElement, "hide-sidebar");
}
break;
}
}
@ -186,6 +193,11 @@
"js_name": "line-numbers",
"default": false,
},
{
"name": "Hide persistent navigation bar",
"js_name": "hide-sidebar",
"default": false,
},
{
"name": "Disable keyboard shortcuts",
"js_name": "disable-shortcuts",
@ -216,6 +228,13 @@
function displaySettings() {
settingsMenu.style.display = "";
onEachLazy(settingsMenu.querySelectorAll("input[type='checkbox']"), el => {
const val = getSettingValue(el.id);
const checked = val === "true";
if (checked !== el.checked && val !== null) {
el.checked = checked;
}
});
}
function settingsBlurHandler(event) {

View File

@ -71,16 +71,31 @@ function createDirEntry(elem, parent, fullPath, hasFoundFile) {
return hasFoundFile;
}
let toggleLabel;
function getToggleLabel() {
toggleLabel = toggleLabel || document.querySelector("#src-sidebar-toggle button");
return toggleLabel;
}
window.rustdocCloseSourceSidebar = () => {
removeClass(document.documentElement, "src-sidebar-expanded");
getToggleLabel().innerText = ">";
updateLocalStorage("source-sidebar-show", "false");
};
window.rustdocShowSourceSidebar = () => {
addClass(document.documentElement, "src-sidebar-expanded");
getToggleLabel().innerText = "<";
updateLocalStorage("source-sidebar-show", "true");
};
function toggleSidebar() {
const child = this.parentNode.children[0];
if (child.innerText === ">") {
addClass(document.documentElement, "src-sidebar-expanded");
child.innerText = "<";
updateLocalStorage("source-sidebar-show", "true");
window.rustdocShowSourceSidebar();
} else {
removeClass(document.documentElement, "src-sidebar-expanded");
child.innerText = ">";
updateLocalStorage("source-sidebar-show", "false");
window.rustdocCloseSourceSidebar();
}
}

View File

@ -183,11 +183,38 @@ if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
updateTheme();
// Hide, show, and resize the sidebar at page load time
//
// This needs to be done here because this JS is render-blocking,
// so that the sidebar doesn't "jump" after appearing on screen.
// The user interaction to change this is set up in main.js.
if (getSettingValue("source-sidebar-show") === "true") {
// At this point in page load, `document.body` is not available yet.
// Set a class on the `<html>` element instead.
addClass(document.documentElement, "src-sidebar-expanded");
}
if (getSettingValue("hide-sidebar") === "true") {
// At this point in page load, `document.body` is not available yet.
// Set a class on the `<html>` element instead.
addClass(document.documentElement, "hide-sidebar");
}
function updateSidebarWidth() {
const desktopSidebarWidth = getSettingValue("desktop-sidebar-width");
if (desktopSidebarWidth && desktopSidebarWidth !== "null") {
document.documentElement.style.setProperty(
"--desktop-sidebar-width",
desktopSidebarWidth + "px"
);
}
const srcSidebarWidth = getSettingValue("src-sidebar-width");
if (srcSidebarWidth && srcSidebarWidth !== "null") {
document.documentElement.style.setProperty(
"--src-sidebar-width",
srcSidebarWidth + "px"
);
}
}
updateSidebarWidth();
// If we navigate away (for example to a settings page), and then use the back or
// forward button to get back to a page, the theme may have changed in the meantime.
@ -201,5 +228,6 @@ if (getSettingValue("source-sidebar-show") === "true") {
window.addEventListener("pageshow", ev => {
if (ev.persisted) {
setTimeout(updateTheme, 0);
setTimeout(updateSidebarWidth, 0);
}
});

View File

@ -114,6 +114,7 @@
{% endif %}
{{ sidebar|safe }}
</nav> {# #}
<div class="sidebar-resizer"></div>
<main> {# #}
{% if page.css_class != "src" %}<div class="width-limiter">{% endif %}
<nav class="sub"> {# #}
@ -128,6 +129,11 @@
{% endif %}
<form class="search-form"> {# #}
<span></span> {# This empty span is a hacky fix for Safari - See #93184 #}
{% if page.css_class != "src" %}
<div id="sidebar-button" tabindex="-1"> {# #}
<a href="{{page.root_path|safe}}{{layout.krate|safe}}/all.html" title="show sidebar"></a> {# #}
</div> {# #}
{% endif %}
<input {#+ #}
class="search-input" {#+ #}
name="search" {#+ #}
@ -136,8 +142,8 @@
spellcheck="false" {#+ #}
placeholder="Click or press S to search, ? for more options…" {#+ #}
type="search"> {# #}
<div id="help-button" title="help" tabindex="-1"> {# #}
<a href="{{page.root_path|safe}}help.html">?</a> {# #}
<div id="help-button" tabindex="-1"> {# #}
<a href="{{page.root_path|safe}}help.html" title="help">?</a> {# #}
</div> {# #}
<div id="settings-menu" tabindex="-1"> {# #}
<a href="{{page.root_path|safe}}settings.html" title="settings"> {# #}

View File

@ -0,0 +1,20 @@
// Checks sidebar resizing stays synced with the setting
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
set-window-size: (400, 600)
// Verify that the "hide" option is unchecked
click: "#settings-menu"
wait-for: "#settings"
assert-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "false"})
assert-css: (".mobile-topbar", {"display": "flex"})
// Toggle it
click: "#hide-sidebar"
assert-property: ("#hide-sidebar", {"checked": "true"})
assert-css: (".mobile-topbar", {"display": "none"})
// Toggle it again
click: "#hide-sidebar"
assert-property: ("#hide-sidebar", {"checked": "false"})
assert-css: (".mobile-topbar", {"display": "flex"})

View File

@ -26,12 +26,12 @@ define-function: (
assert-css: (".item-table .keyword", {"color": |keyword|}, ALL)
// Checking sidebar elements.
assert-css: (
".sidebar-elems a:not(.current)",
".sidebar-elems li:not(.current) a",
{"color": |sidebar|, "background-color": "rgba(0, 0, 0, 0)", "font-weight": "400"},
ALL,
)
assert-css: (
".sidebar-elems a.current",
".sidebar-elems li.current a",
{
"color": |sidebar_current|,
"background-color": |sidebar_current_background|,

View File

@ -17,10 +17,10 @@ define-function: (
reload:
// Struct
assert-css: (
".sidebar .block.struct a:not(.current)",
".sidebar .block.struct li:not(.current) a",
{"color": |struct|, "background-color": "rgba(0, 0, 0, 0)"},
)
move-cursor-to: ".sidebar .block.struct a:not(.current)"
move-cursor-to: ".sidebar .block.struct li:not(.current) a"
assert-css: (
".sidebar .block.struct a:hover",
{"color": |struct_hover|, "background-color": |struct_hover_background|},

View File

@ -0,0 +1,23 @@
// Checks sidebar resizing stays synced with the setting
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-property: (".sidebar", {"clientWidth": "200"})
show-text: true
// Verify that the "hide" option is unchecked
click: "#settings-menu"
wait-for: "#settings"
assert-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "false"})
press-key: "Escape"
wait-for-css: ("#settings", {"display": "none"})
drag-and-drop: ((205, 100), (5, 100))
assert-css: (".sidebar", {"display": "none"})
// Verify that the "hide" option is checked
focus: "#settings-menu a"
press-key: "Enter"
wait-for-css: ("#settings", {"display": "block"})
assert-property: ("#hide-sidebar", {"checked": "true"})
click: "#hide-sidebar"
wait-for-css: (".sidebar", {"display": "block"})

View File

@ -0,0 +1,37 @@
// Checks sidebar resizing
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
set-window-size: (1280, 600)
wait-for-property: (".sidebar", {"clientWidth": 200}, [NEAR])
// resize past maximum (don't grow past 500)
drag-and-drop: ((205, 100), (600, 100))
wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
// make the window small enough that the sidebar has to shrink
set-window-size: (750, 600)
wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
// grow the window again to make the sidebar bigger
set-window-size: (1280, 600)
wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
// make the window small enough that the sidebar has to shrink
set-window-size: (750, 600)
wait-for-property: (".sidebar", {"clientWidth": 350}, [NEAR])
assert-local-storage: {"rustdoc-desktop-sidebar-width": "350"}
set-window-size: (400, 600)
wait-for-css: (".sidebar", {"display": "block", "left": "-1000px"})
assert-local-storage: {"rustdoc-desktop-sidebar-width": "350"}
// grow the window again to make the sidebar bigger
set-window-size: (1280, 600)
wait-for-property: (".sidebar", {"clientWidth": 500}, [NEAR])
// shrink back down again, then reload the page
// the "desired size" is a bit of remembered implicit state,
// and rustdoc tries to minimize things like this
set-window-size: (800, 600)
wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])
reload:
set-window-size: (1280, 600)
wait-for-property: (".sidebar", {"clientWidth": 400}, [NEAR])

View File

@ -0,0 +1,28 @@
// Checks sidebar resizing
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-property: (".sidebar", {"clientWidth": "200"})
show-text: true
// normal resizing
drag-and-drop: ((205, 100), (185, 100))
assert-property: (".sidebar", {"clientWidth": "182"})
// resize past maximum (don't grow past 500)
drag-and-drop: ((185, 100), (600, 100))
assert-property: (".sidebar", {"clientWidth": "500"})
// resize past minimum (hide sidebar)
drag-and-drop: ((501, 100), (5, 100))
assert-property: (".sidebar", {"clientWidth": "0"})
assert-css: (".sidebar", {"display": "none"})
assert-local-storage: {"rustdoc-hide-sidebar": "true"}
set-local-storage: {"rustdoc-hide-sidebar": "false"}
// Now same thing, but for source code
go-to: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
assert-property: (".sidebar", {"clientWidth": "49"})
drag-and-drop: ((52, 100), (185, 100))
assert-property: (".sidebar", {"clientWidth": "181"})
drag-and-drop: ((185, 100), (600, 100))
assert-property: (".sidebar", {"clientWidth": "499"})
drag-and-drop: ((500, 100), (5, 100))
// instead of hiding the sidebar entirely, this
// will switch to the toggle mode
assert-property: (".sidebar", {"clientWidth": "49"})

View File

@ -48,6 +48,7 @@ call-function: (
// Next, desktop mode layout.
set-window-size: (1100, 800)
wait-for: "#src-sidebar-toggle"
// We check that the sidebar isn't expanded and has the expected width.
assert-css: ("nav.sidebar", {"width": "50px"})
// We now click on the button to expand the sidebar.
@ -58,7 +59,7 @@ assert-css: (".src-sidebar-expanded nav.sidebar a", {"font-size": "14px"})
// We collapse the sidebar.
click: (10, 10)
// We ensure that the class has been removed.
wait-for: "html:not(.expanded)"
wait-for: "html:not(.src-sidebar-expanded)"
assert: "nav.sidebar"
// Checking that only the path to the current file is "open".

View File

@ -57,7 +57,7 @@ assert-count: (".sidebar h2", 1)
assert-text: ("#all-types", "All Items")
assert-css: ("#all-types", {"color": "#356da4"})
// We check that we have the crates list and that the "current" on is "test_docs".
assert-text: (".sidebar-elems ul.crate > li > a.current", "test_docs")
assert-text: (".sidebar-elems ul.crate > li.current > a", "test_docs")
// And we're also supposed to have the list of items in the current module.
assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Re-exports")
assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Modules")
@ -98,7 +98,7 @@ assert-property: (".sidebar", {"clientWidth": "200"})
assert-text: (".sidebar > .sidebar-crate > h2 > a", "lib2")
assert-count: (".sidebar .location", 0)
// We check that we have the crates list and that the "current" on is now "lib2".
assert-text: (".sidebar-elems ul.crate > li > a.current", "lib2")
assert-text: (".sidebar-elems ul.crate > li.current > a", "lib2")
// We now go to the "foobar" function page.
assert-text: (".sidebar-elems > section ul.block > li:nth-child(1)", "Modules")
assert-text: (".sidebar-elems > section ul.block > li:nth-child(2)", "Structs")

View File

@ -96,4 +96,6 @@
--scrape-example-help-hover-color: #000;
--scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
--scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
--sidebar-resizer-hover: hsl(207, 90%, 66%);
--sidebar-resizer-active: hsl(207, 90%, 54%);
}

View File

@ -0,0 +1,30 @@
// This test ensures that only the re-export `cfg` will be displayed and that it won't
// include `cfg`s from the previous chained items.
#![crate_name = "foo"]
#![feature(doc_auto_cfg, doc_cfg)]
mod foo {
#[cfg(not(feature = "foo"))]
pub struct Bar;
#[doc(cfg(not(feature = "bar")))]
pub struct Bar2;
}
// @has 'foo/index.html'
// @has - '//*[@class="item-name"]' 'BabarNon-lie'
#[cfg(not(feature = "lie"))]
pub use crate::foo::Bar as Babar;
// @has - '//*[@class="item-name"]' 'Babar2Non-cake'
#[doc(cfg(not(feature = "cake")))]
pub use crate::foo::Bar2 as Babar2;
// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar as Elephant;Non-robot'
#[cfg(not(feature = "robot"))]
pub use crate::Babar as Elephant;
// @has - '//*[@class="item-table"]/li' 'pub use crate::Babar2 as Elephant2;Non-cat'
#[doc(cfg(not(feature = "cat")))]
pub use crate::Babar2 as Elephant2;

View File

@ -27,7 +27,7 @@ pub mod mod1 {
pub mod mod2 {
// @has - '//code' 'pub use tag::Portability;'
// @!has - '//span' 'Deprecated'
// @has - '//span' 'sync'
// @!has - '//span' 'sync'
pub use tag::Portability;
}
@ -35,7 +35,7 @@ pub mod mod2 {
pub mod mod3 {
// @has - '//code' 'pub use tag::Both;'
// @has - '//span' 'Deprecated'
// @has - '//span' 'sync'
// @!has - '//span' 'sync'
pub use tag::Both;
}

View File

@ -35,7 +35,7 @@ pub mod mod1 {
pub mod mod2 {
// @has - '//code' 'pub use tag::Portability;'
// @!has - '//span' 'Experimental'
// @has - '//span' 'sync'
// @!has - '//span' 'sync'
#[stable(feature = "rust1", since = "1.0.0")]
pub use tag::Portability;
}
@ -45,7 +45,7 @@ pub mod mod2 {
pub mod mod3 {
// @has - '//code' 'pub use tag::Both;'
// @has - '//span' 'Experimental'
// @has - '//span' 'sync'
// @!has - '//span' 'sync'
#[stable(feature = "rust1", since = "1.0.0")]
pub use tag::Both;
}

View File

@ -113,6 +113,13 @@ unsafe fn assign_to_ref() {
*((&std::cell::UnsafeCell::new(0)) as *const _ as *mut i32) = 5;
//~^ ERROR assigning to `&T` is undefined behavior
let value = num as *const i32 as *mut i32;
*value = 1;
//~^ ERROR assigning to `&T` is undefined behavior
let value = num as *const i32;
let value = value as *mut i32;
*value = 1;
//~^ ERROR assigning to `&T` is undefined behavior
let value = num as *const i32 as *mut i32;
*value = 1;
//~^ ERROR assigning to `&T` is undefined behavior

View File

@ -286,7 +286,27 @@ LL | *value = 1;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:120:5
--> $DIR/reference_casting.rs:121:5
|
LL | let value = value as *mut i32;
| ----------------- casting happend here
LL | *value = 1;
| ^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:124:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
LL | *value = 1;
| ^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:127:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
@ -297,7 +317,7 @@ LL | *value_rebind = 1;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:122:5
--> $DIR/reference_casting.rs:129:5
|
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -305,7 +325,7 @@ LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:124:5
--> $DIR/reference_casting.rs:131:5
|
LL | *(num as *const _ as usize as *mut i32) = 2;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -313,7 +333,7 @@ LL | *(num as *const _ as usize as *mut i32) = 2;
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:126:5
--> $DIR/reference_casting.rs:133:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
@ -324,7 +344,7 @@ LL | std::ptr::write(value, 2);
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:128:5
--> $DIR/reference_casting.rs:135:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
@ -335,7 +355,7 @@ LL | std::ptr::write_unaligned(value, 2);
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:130:5
--> $DIR/reference_casting.rs:137:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
@ -346,12 +366,12 @@ LL | std::ptr::write_volatile(value, 2);
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:134:9
--> $DIR/reference_casting.rs:141:9
|
LL | *(this as *const _ as *mut _) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: for more information, visit <https://doc.rust-lang.org/book/ch15-05-interior-mutability.html>
error: aborting due to 40 previous errors
error: aborting due to 42 previous errors