Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f77bfb7

Browse files
committedMar 1, 2023
Auto merge of #108620 - Dylan-DPC:rollup-o5c4evy, r=Dylan-DPC
Rollup of 7 pull requests Successful merges: - #108143 (rustdoc: search by macro when query ends with `!`) - #108394 (Make `x doc --open` work on every book) - #108427 (Recover from for-else and while-else) - #108462 (Fix `VecDeque::append` capacity overflow for ZSTs) - #108568 (Make associated_item_def_ids for traits use an unstable option to also return associated types for RPITITs) - #108604 (Add regression test for #107280) - #108605 (Add regression test for #105821) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents 609496e + 02e4eef commit f77bfb7

40 files changed

+642
-72
lines changed
 

‎compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,9 +1101,18 @@ fn should_encode_const(def_kind: DefKind) -> bool {
11011101
}
11021102
}
11031103

1104-
// Return `false` to avoid encoding impl trait in trait, while we don't use the query.
1105-
fn should_encode_fn_impl_trait_in_trait<'tcx>(_tcx: TyCtxt<'tcx>, _def_id: DefId) -> bool {
1106-
false
1104+
// We only encode impl trait in trait when using `lower-impl-trait-in-trait-to-assoc-ty` unstable
1105+
// option.
1106+
fn should_encode_fn_impl_trait_in_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
1107+
if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
1108+
&& let Some(assoc_item) = tcx.opt_associated_item(def_id)
1109+
&& assoc_item.container == ty::AssocItemContainer::TraitContainer
1110+
&& assoc_item.kind == ty::AssocKind::Fn
1111+
{
1112+
true
1113+
} else {
1114+
false
1115+
}
11071116
}
11081117

11091118
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {

‎compiler/rustc_middle/src/hir/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ pub fn provide(providers: &mut Providers) {
177177
}
178178
};
179179
providers.opt_def_kind = |tcx, def_id| tcx.hir().opt_def_kind(def_id.expect_local());
180+
providers.opt_rpitit_info = |_, _| None;
180181
providers.all_local_trait_impls = |tcx, ()| &tcx.resolutions(()).trait_impls;
181182
providers.expn_that_defined = |tcx, id| {
182183
let id = id.expect_local();

‎compiler/rustc_middle/src/query/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ rustc_queries! {
9090
/// Definitions that were generated with no HIR, would be feeded to return `None`.
9191
query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option<hir::HirId>{
9292
desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key.to_def_id()) }
93+
feedable
9394
}
9495

9596
/// Gives access to the HIR node's parent for the HIR owner `key`.
@@ -166,6 +167,7 @@ rustc_queries! {
166167
}
167168
cache_on_disk_if { key.is_local() }
168169
separate_provide_extern
170+
feedable
169171
}
170172

171173
query collect_return_position_impl_trait_in_trait_tys(key: DefId)
@@ -222,6 +224,7 @@ rustc_queries! {
222224
arena_cache
223225
cache_on_disk_if { key.is_local() }
224226
separate_provide_extern
227+
feedable
225228
}
226229

227230
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to the
@@ -264,6 +267,7 @@ rustc_queries! {
264267
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
265268
cache_on_disk_if { key.is_local() }
266269
separate_provide_extern
270+
feedable
267271
}
268272

269273
/// Elaborated version of the predicates from `explicit_item_bounds`.
@@ -588,6 +592,7 @@ rustc_queries! {
588592
desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) }
589593
cache_on_disk_if { key.is_local() }
590594
separate_provide_extern
595+
feedable
591596
}
592597

593598
/// Returns the inferred outlives predicates (e.g., for `struct
@@ -596,6 +601,7 @@ rustc_queries! {
596601
desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) }
597602
cache_on_disk_if { key.is_local() }
598603
separate_provide_extern
604+
feedable
599605
}
600606

601607
/// Maps from the `DefId` of a trait to the list of
@@ -728,6 +734,7 @@ rustc_queries! {
728734
desc { |tcx| "computing associated item data for `{}`", tcx.def_path_str(key) }
729735
cache_on_disk_if { key.is_local() }
730736
separate_provide_extern
737+
feedable
731738
}
732739

733740
/// Collects the associated items defined on a trait or impl.
@@ -1142,6 +1149,15 @@ rustc_queries! {
11421149
desc { |tcx| "looking up definition kind of `{}`", tcx.def_path_str(def_id) }
11431150
cache_on_disk_if { def_id.is_local() }
11441151
separate_provide_extern
1152+
feedable
1153+
}
1154+
1155+
/// The `opt_rpitit_info` query returns the pair of the def id of the function where the RPIT
1156+
/// is defined and the opaque def id if any.
1157+
query opt_rpitit_info(def_id: DefId) -> Option<ty::ImplTraitInTraitData> {
1158+
desc { |tcx| "opt_rpitit_info `{}`", tcx.def_path_str(def_id) }
1159+
cache_on_disk_if { def_id.is_local() }
1160+
feedable
11451161
}
11461162

11471163
/// Gets the span for the definition.
@@ -1157,6 +1173,7 @@ rustc_queries! {
11571173
desc { |tcx| "looking up span for `{}`'s identifier", tcx.def_path_str(def_id) }
11581174
cache_on_disk_if { def_id.is_local() }
11591175
separate_provide_extern
1176+
feedable
11601177
}
11611178

11621179
query lookup_stability(def_id: DefId) -> Option<attr::Stability> {
@@ -1498,6 +1515,7 @@ rustc_queries! {
14981515
desc { |tcx| "looking up whether `{}` is a default impl", tcx.def_path_str(def_id) }
14991516
cache_on_disk_if { def_id.is_local() }
15001517
separate_provide_extern
1518+
feedable
15011519
}
15021520

15031521
query check_well_formed(key: hir::OwnerId) -> () {
@@ -1695,6 +1713,7 @@ rustc_queries! {
16951713
query visibility(def_id: DefId) -> ty::Visibility<DefId> {
16961714
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
16971715
separate_provide_extern
1716+
feedable
16981717
}
16991718

17001719
query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {

‎compiler/rustc_middle/src/ty/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2071,6 +2071,12 @@ pub enum ImplOverlapKind {
20712071
Issue33140,
20722072
}
20732073

2074+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
2075+
pub enum ImplTraitInTraitData {
2076+
Trait { fn_def_id: DefId, opaque_def_id: DefId },
2077+
Impl { fn_def_id: DefId },
2078+
}
2079+
20742080
impl<'tcx> TyCtxt<'tcx> {
20752081
pub fn typeck_body(self, body: hir::BodyId) -> &'tcx TypeckResults<'tcx> {
20762082
self.typeck(self.hir().body_owner_def_id(body))

‎compiler/rustc_parse/locales/en-US.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ parse_missing_in_in_for_loop = missing `in` in `for` loop
151151
parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
152152
.suggestion = try adding an expression to the `for` loop
153153
154+
parse_loop_else = `{$loop_kind}...else` loops are not supported
155+
.note = consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
156+
.loop_keyword = `else` is attached to this loop
157+
154158
parse_missing_comma_after_match_arm = expected `,` following `match` arm
155159
.suggestion = missing a comma here to end this `match` arm
156160

‎compiler/rustc_parse/src/errors.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,17 @@ pub(crate) struct MissingExpressionInForLoop {
451451
pub span: Span,
452452
}
453453

454+
#[derive(Diagnostic)]
455+
#[diag(parse_loop_else)]
456+
#[note]
457+
pub(crate) struct LoopElseNotSupported {
458+
#[primary_span]
459+
pub span: Span,
460+
pub loop_kind: &'static str,
461+
#[label(parse_loop_keyword)]
462+
pub loop_kw: Span,
463+
}
464+
454465
#[derive(Diagnostic)]
455466
#[diag(parse_missing_comma_after_match_arm)]
456467
pub(crate) struct MissingCommaAfterMatchArm {

‎compiler/rustc_parse/src/parser/expr.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2503,9 +2503,27 @@ impl<'a> Parser<'a> {
25032503
let (attrs, loop_block) = self.parse_inner_attrs_and_block()?;
25042504

25052505
let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
2506+
2507+
self.recover_loop_else("for", lo)?;
2508+
25062509
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
25072510
}
25082511

2512+
/// Recovers from an `else` clause after a loop (`for...else`, `while...else`)
2513+
fn recover_loop_else(&mut self, loop_kind: &'static str, loop_kw: Span) -> PResult<'a, ()> {
2514+
if self.token.is_keyword(kw::Else) && self.may_recover() {
2515+
let else_span = self.token.span;
2516+
self.bump();
2517+
let else_clause = self.parse_expr_else()?;
2518+
self.sess.emit_err(errors::LoopElseNotSupported {
2519+
span: else_span.to(else_clause.span),
2520+
loop_kind,
2521+
loop_kw,
2522+
});
2523+
}
2524+
Ok(())
2525+
}
2526+
25092527
fn error_missing_in_for_loop(&mut self) {
25102528
let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) {
25112529
// Possibly using JS syntax (#75311).
@@ -2530,6 +2548,9 @@ impl<'a> Parser<'a> {
25302548
err.span_label(cond.span, "this `while` condition successfully parsed");
25312549
err
25322550
})?;
2551+
2552+
self.recover_loop_else("while", lo)?;
2553+
25332554
Ok(self.mk_expr_with_attrs(
25342555
lo.to(self.prev_token.span),
25352556
ExprKind::While(cond, body, opt_label),
@@ -2541,6 +2562,7 @@ impl<'a> Parser<'a> {
25412562
fn parse_expr_loop(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
25422563
let loop_span = self.prev_token.span;
25432564
let (attrs, body) = self.parse_inner_attrs_and_block()?;
2565+
self.recover_loop_else("loop", lo)?;
25442566
Ok(self.mk_expr_with_attrs(
25452567
lo.to(self.prev_token.span),
25462568
ExprKind::Loop(body, opt_label, loop_span),

‎compiler/rustc_session/src/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,9 @@ options! {
15031503
"what location details should be tracked when using caller_location, either \
15041504
`none`, or a comma separated list of location details, for which \
15051505
valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
1506+
lower_impl_trait_in_trait_to_assoc_ty: bool = (false, parse_bool, [TRACKED],
1507+
"modify the lowering strategy for `impl Trait` in traits so that they are lowered to \
1508+
generic associated types"),
15061509
ls: bool = (false, parse_bool, [UNTRACKED],
15071510
"list the symbols defined by a library crate (default: no)"),
15081511
macro_backtrace: bool = (false, parse_bool, [UNTRACKED],

‎compiler/rustc_ty_utils/src/assoc.rs

Lines changed: 90 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use rustc_hir::def::DefKind;
44
use rustc_hir::def_id::{DefId, LocalDefId};
55
use rustc_hir::definitions::DefPathData;
66
use rustc_hir::intravisit::{self, Visitor};
7-
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
7+
use rustc_middle::ty::{self, DefIdTree, ImplTraitInTraitData, InternalSubsts, TyCtxt};
8+
use rustc_span::symbol::kw;
89

910
pub fn provide(providers: &mut ty::query::Providers) {
1011
*providers = ty::query::Providers {
@@ -21,9 +22,37 @@ pub fn provide(providers: &mut ty::query::Providers) {
2122
fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
2223
let item = tcx.hir().expect_item(def_id.expect_local());
2324
match item.kind {
24-
hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter(
25-
trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()),
26-
),
25+
hir::ItemKind::Trait(.., ref trait_item_refs) => {
26+
if tcx.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty {
27+
// We collect RPITITs for each trait method's return type and create a
28+
// corresponding associated item using associated_items_for_impl_trait_in_trait
29+
// query.
30+
tcx.arena.alloc_from_iter(
31+
trait_item_refs
32+
.iter()
33+
.map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id())
34+
.chain(
35+
trait_item_refs
36+
.iter()
37+
.filter(|trait_item_ref| {
38+
matches!(trait_item_ref.kind, hir::AssocItemKind::Fn { .. })
39+
})
40+
.flat_map(|trait_item_ref| {
41+
let trait_fn_def_id =
42+
trait_item_ref.id.owner_id.def_id.to_def_id();
43+
tcx.associated_items_for_impl_trait_in_trait(trait_fn_def_id)
44+
})
45+
.map(|def_id| *def_id),
46+
),
47+
)
48+
} else {
49+
tcx.arena.alloc_from_iter(
50+
trait_item_refs
51+
.iter()
52+
.map(|trait_item_ref| trait_item_ref.id.owner_id.to_def_id()),
53+
)
54+
}
55+
}
2756
hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter(
2857
impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()),
2958
),
@@ -193,10 +222,65 @@ fn associated_item_for_impl_trait_in_trait(
193222
let span = tcx.def_span(opaque_ty_def_id);
194223
let trait_assoc_ty =
195224
tcx.at(span).create_def(trait_def_id.expect_local(), DefPathData::ImplTraitAssocTy);
196-
trait_assoc_ty.def_id()
225+
226+
let local_def_id = trait_assoc_ty.def_id();
227+
let def_id = local_def_id.to_def_id();
228+
229+
trait_assoc_ty.opt_def_kind(Some(DefKind::AssocTy));
230+
231+
// There's no HIR associated with this new synthesized `def_id`, so feed
232+
// `opt_local_def_id_to_hir_id` with `None`.
233+
trait_assoc_ty.opt_local_def_id_to_hir_id(None);
234+
235+
// Copy span of the opaque.
236+
trait_assoc_ty.def_ident_span(Some(span));
237+
238+
// Add the def_id of the function and opaque that generated this synthesized associated type.
239+
trait_assoc_ty.opt_rpitit_info(Some(ImplTraitInTraitData::Trait {
240+
fn_def_id,
241+
opaque_def_id: opaque_ty_def_id.to_def_id(),
242+
}));
243+
244+
trait_assoc_ty.associated_item(ty::AssocItem {
245+
name: kw::Empty,
246+
kind: ty::AssocKind::Type,
247+
def_id,
248+
trait_item_def_id: None,
249+
container: ty::TraitContainer,
250+
fn_has_self_parameter: false,
251+
});
252+
253+
// Copy visility of the containing function.
254+
trait_assoc_ty.visibility(tcx.visibility(fn_def_id));
255+
256+
// Copy impl_defaultness of the containing function.
257+
trait_assoc_ty.impl_defaultness(tcx.impl_defaultness(fn_def_id));
258+
259+
// Copy type_of of the opaque.
260+
trait_assoc_ty.type_of(ty::EarlyBinder(tcx.mk_opaque(
261+
opaque_ty_def_id.to_def_id(),
262+
InternalSubsts::identity_for_item(tcx, opaque_ty_def_id.to_def_id()),
263+
)));
264+
265+
// Copy generics_of of the opaque.
266+
trait_assoc_ty.generics_of(tcx.generics_of(opaque_ty_def_id).clone());
267+
268+
// There are no predicates for the synthesized associated type.
269+
trait_assoc_ty.explicit_predicates_of(ty::GenericPredicates {
270+
parent: Some(trait_def_id),
271+
predicates: &[],
272+
});
273+
274+
// There are no inferred outlives for the synthesized associated type.
275+
trait_assoc_ty.inferred_outlives_of(&[]);
276+
277+
// FIXME implement this.
278+
trait_assoc_ty.explicit_item_bounds(&[]);
279+
280+
local_def_id
197281
}
198282

199-
/// Given an `trait_assoc_def_id` that corresponds to a previously synthethized impl trait in trait
283+
/// Given an `trait_assoc_def_id` that corresponds to a previously synthesized impl trait in trait
200284
/// into an associated type and an `impl_def_id` corresponding to an impl block, create and return
201285
/// the corresponding associated item inside the impl block.
202286
fn impl_associated_item_for_impl_trait_in_trait(

‎library/alloc/src/collections/vec_deque/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1924,7 +1924,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
19241924
#[stable(feature = "append", since = "1.4.0")]
19251925
pub fn append(&mut self, other: &mut Self) {
19261926
if T::IS_ZST {
1927-
self.len += other.len;
1927+
self.len = self.len.checked_add(other.len).expect("capacity overflow");
19281928
other.len = 0;
19291929
other.head = 0;
19301930
return;

‎library/alloc/tests/vec_deque.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,20 @@ fn test_append_double_drop() {
10451045
assert_eq!(count_b, 1);
10461046
}
10471047

1048+
#[test]
1049+
#[should_panic]
1050+
fn test_append_zst_capacity_overflow() {
1051+
let mut v = Vec::with_capacity(usize::MAX);
1052+
// note: using resize instead of set_len here would
1053+
// be *extremely* slow in unoptimized builds.
1054+
// SAFETY: `v` has capacity `usize::MAX`, and no initialization
1055+
// is needed for empty tuples.
1056+
unsafe { v.set_len(usize::MAX) };
1057+
let mut v = VecDeque::from(v);
1058+
let mut w = vec![()].into();
1059+
v.append(&mut w);
1060+
}
1061+
10481062
#[test]
10491063
fn test_retain() {
10501064
let mut buf = VecDeque::new();

‎src/bootstrap/doc.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ macro_rules! book {
6262
target: self.target,
6363
name: INTERNER.intern_str($book_name),
6464
src: INTERNER.intern_path(builder.src.join($path)),
65+
parent: Some(self),
6566
})
6667
}
6768
}
@@ -119,18 +120,20 @@ impl Step for UnstableBook {
119120
target: self.target,
120121
name: INTERNER.intern_str("unstable-book"),
121122
src: INTERNER.intern_path(builder.md_doc_out(self.target).join("unstable-book")),
123+
parent: Some(self),
122124
})
123125
}
124126
}
125127

126128
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
127-
struct RustbookSrc {
129+
struct RustbookSrc<P: Step> {
128130
target: TargetSelection,
129131
name: Interned<String>,
130132
src: Interned<PathBuf>,
133+
parent: Option<P>,
131134
}
132135

133-
impl Step for RustbookSrc {
136+
impl<P: Step> Step for RustbookSrc<P> {
134137
type Output = ();
135138

136139
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -152,13 +155,18 @@ impl Step for RustbookSrc {
152155
let index = out.join("index.html");
153156
let rustbook = builder.tool_exe(Tool::Rustbook);
154157
let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
155-
if builder.config.dry_run() || up_to_date(&src, &index) && up_to_date(&rustbook, &index) {
156-
return;
158+
159+
if !builder.config.dry_run() && !(up_to_date(&src, &index) || up_to_date(&rustbook, &index))
160+
{
161+
builder.info(&format!("Rustbook ({}) - {}", target, name));
162+
let _ = fs::remove_dir_all(&out);
163+
164+
builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
157165
}
158-
builder.info(&format!("Rustbook ({}) - {}", target, name));
159-
let _ = fs::remove_dir_all(&out);
160166

161-
builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(out));
167+
if self.parent.is_some() {
168+
builder.maybe_open_in_browser::<P>(index)
169+
}
162170
}
163171
}
164172

@@ -205,6 +213,7 @@ impl Step for TheBook {
205213
target,
206214
name: INTERNER.intern_str("book"),
207215
src: INTERNER.intern_path(builder.src.join(&relative_path)),
216+
parent: Some(self),
208217
});
209218

210219
// building older edition redirects
@@ -213,6 +222,9 @@ impl Step for TheBook {
213222
target,
214223
name: INTERNER.intern_string(format!("book/{}", edition)),
215224
src: INTERNER.intern_path(builder.src.join(&relative_path).join(edition)),
225+
// There should only be one book that is marked as the parent for each target, so
226+
// treat the other editions as not having a parent.
227+
parent: Option::<Self>::None,
216228
});
217229
}
218230

@@ -228,10 +240,6 @@ impl Step for TheBook {
228240

229241
invoke_rustdoc(builder, compiler, &shared_assets, target, path);
230242
}
231-
232-
let out = builder.doc_out(target);
233-
let index = out.join("book").join("index.html");
234-
builder.maybe_open_in_browser::<Self>(index);
235243
}
236244
}
237245

@@ -1032,10 +1040,7 @@ impl Step for RustcBook {
10321040
target: self.target,
10331041
name: INTERNER.intern_str("rustc"),
10341042
src: INTERNER.intern_path(out_base),
1043+
parent: Some(self),
10351044
});
1036-
1037-
let out = builder.doc_out(self.target);
1038-
let index = out.join("rustc").join("index.html");
1039-
builder.maybe_open_in_browser::<Self>(index);
10401045
}
10411046
}

‎src/doc/rustdoc/src/how-to-read-rustdoc.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ When typing in the search bar, you can prefix your search term with a type
8484
followed by a colon (such as `mod:`) to restrict the results to just that
8585
kind of item. (The available items are listed in the help popup.)
8686

87+
Searching for `println!` will search for a macro named `println`, just like
88+
searching for `macro:println` does.
89+
8790
### Changing displayed theme
8891

8992
You can change the displayed theme by opening the settings menu (the gear

‎src/librustdoc/html/static/js/search.js

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,20 +300,21 @@ function initSearch(rawSearchIndex) {
300300
* @return {integer}
301301
*/
302302
function getIdentEndPosition(parserState) {
303+
const start = parserState.pos;
303304
let end = parserState.pos;
304-
let foundExclamation = false;
305+
let foundExclamation = -1;
305306
while (parserState.pos < parserState.length) {
306307
const c = parserState.userQuery[parserState.pos];
307308
if (!isIdentCharacter(c)) {
308309
if (c === "!") {
309-
if (foundExclamation) {
310+
if (foundExclamation !== -1) {
310311
throw new Error("Cannot have more than one `!` in an ident");
311312
} else if (parserState.pos + 1 < parserState.length &&
312313
isIdentCharacter(parserState.userQuery[parserState.pos + 1])
313314
) {
314315
throw new Error("`!` can only be at the end of an ident");
315316
}
316-
foundExclamation = true;
317+
foundExclamation = parserState.pos;
317318
} else if (isErrorCharacter(c)) {
318319
throw new Error(`Unexpected \`${c}\``);
319320
} else if (
@@ -326,16 +327,35 @@ function initSearch(rawSearchIndex) {
326327
if (!isPathStart(parserState)) {
327328
break;
328329
}
330+
if (foundExclamation !== -1) {
331+
if (start <= (end - 2)) {
332+
throw new Error("Cannot have associated items in macros");
333+
} else {
334+
// if start == end - 1, we got the never type
335+
// while the never type has no associated macros, we still
336+
// can parse a path like that
337+
foundExclamation = -1;
338+
}
339+
}
329340
// Skip current ":".
330341
parserState.pos += 1;
331-
foundExclamation = false;
332342
} else {
333343
throw new Error(`Unexpected \`${c}\``);
334344
}
335345
}
336346
parserState.pos += 1;
337347
end = parserState.pos;
338348
}
349+
// if start == end - 1, we got the never type
350+
if (foundExclamation !== -1 && start <= (end - 2)) {
351+
if (parserState.typeFilter === null) {
352+
parserState.typeFilter = "macro";
353+
} else if (parserState.typeFilter !== "macro") {
354+
throw new Error("Invalid search type: macro `!` and " +
355+
`\`${parserState.typeFilter}\` both specified`);
356+
}
357+
end = foundExclamation;
358+
}
339359
return end;
340360
}
341361

@@ -589,8 +609,8 @@ function initSearch(rawSearchIndex) {
589609
*
590610
* The supported syntax by this parser is as follow:
591611
*
592-
* ident = *(ALPHA / DIGIT / "_") [!]
593-
* path = ident *(DOUBLE-COLON ident)
612+
* ident = *(ALPHA / DIGIT / "_")
613+
* path = ident *(DOUBLE-COLON ident) [!]
594614
* arg = path [generics]
595615
* arg-without-generic = path
596616
* type-sep = COMMA/WS *(COMMA/WS)

‎tests/rustdoc-js-std/parser-errors.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ const QUERY = [
3737
"mod : :",
3838
"a!a",
3939
"a!!",
40+
"mod:a!",
41+
"a!::a",
4042
];
4143

4244
const PARSED = [
@@ -382,4 +384,22 @@ const PARSED = [
382384
userQuery: "a!!",
383385
error: 'Cannot have more than one `!` in an ident',
384386
},
387+
{
388+
elems: [],
389+
foundElems: 0,
390+
original: "mod:a!",
391+
returned: [],
392+
typeFilter: -1,
393+
userQuery: "mod:a!",
394+
error: 'Invalid search type: macro `!` and `mod` both specified',
395+
},
396+
{
397+
elems: [],
398+
foundElems: 0,
399+
original: "a!::a",
400+
returned: [],
401+
typeFilter: -1,
402+
userQuery: "a!::a",
403+
error: 'Cannot have associated items in macros',
404+
},
385405
];

‎tests/rustdoc-js-std/parser-filter.js

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo'];
1+
const QUERY = ['fn:foo', 'enum : foo', 'macro<f>:foo', 'macro!', 'macro:mac!', 'a::mac!'];
22

33
const PARSED = [
44
{
@@ -40,4 +40,49 @@ const PARSED = [
4040
userQuery: "macro<f>:foo",
4141
error: "Unexpected `:`",
4242
},
43+
{
44+
elems: [{
45+
name: "macro",
46+
fullPath: ["macro"],
47+
pathWithoutLast: [],
48+
pathLast: "macro",
49+
generics: [],
50+
}],
51+
foundElems: 1,
52+
original: "macro!",
53+
returned: [],
54+
typeFilter: 14,
55+
userQuery: "macro!",
56+
error: null,
57+
},
58+
{
59+
elems: [{
60+
name: "mac",
61+
fullPath: ["mac"],
62+
pathWithoutLast: [],
63+
pathLast: "mac",
64+
generics: [],
65+
}],
66+
foundElems: 1,
67+
original: "macro:mac!",
68+
returned: [],
69+
typeFilter: 14,
70+
userQuery: "macro:mac!",
71+
error: null,
72+
},
73+
{
74+
elems: [{
75+
name: "a::mac",
76+
fullPath: ["a", "mac"],
77+
pathWithoutLast: ["a"],
78+
pathLast: "mac",
79+
generics: [],
80+
}],
81+
foundElems: 1,
82+
original: "a::mac!",
83+
returned: [],
84+
typeFilter: 14,
85+
userQuery: "a::mac!",
86+
error: null,
87+
},
4388
];

‎tests/rustdoc-js-std/parser-ident.js

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const QUERY = [
33
"!",
44
"a!",
55
"a!::b",
6+
"!::b",
67
"a!::b!",
78
];
89

@@ -47,47 +48,50 @@ const PARSED = [
4748
},
4849
{
4950
elems: [{
50-
name: "a!",
51-
fullPath: ["a!"],
51+
name: "a",
52+
fullPath: ["a"],
5253
pathWithoutLast: [],
53-
pathLast: "a!",
54+
pathLast: "a",
5455
generics: [],
5556
}],
5657
foundElems: 1,
5758
original: "a!",
5859
returned: [],
59-
typeFilter: -1,
60+
typeFilter: 14,
6061
userQuery: "a!",
6162
error: null,
6263
},
6364
{
64-
elems: [{
65-
name: "a!::b",
66-
fullPath: ["a!", "b"],
67-
pathWithoutLast: ["a!"],
68-
pathLast: "b",
69-
generics: [],
70-
}],
71-
foundElems: 1,
65+
elems: [],
66+
foundElems: 0,
7267
original: "a!::b",
7368
returned: [],
7469
typeFilter: -1,
7570
userQuery: "a!::b",
76-
error: null,
71+
error: "Cannot have associated items in macros",
7772
},
7873
{
7974
elems: [{
80-
name: "a!::b!",
81-
fullPath: ["a!", "b!"],
82-
pathWithoutLast: ["a!"],
83-
pathLast: "b!",
75+
name: "!::b",
76+
fullPath: ["!", "b"],
77+
pathWithoutLast: ["!"],
78+
pathLast: "b",
8479
generics: [],
8580
}],
8681
foundElems: 1,
82+
original: "!::b",
83+
returned: [],
84+
typeFilter: -1,
85+
userQuery: "!::b",
86+
error: null,
87+
},
88+
{
89+
elems: [],
90+
foundElems: 0,
8791
original: "a!::b!",
8892
returned: [],
8993
typeFilter: -1,
9094
userQuery: "a!::b!",
91-
error: null,
95+
error: "Cannot have associated items in macros",
9296
},
9397
];

‎tests/rustdoc-js/macro-search.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// exact-check
2+
3+
const QUERY = 'abracadabra!';
4+
5+
const EXPECTED = {
6+
'others': [
7+
{ 'path': 'macro_search', 'name': 'abracadabra' },
8+
{ 'path': 'macro_search', 'name': 'abracadabra_b' },
9+
],
10+
};

‎tests/rustdoc-js/macro-search.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#[macro_export]
2+
macro_rules! abracadabra {
3+
() => {}
4+
}
5+
#[macro_export]
6+
macro_rules! abracadabra_b {
7+
() => {}
8+
}
9+
pub fn abracadabra() {}
10+
pub fn abracadabra_c() {}

‎tests/rustdoc-ui/z-help.stdout

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
-Z llvm-plugins=val -- a list LLVM plugins to enable (space separated)
8888
-Z llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no)
8989
-Z location-detail=val -- what location details should be tracked when using caller_location, either `none`, or a comma separated list of location details, for which valid options are `file`, `line`, and `column` (default: `file,line,column`)
90+
-Z lower-impl-trait-in-trait-to-assoc-ty=val -- modify the lowering strategy for `impl Trait` in traits so that they are lowered to generic associated types
9091
-Z ls=val -- list the symbols defined by a library crate (default: no)
9192
-Z macro-backtrace=val -- show macro backtraces (default: no)
9293
-Z maximal-hir-to-mir-coverage=val -- save as much information as possible about the correspondence between MIR and HIR as source scopes (default: no)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// edition:2021
2+
3+
async fn foo() {
4+
inner::<false>().await
5+
//~^ ERROR: function takes 2 generic arguments but 1 generic argument was supplied
6+
//~| ERROR: type inside `async fn` body must be known in this context
7+
//~| ERROR: type inside `async fn` body must be known in this context
8+
//~| ERROR: type inside `async fn` body must be known in this context
9+
//~| ERROR: type inside `async fn` body must be known in this context
10+
//~| ERROR: type inside `async fn` body must be known in this context
11+
}
12+
13+
async fn inner<T, const PING: bool>() {}
14+
15+
fn main() {}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
error[E0107]: function takes 2 generic arguments but 1 generic argument was supplied
2+
--> $DIR/issue-107280.rs:4:5
3+
|
4+
LL | inner::<false>().await
5+
| ^^^^^ ----- supplied 1 generic argument
6+
| |
7+
| expected 2 generic arguments
8+
|
9+
note: function defined here, with 2 generic parameters: `T`, `PING`
10+
--> $DIR/issue-107280.rs:13:10
11+
|
12+
LL | async fn inner<T, const PING: bool>() {}
13+
| ^^^^^ - ----------------
14+
help: add missing generic argument
15+
|
16+
LL | inner::<false, PING>().await
17+
| ++++++
18+
19+
error[E0698]: type inside `async fn` body must be known in this context
20+
--> $DIR/issue-107280.rs:4:5
21+
|
22+
LL | inner::<false>().await
23+
| ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner`
24+
|
25+
note: the type is part of the `async fn` body because of this `await`
26+
--> $DIR/issue-107280.rs:4:21
27+
|
28+
LL | inner::<false>().await
29+
| ^^^^^^
30+
31+
error[E0698]: type inside `async fn` body must be known in this context
32+
--> $DIR/issue-107280.rs:4:5
33+
|
34+
LL | inner::<false>().await
35+
| ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner`
36+
|
37+
note: the type is part of the `async fn` body because of this `await`
38+
--> $DIR/issue-107280.rs:4:21
39+
|
40+
LL | inner::<false>().await
41+
| ^^^^^^
42+
43+
error[E0698]: type inside `async fn` body must be known in this context
44+
--> $DIR/issue-107280.rs:4:5
45+
|
46+
LL | inner::<false>().await
47+
| ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner`
48+
|
49+
note: the type is part of the `async fn` body because of this `await`
50+
--> $DIR/issue-107280.rs:4:21
51+
|
52+
LL | inner::<false>().await
53+
| ^^^^^^
54+
55+
error[E0698]: type inside `async fn` body must be known in this context
56+
--> $DIR/issue-107280.rs:4:5
57+
|
58+
LL | inner::<false>().await
59+
| ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner`
60+
|
61+
note: the type is part of the `async fn` body because of this `await`
62+
--> $DIR/issue-107280.rs:4:21
63+
|
64+
LL | inner::<false>().await
65+
| ^^^^^^
66+
67+
error[E0698]: type inside `async fn` body must be known in this context
68+
--> $DIR/issue-107280.rs:4:5
69+
|
70+
LL | inner::<false>().await
71+
| ^^^^^^^^^^^^^^ cannot infer the value of const parameter `PING` declared on the function `inner`
72+
|
73+
note: the type is part of the `async fn` body because of this `await`
74+
--> $DIR/issue-107280.rs:4:21
75+
|
76+
LL | inner::<false>().await
77+
| ^^^^^^
78+
79+
error: aborting due to 6 previous errors
80+
81+
Some errors have detailed explanations: E0107, E0698.
82+
For more information about an error, try `rustc --explain E0107`.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// check-pass
2+
3+
#![allow(incomplete_features)]
4+
#![feature(adt_const_params, const_ptr_read, generic_const_exprs)]
5+
#![allow(dead_code)]
6+
7+
const fn catone<const M: usize>(_a: &[u8; M]) -> [u8; M + 1]
8+
where
9+
[(); M + 1]:,
10+
{
11+
unimplemented!()
12+
}
13+
14+
struct Catter<const A: &'static [u8]>;
15+
impl<const A: &'static [u8]> Catter<A>
16+
where
17+
[(); A.len() + 1]:,
18+
{
19+
const ZEROS: &'static [u8; A.len()] = &[0_u8; A.len()];
20+
const R: &'static [u8] = &catone(Self::ZEROS);
21+
}
22+
23+
fn main() {}

‎tests/ui/for/for-else-err.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
for _ in 0..1 {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `for...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
}
8+
}

‎tests/ui/for/for-else-err.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `for...else` loops are not supported
2+
--> $DIR/for-else-err.rs:4:7
3+
|
4+
LL | for _ in 0..1 {
5+
| --- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | }
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+

‎tests/ui/for/for-else-let-else-err.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
let _ = for _ in 0..1 {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `for...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
};
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `for...else` loops are not supported
2+
--> $DIR/for-else-let-else-err.rs:4:7
3+
|
4+
LL | let _ = for _ in 0..1 {
5+
| --- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | };
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+

‎tests/ui/let-else/let-else-brace-before-else.fixed

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ fn main() {
77
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
88
return;
99
};
10-
let Some(1) = (loop { break Some(1) }) else {
11-
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
12-
return;
13-
};
1410
let 2 = 1 + (match 1 { n => n }) else {
1511
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
1612
return;

‎tests/ui/let-else/let-else-brace-before-else.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ fn main() {
77
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
88
return;
99
};
10-
let Some(1) = loop { break Some(1) } else {
11-
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
12-
return;
13-
};
1410
let 2 = 1 + match 1 { n => n } else {
1511
//~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed
1612
return;

‎tests/ui/let-else/let-else-brace-before-else.stderr

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,7 @@ LL | let Some(1) = ({ Some(1) }) else {
1010
| + +
1111

1212
error: right curly brace `}` before `else` in a `let...else` statement not allowed
13-
--> $DIR/let-else-brace-before-else.rs:10:40
14-
|
15-
LL | let Some(1) = loop { break Some(1) } else {
16-
| ^
17-
|
18-
help: wrap the expression in parentheses
19-
|
20-
LL | let Some(1) = (loop { break Some(1) }) else {
21-
| + +
22-
23-
error: right curly brace `}` before `else` in a `let...else` statement not allowed
24-
--> $DIR/let-else-brace-before-else.rs:14:34
13+
--> $DIR/let-else-brace-before-else.rs:10:34
2514
|
2615
LL | let 2 = 1 + match 1 { n => n } else {
2716
| ^
@@ -32,7 +21,7 @@ LL | let 2 = 1 + (match 1 { n => n }) else {
3221
| + +
3322

3423
error: right curly brace `}` before `else` in a `let...else` statement not allowed
35-
--> $DIR/let-else-brace-before-else.rs:18:40
24+
--> $DIR/let-else-brace-before-else.rs:14:40
3625
|
3726
LL | let Some(1) = unsafe { unsafe_fn() } else {
3827
| ^
@@ -42,5 +31,5 @@ help: wrap the expression in parentheses
4231
LL | let Some(1) = (unsafe { unsafe_fn() }) else {
4332
| + +
4433

45-
error: aborting due to 4 previous errors
34+
error: aborting due to 3 previous errors
4635

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
fn main() {
2+
let Some(1) = loop {
3+
//~^ NOTE `else` is attached to this loop
4+
break Some(1)
5+
} else {
6+
//~^ ERROR `loop...else` loops are not supported
7+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
8+
return;
9+
};
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: `loop...else` loops are not supported
2+
--> $DIR/loop-else-break-with-value.rs:5:7
3+
|
4+
LL | let Some(1) = loop {
5+
| ---- `else` is attached to this loop
6+
...
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | return;
12+
LL | | };
13+
| |_____^
14+
|
15+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
16+
17+
error: aborting due to previous error
18+

‎tests/ui/loops/loop-else-err.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
loop {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `loop...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
}
8+
}

‎tests/ui/loops/loop-else-err.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `loop...else` loops are not supported
2+
--> $DIR/loop-else-err.rs:4:7
3+
|
4+
LL | loop {
5+
| ---- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | }
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
let _ = loop {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `loop...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
};
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `loop...else` loops are not supported
2+
--> $DIR/loop-else-let-else-err.rs:4:7
3+
|
4+
LL | let _ = loop {
5+
| ---- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | };
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+

‎tests/ui/while/while-else-err.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
while false {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `while...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
};
8+
}

‎tests/ui/while/while-else-err.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `while...else` loops are not supported
2+
--> $DIR/while-else-err.rs:4:7
3+
|
4+
LL | while false {
5+
| ----- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | };
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
let _ = while false {
3+
//~^ NOTE `else` is attached to this loop
4+
} else {
5+
//~^ ERROR `while...else` loops are not supported
6+
//~| NOTE consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
7+
};
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: `while...else` loops are not supported
2+
--> $DIR/while-else-let-else-err.rs:4:7
3+
|
4+
LL | let _ = while false {
5+
| ----- `else` is attached to this loop
6+
LL |
7+
LL | } else {
8+
| _______^
9+
LL | |
10+
LL | |
11+
LL | | };
12+
| |_____^
13+
|
14+
= note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run
15+
16+
error: aborting due to previous error
17+

0 commit comments

Comments
 (0)
Please sign in to comment.