Skip to content

Commit 1a95cc6

Browse files
committed
Auto merge of rust-lang#140702 - GuillaumeGomez:rollup-rpyxs20, r=GuillaumeGomez
Rollup of 4 pull requests Successful merges: - rust-lang#140135 (Unify sidebar buttons to use the same image) - rust-lang#140632 (add a test for issue rust-lang#81317) - rust-lang#140658 (`deref_patterns`: let string and byte string literal patterns peel references and smart pointers before matching) - rust-lang#140681 (Don't ignore compiler stderr in `lib-defaults.rs`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents f5d3fe2 + 9d05497 commit 1a95cc6

File tree

18 files changed

+586
-125
lines changed

18 files changed

+586
-125
lines changed

compiler/rustc_hir_typeck/src/pat.rs

+101-39
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,20 @@ enum PeelKind {
177177
/// Only peel reference types. This is used for explicit `deref!(_)` patterns, which dereference
178178
/// any number of `&`/`&mut` references, plus a single smart pointer.
179179
ExplicitDerefPat,
180-
/// Implicitly peel any number of references, and if `deref_patterns` is enabled, smart pointer
181-
/// ADTs. In order to peel only as much as necessary for the pattern to match, the `until_adt`
182-
/// field contains the ADT def that the pattern is a constructor for, if applicable, so that we
183-
/// don't peel it. See [`ResolvedPat`] for more information.
184-
Implicit { until_adt: Option<DefId> },
180+
/// Implicitly peel references, and if `deref_patterns` is enabled, smart pointer ADTs.
181+
Implicit {
182+
/// The ADT the pattern is a constructor for, if applicable, so that we don't peel it. See
183+
/// [`ResolvedPat`] for more information.
184+
until_adt: Option<DefId>,
185+
/// The number of references at the head of the pattern's type, so we can leave that many
186+
/// untouched. This is `1` for string literals, and `0` for most patterns.
187+
pat_ref_layers: usize,
188+
},
185189
}
186190

187191
impl AdjustMode {
188192
const fn peel_until_adt(opt_adt_def: Option<DefId>) -> AdjustMode {
189-
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def } }
193+
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: opt_adt_def, pat_ref_layers: 0 } }
190194
}
191195
const fn peel_all() -> AdjustMode {
192196
AdjustMode::peel_until_adt(None)
@@ -488,9 +492,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
488492
match pat.kind {
489493
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
490494
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
491-
_ if let AdjustMode::Peel { .. } = adjust_mode
495+
_ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode
492496
&& pat.default_binding_modes
493-
&& let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() =>
497+
&& let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind()
498+
&& self.should_peel_ref(peel_kind, expected) =>
494499
{
495500
debug!("inspecting {:?}", expected);
496501

@@ -531,24 +536,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
531536
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
532537
// examples in `tests/ui/pattern/deref_patterns/`.
533538
_ if self.tcx.features().deref_patterns()
534-
&& let AdjustMode::Peel { kind: PeelKind::Implicit { until_adt } } = adjust_mode
539+
&& let AdjustMode::Peel { kind: peel_kind } = adjust_mode
535540
&& pat.default_binding_modes
536-
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
537-
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
538-
// implicitly deref generics if we allow them here, but primitives, tuples, and
539-
// inference vars definitely should be stopped. Figure out what makes most sense.
540-
&& let ty::Adt(scrutinee_adt, _) = *expected.kind()
541-
// Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
542-
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
543-
&& until_adt != Some(scrutinee_adt.did())
544-
// At this point, the pattern isn't able to match `expected` without peeling. Check
545-
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
546-
// type error instead of a missing impl error if not. This only checks for `Deref`,
547-
// not `DerefPure`: we require that too, but we want a trait error if it's missing.
548-
&& let Some(deref_trait) = self.tcx.lang_items().deref_trait()
549-
&& self
550-
.type_implements_trait(deref_trait, [expected], self.param_env)
551-
.may_apply() =>
541+
&& self.should_peel_smart_pointer(peel_kind, expected) =>
552542
{
553543
debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
554544
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
@@ -680,21 +670,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
680670

681671
// String and byte-string literals result in types `&str` and `&[u8]` respectively.
682672
// All other literals result in non-reference types.
683-
// As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`.
684-
//
685-
// Call `resolve_vars_if_possible` here for inline const blocks.
686-
PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() {
687-
ty::Ref(..) => AdjustMode::Pass,
688-
_ => {
689-
// Path patterns have already been handled, and inline const blocks currently
690-
// aren't possible to write, so any handling for them would be untested.
691-
if cfg!(debug_assertions)
692-
&& self.tcx.features().deref_patterns()
693-
&& !matches!(lt.kind, PatExprKind::Lit { .. })
694-
{
695-
span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
673+
// As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}` unless
674+
// `deref_patterns` is enabled.
675+
PatKind::Expr(lt) => {
676+
// Path patterns have already been handled, and inline const blocks currently
677+
// aren't possible to write, so any handling for them would be untested.
678+
if cfg!(debug_assertions)
679+
&& self.tcx.features().deref_patterns()
680+
&& !matches!(lt.kind, PatExprKind::Lit { .. })
681+
{
682+
span_bug!(lt.span, "FIXME(deref_patterns): adjust mode unimplemented for {:?}", lt.kind);
683+
}
684+
// Call `resolve_vars_if_possible` here for inline const blocks.
685+
let lit_ty = self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt));
686+
// If `deref_patterns` is enabled, allow `if let "foo" = &&"foo" {}`.
687+
if self.tcx.features().deref_patterns() {
688+
let mut peeled_ty = lit_ty;
689+
let mut pat_ref_layers = 0;
690+
while let ty::Ref(_, inner_ty, mutbl) = *peeled_ty.kind() {
691+
// We rely on references at the head of constants being immutable.
692+
debug_assert!(mutbl.is_not());
693+
pat_ref_layers += 1;
694+
peeled_ty = inner_ty;
696695
}
697-
AdjustMode::peel_all()
696+
AdjustMode::Peel { kind: PeelKind::Implicit { until_adt: None, pat_ref_layers } }
697+
} else {
698+
if lit_ty.is_ref() { AdjustMode::Pass } else { AdjustMode::peel_all() }
698699
}
699700
},
700701

@@ -720,6 +721,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
720721
}
721722
}
722723

724+
/// Assuming `expected` is a reference type, determine whether to peel it before matching.
725+
fn should_peel_ref(&self, peel_kind: PeelKind, mut expected: Ty<'tcx>) -> bool {
726+
debug_assert!(expected.is_ref());
727+
let pat_ref_layers = match peel_kind {
728+
PeelKind::ExplicitDerefPat => 0,
729+
PeelKind::Implicit { pat_ref_layers, .. } => pat_ref_layers,
730+
};
731+
732+
// Most patterns don't have reference types, so we'll want to peel all references from the
733+
// scrutinee before matching. To optimize for the common case, return early.
734+
if pat_ref_layers == 0 {
735+
return true;
736+
}
737+
debug_assert!(
738+
self.tcx.features().deref_patterns(),
739+
"Peeling for patterns with reference types is gated by `deref_patterns`."
740+
);
741+
742+
// If the pattern has as many or more layers of reference as the expected type, we can match
743+
// without peeling more, unless we find a smart pointer or `&mut` that we also need to peel.
744+
// We don't treat `&` and `&mut` as interchangeable, but by peeling `&mut`s before matching,
745+
// we can still, e.g., match on a `&mut str` with a string literal pattern. This is because
746+
// string literal patterns may be used where `str` is expected.
747+
let mut expected_ref_layers = 0;
748+
while let ty::Ref(_, inner_ty, mutbl) = *expected.kind() {
749+
if mutbl.is_mut() {
750+
// Mutable references can't be in the final value of constants, thus they can't be
751+
// at the head of their types, thus we should always peel `&mut`.
752+
return true;
753+
}
754+
expected_ref_layers += 1;
755+
expected = inner_ty;
756+
}
757+
pat_ref_layers < expected_ref_layers || self.should_peel_smart_pointer(peel_kind, expected)
758+
}
759+
760+
/// Determine whether `expected` is a smart pointer type that should be peeled before matching.
761+
fn should_peel_smart_pointer(&self, peel_kind: PeelKind, expected: Ty<'tcx>) -> bool {
762+
// Explicit `deref!(_)` patterns match against smart pointers; don't peel in that case.
763+
if let PeelKind::Implicit { until_adt, .. } = peel_kind
764+
// For simplicity, only apply overloaded derefs if `expected` is a known ADT.
765+
// FIXME(deref_patterns): we'll get better diagnostics for users trying to
766+
// implicitly deref generics if we allow them here, but primitives, tuples, and
767+
// inference vars definitely should be stopped. Figure out what makes most sense.
768+
&& let ty::Adt(scrutinee_adt, _) = *expected.kind()
769+
// Don't peel if the pattern type already matches the scrutinee. E.g., stop here if
770+
// matching on a `Cow<'a, T>` scrutinee with a `Cow::Owned(_)` pattern.
771+
&& until_adt != Some(scrutinee_adt.did())
772+
// At this point, the pattern isn't able to match `expected` without peeling. Check
773+
// that it implements `Deref` before assuming it's a smart pointer, to get a normal
774+
// type error instead of a missing impl error if not. This only checks for `Deref`,
775+
// not `DerefPure`: we require that too, but we want a trait error if it's missing.
776+
&& let Some(deref_trait) = self.tcx.lang_items().deref_trait()
777+
&& self.type_implements_trait(deref_trait, [expected], self.param_env).may_apply()
778+
{
779+
true
780+
} else {
781+
false
782+
}
783+
}
784+
723785
fn check_pat_expr_unadjusted(&self, lt: &'tcx hir::PatExpr<'tcx>) -> Ty<'tcx> {
724786
let ty = match &lt.kind {
725787
rustc_hir::PatExprKind::Lit { lit, negated } => {
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.20.3
1+
0.20.6

src/doc/unstable-book/src/language-features/deref-patterns.md

+24-7
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,26 @@ let deref!(x) = Box::new(NoCopy) else { unreachable!() };
6565
drop::<NoCopy>(x);
6666
```
6767

68-
Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str`
69-
is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is
70-
expected. This lets them be used in `deref!(_)` patterns:
68+
Additionally, `deref_patterns` implements changes to string and byte string literal patterns,
69+
allowing then to be used in deref patterns:
7170

7271
```rust
7372
# #![feature(deref_patterns)]
7473
# #![allow(incomplete_features)]
75-
match ("test".to_string(), b"test".to_vec()) {
76-
(deref!("test"), deref!(b"test")) => {}
74+
match ("test".to_string(), Box::from("test"), b"test".to_vec()) {
75+
("test", "test", b"test") => {}
76+
_ => panic!(),
77+
}
78+
79+
// This works through multiple layers of reference and smart pointer:
80+
match (&Box::new(&"test".to_string()), &&&"test") {
81+
("test", "test") => {}
82+
_ => panic!(),
83+
}
84+
85+
// `deref!("...")` syntax may also be used:
86+
match "test".to_string() {
87+
deref!("test") => {}
7788
_ => panic!(),
7889
}
7990

@@ -82,10 +93,16 @@ match *"test" {
8293
"test" => {}
8394
_ => panic!(),
8495
}
96+
match *b"test" {
97+
b"test" => {}
98+
_ => panic!(),
99+
}
100+
match *(b"test" as &[u8]) {
101+
b"test" => {}
102+
_ => panic!(),
103+
}
85104
```
86105

87-
Implicit deref pattern syntax is not yet supported for string or byte string literals.
88-
89106
[`box_patterns`]: ./box-patterns.md
90107
[`string_deref_patterns`]: ./string-deref-patterns.md
91108
[smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors

src/librustdoc/html/static/css/rustdoc.css

+22-21
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ xmlns="http://www.w3.org/2000/svg" fill="black" height="18px">\
5555
--collapse-arrow-image: url('data:image/svg+xml,<svg width="16" height="16" viewBox="0 0 16 16" \
5656
enable-background="new 0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path fill="none" \
5757
d="M3,8l4,4l4,-4m-4,4M3,4l4,4l4,-4" stroke="black" stroke-width="2"/></svg>');
58+
--hamburger-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
59+
viewBox="0 0 22 22" fill="none" stroke="black">\
60+
<path d="M3,5h16M3,11h16M3,17h16" stroke-width="2.75"/></svg>');
5861
}
5962

6063
:root.sans-serif {
@@ -2001,9 +2004,11 @@ a.tooltip:hover::after {
20012004
display: flex;
20022005
margin-right: 4px;
20032006
position: fixed;
2004-
left: 6px;
20052007
height: 34px;
20062008
width: 34px;
2009+
}
2010+
.hide-sidebar #sidebar-button {
2011+
left: 6px;
20072012
background-color: var(--main-background-color);
20082013
z-index: 1;
20092014
}
@@ -2019,6 +2024,8 @@ a.tooltip:hover::after {
20192024
align-items: center;
20202025
justify-content: center;
20212026
flex-direction: column;
2027+
}
2028+
#settings-menu > a, #help-button > a, button#toggle-all-docs {
20222029
border: 1px solid transparent;
20232030
border-radius: var(--button-border-radius);
20242031
color: var(--main-color);
@@ -2031,14 +2038,15 @@ a.tooltip:hover::after {
20312038
min-width: 0;
20322039
}
20332040
#sidebar-button > a {
2034-
background-color: var(--button-background-color);
2035-
border-color: var(--border-color);
2041+
background-color: var(--sidebar-background-color);
20362042
width: 33px;
20372043
}
2044+
#sidebar-button > a:hover, #sidebar-button > a:focus-visible {
2045+
background-color: var(--main-background-color);
2046+
}
20382047

20392048
#settings-menu > a:hover, #settings-menu > a:focus-visible,
20402049
#help-button > a:hover, #help-button > a:focus-visible,
2041-
#sidebar-button > a:hover, #sidebar-button > a:focus-visible,
20422050
button#toggle-all-docs:hover, button#toggle-all-docs:focus-visible {
20432051
border-color: var(--settings-button-border-focus);
20442052
text-decoration: none;
@@ -2405,28 +2413,16 @@ However, it's not needed with smaller screen width because the doc/code block is
24052413
use hamburger button */
24062414
.src #sidebar-button > a::before, .sidebar-menu-toggle::before {
24072415
/* hamburger button image */
2408-
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
2409-
viewBox="0 0 22 22" fill="none" stroke="black">\
2410-
<path d="M3,5h16M3,11h16M3,17h16" stroke-width="2.75"/></svg>');
2416+
content: var(--hamburger-image);
24112417
opacity: 0.75;
2418+
filter: var(--mobile-sidebar-menu-filter);
24122419
}
24132420
.sidebar-menu-toggle:hover::before,
24142421
.sidebar-menu-toggle:active::before,
24152422
.sidebar-menu-toggle:focus::before {
24162423
opacity: 1;
24172424
}
24182425

2419-
/* src sidebar button opens a folder view */
2420-
.src #sidebar-button > a::before {
2421-
/* folder image */
2422-
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
2423-
viewBox="0 0 22 22" fill="none" stroke="black">\
2424-
<path d="M16,9v-4h-6v-1l-2,-2h-4l-2,2v16h13L21,9h-15L2,19" stroke-width="1.25"/>\
2425-
<path d="M15,7h-11v3" stroke-width="0.75"/>\
2426-
<path d="M3.75,10v1.25" stroke-width="0.375"/></svg>');
2427-
opacity: 0.75;
2428-
}
2429-
24302426
/* Media Queries */
24312427

24322428
/* Make sure all the buttons line wrap at the same time */
@@ -2611,9 +2607,6 @@ in src-script.js and main.js
26112607
width: 22px;
26122608
height: 22px;
26132609
}
2614-
.sidebar-menu-toggle::before {
2615-
filter: var(--mobile-sidebar-menu-filter);
2616-
}
26172610
.sidebar-menu-toggle:hover {
26182611
background: var(--main-background-color);
26192612
}
@@ -2671,6 +2664,14 @@ in src-script.js and main.js
26712664
margin: 0 0 -25px 0;
26722665
padding: var(--nav-sub-mobile-padding);
26732666
}
2667+
2668+
html:not(.src-sidebar-expanded) .src #sidebar-button > a {
2669+
background-color: var(--main-background-color);
2670+
}
2671+
html:not(.src-sidebar-expanded) .src #sidebar-button > a:hover,
2672+
html:not(.src-sidebar-expanded) .src #sidebar-button > a:focus-visible {
2673+
background-color: var(--sidebar-background-color);
2674+
}
26742675
}
26752676

26762677

tests/rustdoc-gui/sidebar.goml

+36
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,42 @@ assert-position-false: (".sidebar-crate > h2 > a", {"x": -3})
199199
drag-and-drop: ((205, 100), (108, 100))
200200
assert-position: (".sidebar-crate > h2 > a", {"x": -3})
201201

202+
// Check that the mobile sidebar and the source sidebar use the same icon.
203+
store-css: (".mobile-topbar .sidebar-menu-toggle::before", {"content": image_url})
204+
// Then we go to a source page.
205+
click: ".main-heading .src"
206+
assert-css: ("#sidebar-button a::before", {"content": |image_url|})
207+
// Check that hover events work as expected.
208+
store-css: ("#sidebar-button a", {"background-color": sidebar_background})
209+
move-cursor-to: "#sidebar-button a"
210+
store-css: ("#sidebar-button a:hover", {"background-color": sidebar_background_hover})
211+
assert: |sidebar_background| != |sidebar_background_hover|
212+
click: "#sidebar-button a"
213+
wait-for: "html.src-sidebar-expanded"
214+
assert-css: ("#sidebar-button a:hover", {"background-color": |sidebar_background_hover|})
215+
move-cursor-to: "#settings-menu"
216+
assert-css: ("#sidebar-button a:not(:hover)", {"background-color": |sidebar_background|})
217+
// Closing sidebar.
218+
click: "#sidebar-button a"
219+
wait-for: "html:not(.src-sidebar-expanded)"
220+
// Now we check the same when the sidebar button is moved alongside the search.
221+
set-window-size: (500, 500)
222+
store-css: ("#sidebar-button a:hover", {"background-color": not_sidebar_background_hover})
223+
move-cursor-to: "#settings-menu"
224+
store-css: ("#sidebar-button a:not(:hover)", {"background-color": not_sidebar_background})
225+
// The sidebar background is supposed to be the same as the main background.
226+
assert-css: ("body", {"background-color": |not_sidebar_background|})
227+
assert: |not_sidebar_background| != |not_sidebar_background_hover| && |not_sidebar_background| != |sidebar_background|
228+
// The hover background is supposed to be the same as the sidebar background.
229+
assert: |not_sidebar_background_hover| == |sidebar_background|
230+
click: "#sidebar-button a"
231+
wait-for: "html.src-sidebar-expanded"
232+
// And now the background colors are supposed to be the same as the sidebar since the sidebar has
233+
// been open.
234+
assert-css: ("#sidebar-button a:hover", {"background-color": |sidebar_background_hover|})
235+
move-cursor-to: "h2"
236+
assert-css: ("#sidebar-button a:not(:hover)", {"background-color": |sidebar_background|})
237+
202238
// Configuration option to show TOC in sidebar.
203239
set-local-storage: {"rustdoc-hide-toc": "true"}
204240
go-to: "file://" + |DOC_PATH| + "/test_docs/enum.WhoLetTheDogOut.html"

0 commit comments

Comments
 (0)