From 3eb87dbe2ddd99c5334d5282e6a4d3de6d9fa6c0 Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Thu, 7 Oct 2021 03:33:00 -0700 Subject: [PATCH 01/18] doc: guarantee call order for sort_by_cached_key `slice::sort_by_cached_key` takes a caching function `f: impl FnMut(&T) -> K`, which means that the order that calls to the caching function are made is user-visible. This adds a clause to the documentation to promise the current behavior, which is that `f` is called on all elements of the slice from left to right, unless the slice has len < 2 in which case `f` is not called. --- library/alloc/src/slice.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 4c8ea6902ff14..58c35dfa3611e 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -374,7 +374,8 @@ impl [T] { /// During sorting, the key function is called only once per element. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) - /// worst-case, where the key function is *O*(*m*). + /// worst-case, where the key function is *O*(*m*). If the slice requires sorting, + /// the key function is called on all elements of the slice in the original order. /// /// For simple key functions (e.g., functions that are property accesses or /// basic operations), [`sort_by_key`](slice::sort_by_key) is likely to be From 61ff847203d6491f13782b0f6e461b593afc7a0d Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Sat, 27 Nov 2021 09:57:09 +0800 Subject: [PATCH 02/18] Use iterator instead of recursion in `codegen_place` --- compiler/rustc_codegen_ssa/src/mir/place.rs | 143 +++++++++----------- 1 file changed, 67 insertions(+), 76 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 4b07ed1a1e6c2..c21d19a62279f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -429,87 +429,78 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let cx = self.cx; let tcx = self.cx.tcx(); - let result = match place_ref { - mir::PlaceRef { local, projection: [] } => match self.locals[local] { - LocalRef::Place(place) => { - return place; - } - LocalRef::UnsizedPlace(place) => { - return bx.load_operand(place).deref(cx); - } - LocalRef::Operand(..) => { + let mut base = 0; + let mut cg_base = match self.locals[place_ref.local] { + LocalRef::Place(place) => place, + LocalRef::UnsizedPlace(place) => bx.load_operand(place).deref(cx), + LocalRef::Operand(..) => { + if let Some(elem) = place_ref + .projection + .iter() + .enumerate() + .find(|elem| matches!(elem.1, mir::ProjectionElem::Deref)) + { + base = elem.0 + 1; + self.codegen_consume( + bx, + mir::PlaceRef { projection: &place_ref.projection[..elem.0], ..place_ref }, + ) + .deref(bx.cx()) + } else { bug!("using operand local {:?} as place", place_ref); } - }, - mir::PlaceRef { local, projection: [proj_base @ .., mir::ProjectionElem::Deref] } => { - // Load the pointer from its location. - self.codegen_consume(bx, mir::PlaceRef { local, projection: proj_base }) - .deref(bx.cx()) } - mir::PlaceRef { local, projection: &[ref proj_base @ .., elem] } => { - // FIXME turn this recursion into iteration - let cg_base = - self.codegen_place(bx, mir::PlaceRef { local, projection: proj_base }); - - match elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - cg_base.project_field(bx, field.index()) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::from(index)); - let index = self.codegen_operand(bx, index); - let llindex = index.immediate(); - cg_base.project_index(bx, llindex) - } - mir::ProjectionElem::ConstantIndex { - offset, - from_end: false, - min_length: _, - } => { - let lloffset = bx.cx().const_usize(offset as u64); - cg_base.project_index(bx, lloffset) - } - mir::ProjectionElem::ConstantIndex { - offset, - from_end: true, - min_length: _, - } => { - let lloffset = bx.cx().const_usize(offset as u64); - let lllen = cg_base.len(bx.cx()); - let llindex = bx.sub(lllen, lloffset); - cg_base.project_index(bx, llindex) - } - mir::ProjectionElem::Subslice { from, to, from_end } => { - let mut subslice = - cg_base.project_index(bx, bx.cx().const_usize(from as u64)); - let projected_ty = - PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, elem).ty; - subslice.layout = bx.cx().layout_of(self.monomorphize(projected_ty)); - - if subslice.layout.is_unsized() { - assert!(from_end, "slice subslices should be `from_end`"); - subslice.llextra = Some(bx.sub( - cg_base.llextra.unwrap(), - bx.cx().const_usize((from as u64) + (to as u64)), - )); - } - - // Cast the place pointer type to the new - // array or slice type (`*[%_; new_len]`). - subslice.llval = bx.pointercast( - subslice.llval, - bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), - ); - - subslice + }; + for elem in place_ref.projection[base..].iter() { + cg_base = match elem.clone() { + mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), + mir::ProjectionElem::Field(ref field, _) => { + cg_base.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::from(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, from_end: false, min_length: _ } => { + let lloffset = bx.cx().const_usize(offset as u64); + cg_base.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, from_end: true, min_length: _ } => { + let lloffset = bx.cx().const_usize(offset as u64); + let lllen = cg_base.len(bx.cx()); + let llindex = bx.sub(lllen, lloffset); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to, from_end } => { + let mut subslice = cg_base.project_index(bx, bx.cx().const_usize(from as u64)); + let projected_ty = + PlaceTy::from_ty(cg_base.layout.ty).projection_ty(tcx, elem.clone()).ty; + subslice.layout = bx.cx().layout_of(self.monomorphize(projected_ty)); + + if subslice.layout.is_unsized() { + assert!(from_end, "slice subslices should be `from_end`"); + subslice.llextra = Some(bx.sub( + cg_base.llextra.unwrap(), + bx.cx().const_usize((from as u64) + (to as u64)), + )); } - mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), + + // Cast the place pointer type to the new + // array or slice type (`*[%_; new_len]`). + subslice.llval = bx.pointercast( + subslice.llval, + bx.cx().type_ptr_to(bx.cx().backend_type(subslice.layout)), + ); + + subslice } - } - }; - debug!("codegen_place(place={:?}) => {:?}", place_ref, result); - result + mir::ProjectionElem::Downcast(_, v) => cg_base.project_downcast(bx, v), + }; + } + debug!("codegen_place(place={:?}) => {:?}", place_ref, cg_base); + cg_base } pub fn monomorphized_place_ty(&self, place_ref: mir::PlaceRef<'tcx>) -> Ty<'tcx> { From 4b62a77e4d76f2f29cd46812e6d7400636790723 Mon Sep 17 00:00:00 2001 From: AngelicosPhosphoros Date: Mon, 20 Dec 2021 15:57:47 +0300 Subject: [PATCH 03/18] Little improves in CString `new` when creating from slice Old code already contain optimization for cases with `&str` and `&[u8]` args. This commit adds a specialization for `&mut[u8]` too. Also, I added usage of old slice in search for zero bytes instead of new buffer because it produce better code for Windows on LTO builds. For other platforms, this wouldn't cause any difference because it calls `libc` anyway. Inlined `_new` method into spec trait to reduce amount of code generated to `CString::new` callers. --- library/std/src/ffi/c_str.rs | 67 ++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index 9c1b79d696697..d859bff1a45fa 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -373,38 +373,61 @@ impl CString { /// the position of the nul byte. #[stable(feature = "rust1", since = "1.0.0")] pub fn new>>(t: T) -> Result { - trait SpecIntoVec { - fn into_vec(self) -> Vec; + trait SpecNewImpl { + fn spec_new_impl(self) -> Result; } - impl>> SpecIntoVec for T { - default fn into_vec(self) -> Vec { - self.into() + + impl>> SpecNewImpl for T { + default fn spec_new_impl(self) -> Result { + let bytes: Vec = self.into(); + match memchr::memchr(0, &bytes) { + Some(i) => Err(NulError(i, bytes)), + None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), + } } } - // Specialization for avoiding reallocation. - impl SpecIntoVec for &'_ [u8] { - fn into_vec(self) -> Vec { - let mut v = Vec::with_capacity(self.len() + 1); - v.extend(self); - v + + // Specialization for avoiding reallocation + #[inline(always)] // Without that it is not inlined into specializations + fn spec_new_impl_bytes(bytes: &[u8]) -> Result { + // We cannot have such large slice that we would overflow here + // but using `checked_add` allows LLVM to assume that capacity never overflows + // and generate twice shorter code. + // `saturating_add` doesn't help for some reason. + let capacity = bytes.len().checked_add(1).unwrap(); + + // Allocate before validation to avoid duplication of allocation code. + // We still need to allocate and copy memory even if we get an error. + let mut buffer = Vec::with_capacity(capacity); + buffer.extend(bytes); + + // Check memory of self instead of new buffer. + // This allows better optimizations if lto enabled. + match memchr::memchr(0, bytes) { + Some(i) => Err(NulError(i, buffer)), + None => Ok(unsafe { CString::from_vec_unchecked(buffer) }), } } - impl SpecIntoVec for &'_ str { - fn into_vec(self) -> Vec { - let mut v = Vec::with_capacity(self.len() + 1); - v.extend(self.as_bytes()); - v + + impl SpecNewImpl for &'_ [u8] { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self) } } - Self::_new(SpecIntoVec::into_vec(t)) - } + impl SpecNewImpl for &'_ str { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self.as_bytes()) + } + } - fn _new(bytes: Vec) -> Result { - match memchr::memchr(0, &bytes) { - Some(i) => Err(NulError(i, bytes)), - None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), + impl SpecNewImpl for &'_ mut [u8] { + fn spec_new_impl(self) -> Result { + spec_new_impl_bytes(self) + } } + + t.spec_new_impl() } /// Creates a C-compatible string by consuming a byte vector, From b9f008b1eeed6055eb36e0fae11b30e8ffc2ebfa Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Tue, 4 Jan 2022 12:18:54 -0800 Subject: [PATCH 04/18] Update wording --- library/alloc/src/slice.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 58c35dfa3611e..020dfcbaac684 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -371,11 +371,11 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// During sorting, the key function is called only once per element. + /// During sorting, the key function is called at most once per element, by using + /// temporary storage to remember the results of key evaluation. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) - /// worst-case, where the key function is *O*(*m*). If the slice requires sorting, - /// the key function is called on all elements of the slice in the original order. + /// worst-case, where the key function is *O*(*m*). /// /// For simple key functions (e.g., functions that are property accesses or /// basic operations), [`sort_by_key`](slice::sort_by_key) is likely to be From 06b17a21813c2869ee50cbc4c8a92a04e40d2959 Mon Sep 17 00:00:00 2001 From: Mario Carneiro Date: Tue, 4 Jan 2022 21:32:20 -0800 Subject: [PATCH 05/18] Clarify that ordering is unspecified --- library/alloc/src/slice.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index 020dfcbaac684..d03ae2d5c1519 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -373,6 +373,8 @@ impl [T] { /// /// During sorting, the key function is called at most once per element, by using /// temporary storage to remember the results of key evaluation. + /// The order of calls to the key function is unspecified and may change in future versions + /// of the standard library. /// /// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \* log(*n*)) /// worst-case, where the key function is *O*(*m*). From bd1f09d417c8b0e460f90bcd23b9067d55f7f7dd Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Tue, 11 Jan 2022 21:06:18 +0100 Subject: [PATCH 06/18] Annotate dead code lint with notes about ignored derived impls --- compiler/rustc_passes/src/dead.rs | 52 ++++++++++++++++--- .../ui/derive-uninhabited-enum-38885.stderr | 5 ++ .../ui/derives/clone-debug-dead-code.stderr | 18 +++++++ .../ui/lint/dead-code/unused-variant.stderr | 5 ++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 3b15332c678fd..9db9140f71ea8 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -3,6 +3,7 @@ // from live codes are live, and everything else is dead. use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::pluralize; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -47,6 +48,8 @@ struct MarkSymbolVisitor<'tcx> { ignore_variant_stack: Vec, // maps from tuple struct constructors to tuple struct items struct_constructors: FxHashMap, + // maps from ADTs to ignored derived traits (e.g. Debug and Clone) + ignored_derived_traits: FxHashMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -242,7 +245,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// Automatically generated items marked with `rustc_trivial_field_reads` /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). - fn should_ignore_item(&self, def_id: DefId) -> bool { + fn should_ignore_item(&mut self, def_id: DefId) -> bool { if let Some(impl_of) = self.tcx.impl_of_method(def_id) { if !self.tcx.has_attr(impl_of, sym::automatically_derived) { return false; @@ -250,6 +253,14 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of) { if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { + let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap(); + if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() { + if let Some(v) = self.ignored_derived_traits.get_mut(&adt_def.did) { + v.push(trait_of); + } else { + self.ignored_derived_traits.insert(adt_def.did, vec![trait_of]); + } + } return true; } } @@ -577,7 +588,7 @@ fn create_and_seed_worklist<'tcx>( fn find_live<'tcx>( tcx: TyCtxt<'tcx>, access_levels: &privacy::AccessLevels, -) -> FxHashSet { +) -> (FxHashSet, FxHashMap>) { let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels); let mut symbol_visitor = MarkSymbolVisitor { worklist, @@ -590,14 +601,16 @@ fn find_live<'tcx>( pub_visibility: false, ignore_variant_stack: vec![], struct_constructors, + ignored_derived_traits: FxHashMap::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.live_symbols + (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: FxHashSet, + ignored_derived_traits: FxHashMap>, } impl<'tcx> DeadVisitor<'tcx> { @@ -666,7 +679,34 @@ impl<'tcx> DeadVisitor<'tcx> { self.tcx.struct_span_lint_hir(lint::builtin::DEAD_CODE, id, span, |lint| { let def_id = self.tcx.hir().local_def_id(id); let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); - lint.build(&format!("{} is never {}: `{}`", descr, participle, name)).emit() + let mut err = lint.build(&format!("{} is never {}: `{}`", descr, participle, name)); + let hir = self.tcx.hir(); + if let Some(encl_scope) = hir.get_enclosing_scope(id) { + if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) { + if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id.to_def_id()) { + let traits_str = ign_traits + .iter() + .map(|t| format!("`{}`", self.tcx.item_name(*t))).collect::>() + .join(" and "); + let plural_s = pluralize!(ign_traits.len()); + let article = if ign_traits.len() > 1 { "" } else { "a " }; + let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; + let msg = format!("`{}` has {}derived impl{} for the trait{} {}, but {} ignored during dead code analysis", + self.tcx.item_name(encl_def_id.to_def_id()), + article, + plural_s, + plural_s, + traits_str, + is_are); + if let Some(span) = self.tcx.def_ident_span(encl_def_id) { + err.span_note(span, &msg); + } else { + err.note(&msg); + } + } + } + } + err.emit(); }); } } @@ -796,7 +836,7 @@ impl<'tcx> Visitor<'tcx> for DeadVisitor<'tcx> { pub fn check_crate(tcx: TyCtxt<'_>) { let access_levels = &tcx.privacy_access_levels(()); - let live_symbols = find_live(tcx, access_levels); - let mut visitor = DeadVisitor { tcx, live_symbols }; + let (live_symbols, ignored_derived_traits) = find_live(tcx, access_levels); + let mut visitor = DeadVisitor { tcx, live_symbols, ignored_derived_traits }; tcx.hir().walk_toplevel_module(&mut visitor); } diff --git a/src/test/ui/derive-uninhabited-enum-38885.stderr b/src/test/ui/derive-uninhabited-enum-38885.stderr index 72607629d3c10..feaadd201b306 100644 --- a/src/test/ui/derive-uninhabited-enum-38885.stderr +++ b/src/test/ui/derive-uninhabited-enum-38885.stderr @@ -5,6 +5,11 @@ LL | Void(Void), | ^^^^^^^^^^ | = note: `-W dead-code` implied by `-W unused` +note: `Foo` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis + --> $DIR/derive-uninhabited-enum-38885.rs:11:6 + | +LL | enum Foo { + | ^^^ warning: 1 warning emitted diff --git a/src/test/ui/derives/clone-debug-dead-code.stderr b/src/test/ui/derives/clone-debug-dead-code.stderr index 226007f3647b1..d7cab0815b8f0 100644 --- a/src/test/ui/derives/clone-debug-dead-code.stderr +++ b/src/test/ui/derives/clone-debug-dead-code.stderr @@ -15,18 +15,36 @@ error: field is never read: `f` | LL | struct B { f: () } | ^^^^^ + | +note: `B` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:10:8 + | +LL | struct B { f: () } + | ^ error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:14:12 | LL | struct C { f: () } | ^^^^^ + | +note: `C` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:14:8 + | +LL | struct C { f: () } + | ^ error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:18:12 | LL | struct D { f: () } | ^^^^^ + | +note: `D` has derived impls for the traits `Clone` and `Debug`, but these are ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:18:8 + | +LL | struct D { f: () } + | ^ error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:21:12 diff --git a/src/test/ui/lint/dead-code/unused-variant.stderr b/src/test/ui/lint/dead-code/unused-variant.stderr index a547f5af4b082..abac1f29ce24b 100644 --- a/src/test/ui/lint/dead-code/unused-variant.stderr +++ b/src/test/ui/lint/dead-code/unused-variant.stderr @@ -9,6 +9,11 @@ note: the lint level is defined here | LL | #![deny(dead_code)] | ^^^^^^^^^ +note: `Enum` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis + --> $DIR/unused-variant.rs:4:6 + | +LL | enum Enum { + | ^^^^ error: aborting due to previous error From 8b459dd73887732d4b2741b2459a2edd60a47229 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sat, 15 Jan 2022 21:01:44 +0100 Subject: [PATCH 07/18] Use span of ignored impls for explanatory note --- compiler/rustc_passes/src/dead.rs | 46 +++++++++++-------- .../ui/derive-uninhabited-enum-38885.stderr | 9 ++-- .../ui/derives/clone-debug-dead-code.stderr | 27 ++++++----- .../ui/lint/dead-code/unused-variant.stderr | 9 ++-- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 9db9140f71ea8..3e4e9f205a45c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -16,6 +16,7 @@ use rustc_middle::middle::privacy; use rustc_middle::ty::{self, DefIdTree, TyCtxt}; use rustc_session::lint; use rustc_span::symbol::{sym, Symbol}; +use rustc_span::Span; use std::mem; // Any local node that may call something in its body block should be @@ -49,7 +50,9 @@ struct MarkSymbolVisitor<'tcx> { // maps from tuple struct constructors to tuple struct items struct_constructors: FxHashMap, // maps from ADTs to ignored derived traits (e.g. Debug and Clone) - ignored_derived_traits: FxHashMap>, + // and the span of their respective impl (i.e., part of the derive + // macro) + ignored_derived_traits: FxHashMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -255,10 +258,12 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { if self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads) { let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap(); if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind() { + let impl_span = self.tcx.def_span(impl_of); if let Some(v) = self.ignored_derived_traits.get_mut(&adt_def.did) { - v.push(trait_of); + v.push((impl_span, trait_of)); } else { - self.ignored_derived_traits.insert(adt_def.did, vec![trait_of]); + self.ignored_derived_traits + .insert(adt_def.did, vec![(impl_span, trait_of)]); } } return true; @@ -588,7 +593,7 @@ fn create_and_seed_worklist<'tcx>( fn find_live<'tcx>( tcx: TyCtxt<'tcx>, access_levels: &privacy::AccessLevels, -) -> (FxHashSet, FxHashMap>) { +) -> (FxHashSet, FxHashMap>) { let (worklist, struct_constructors) = create_and_seed_worklist(tcx, access_levels); let mut symbol_visitor = MarkSymbolVisitor { worklist, @@ -610,7 +615,7 @@ fn find_live<'tcx>( struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: FxHashSet, - ignored_derived_traits: FxHashMap>, + ignored_derived_traits: FxHashMap>, } impl<'tcx> DeadVisitor<'tcx> { @@ -683,26 +688,29 @@ impl<'tcx> DeadVisitor<'tcx> { let hir = self.tcx.hir(); if let Some(encl_scope) = hir.get_enclosing_scope(id) { if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) { - if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id.to_def_id()) { + if let Some(ign_traits) = + self.ignored_derived_traits.get(&encl_def_id.to_def_id()) + { let traits_str = ign_traits .iter() - .map(|t| format!("`{}`", self.tcx.item_name(*t))).collect::>() + .map(|(_, t)| format!("`{}`", self.tcx.item_name(*t))) + .collect::>() .join(" and "); let plural_s = pluralize!(ign_traits.len()); let article = if ign_traits.len() > 1 { "" } else { "a " }; let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; - let msg = format!("`{}` has {}derived impl{} for the trait{} {}, but {} ignored during dead code analysis", - self.tcx.item_name(encl_def_id.to_def_id()), - article, - plural_s, - plural_s, - traits_str, - is_are); - if let Some(span) = self.tcx.def_ident_span(encl_def_id) { - err.span_note(span, &msg); - } else { - err.note(&msg); - } + let msg = format!( + "`{}` has {}derived impl{} for the trait{} {}, but {} \ + intentionally ignored during dead code analysis", + self.tcx.item_name(encl_def_id.to_def_id()), + article, + plural_s, + plural_s, + traits_str, + is_are + ); + let multispan = ign_traits.iter().map(|(s, _)| *s).collect::>(); + err.span_note(multispan, &msg); } } } diff --git a/src/test/ui/derive-uninhabited-enum-38885.stderr b/src/test/ui/derive-uninhabited-enum-38885.stderr index feaadd201b306..2a44e56a3302c 100644 --- a/src/test/ui/derive-uninhabited-enum-38885.stderr +++ b/src/test/ui/derive-uninhabited-enum-38885.stderr @@ -5,11 +5,12 @@ LL | Void(Void), | ^^^^^^^^^^ | = note: `-W dead-code` implied by `-W unused` -note: `Foo` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis - --> $DIR/derive-uninhabited-enum-38885.rs:11:6 +note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis + --> $DIR/derive-uninhabited-enum-38885.rs:10:10 | -LL | enum Foo { - | ^^^ +LL | #[derive(Debug)] + | ^^^^^ + = note: this warning originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) warning: 1 warning emitted diff --git a/src/test/ui/derives/clone-debug-dead-code.stderr b/src/test/ui/derives/clone-debug-dead-code.stderr index d7cab0815b8f0..67bb574315a72 100644 --- a/src/test/ui/derives/clone-debug-dead-code.stderr +++ b/src/test/ui/derives/clone-debug-dead-code.stderr @@ -16,11 +16,12 @@ error: field is never read: `f` LL | struct B { f: () } | ^^^^^ | -note: `B` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis - --> $DIR/clone-debug-dead-code.rs:10:8 +note: `B` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:9:10 | -LL | struct B { f: () } - | ^ +LL | #[derive(Clone)] + | ^^^^^ + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:14:12 @@ -28,11 +29,12 @@ error: field is never read: `f` LL | struct C { f: () } | ^^^^^ | -note: `C` has a derived impl for the trait `Debug`, but this is ignored during dead code analysis - --> $DIR/clone-debug-dead-code.rs:14:8 +note: `C` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:13:10 | -LL | struct C { f: () } - | ^ +LL | #[derive(Debug)] + | ^^^^^ + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:18:12 @@ -40,11 +42,12 @@ error: field is never read: `f` LL | struct D { f: () } | ^^^^^ | -note: `D` has derived impls for the traits `Clone` and `Debug`, but these are ignored during dead code analysis - --> $DIR/clone-debug-dead-code.rs:18:8 +note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis + --> $DIR/clone-debug-dead-code.rs:17:10 | -LL | struct D { f: () } - | ^ +LL | #[derive(Debug,Clone)] + | ^^^^^ ^^^^^ + = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info) error: field is never read: `f` --> $DIR/clone-debug-dead-code.rs:21:12 diff --git a/src/test/ui/lint/dead-code/unused-variant.stderr b/src/test/ui/lint/dead-code/unused-variant.stderr index abac1f29ce24b..3b5683a7748fa 100644 --- a/src/test/ui/lint/dead-code/unused-variant.stderr +++ b/src/test/ui/lint/dead-code/unused-variant.stderr @@ -9,11 +9,12 @@ note: the lint level is defined here | LL | #![deny(dead_code)] | ^^^^^^^^^ -note: `Enum` has a derived impl for the trait `Clone`, but this is ignored during dead code analysis - --> $DIR/unused-variant.rs:4:6 +note: `Enum` has a derived impl for the trait `Clone`, but this is intentionally ignored during dead code analysis + --> $DIR/unused-variant.rs:3:10 | -LL | enum Enum { - | ^^^^ +LL | #[derive(Clone)] + | ^^^^^ + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error From 0882bbb3a190c99d0757cbcec87d375a3b85a0ac Mon Sep 17 00:00:00 2001 From: pierwill Date: Tue, 18 Jan 2022 10:05:41 -0600 Subject: [PATCH 08/18] Remove some unused `Ord` derivations based on `DefId` Removes `Ord` and `PartialOrd` from middle::mir::mirsource, inlineasmoperand, terminatorkind, operand, constant, constantkind, and place --- compiler/rustc_middle/src/mir/mod.rs | 22 ++++++--------------- compiler/rustc_middle/src/mir/terminator.rs | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 3f4bbdec31545..48f39b26152cb 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -162,7 +162,7 @@ impl MirPhase { } /// Where a specific `mir::Body` comes from. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable)] pub struct MirSource<'tcx> { pub instance: InstanceDef<'tcx>, @@ -1255,17 +1255,7 @@ pub enum AssertKind { ResumedAfterPanic(GeneratorKind), } -#[derive( - Clone, - Debug, - PartialEq, - PartialOrd, - TyEncodable, - TyDecodable, - Hash, - HashStable, - TypeFoldable -)] +#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum InlineAsmOperand<'tcx> { In { reg: InlineAsmRegOrRegClass, @@ -1747,7 +1737,7 @@ pub struct CopyNonOverlapping<'tcx> { /// A path to a value; something that can be evaluated without /// changing or disturbing program state. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable)] pub struct Place<'tcx> { pub local: Local, @@ -2072,7 +2062,7 @@ pub struct SourceScopeLocalData { /// These are values that can appear inside an rvalue. They are intentionally /// limited to prevent rvalues from being nested in one another. -#[derive(Clone, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum Operand<'tcx> { /// Copy: The value must be available for use afterwards. /// @@ -2500,7 +2490,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { /// this does not necessarily mean that they are `==` in Rust. In /// particular, one must be wary of `NaN`! -#[derive(Clone, Copy, PartialEq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable)] +#[derive(Clone, Copy, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub struct Constant<'tcx> { pub span: Span, @@ -2514,7 +2504,7 @@ pub struct Constant<'tcx> { pub literal: ConstantKind<'tcx>, } -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, TyEncodable, TyDecodable, Hash, HashStable, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)] #[derive(Lift)] pub enum ConstantKind<'tcx> { /// This constant came from the type system diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 51e4afaf2204a..fafd847a1cbaa 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -105,7 +105,7 @@ impl<'a> Iterator for SwitchTargetsIter<'a> { impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {} -#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, PartialOrd)] +#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] pub enum TerminatorKind<'tcx> { /// Block should have one successor in the graph; we jump there. Goto { target: BasicBlock }, From b1605643e3a9f62bdbe97c4854745e2f28ccd456 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 14 Jan 2022 20:41:20 -0800 Subject: [PATCH 09/18] Move expr-related pretty printing functions to module --- compiler/rustc_ast_pretty/src/pprust/state.rs | 567 +---------------- .../rustc_ast_pretty/src/pprust/state/expr.rs | 571 ++++++++++++++++++ 2 files changed, 574 insertions(+), 564 deletions(-) create mode 100644 compiler/rustc_ast_pretty/src/pprust/state/expr.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 17941058ed6f0..70202a380cc8b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1,3 +1,5 @@ +mod expr; + use crate::pp::Breaks::{Consistent, Inconsistent}; use crate::pp::{self, Breaks}; @@ -6,7 +8,7 @@ use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, T use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle}; -use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::util::parser; use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax}; use rustc_ast::{attr, Term}; use rustc_ast::{GenericArg, MacArgs, ModKind}; @@ -1682,42 +1684,6 @@ impl<'a> State<'a> { self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals()) } - fn print_else(&mut self, els: Option<&ast::Expr>) { - if let Some(_else) = els { - match _else.kind { - // Another `else if` block. - ast::ExprKind::If(ref i, ref then, ref e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); - self.word(" else if "); - self.print_expr_as_cond(i); - self.space(); - self.print_block(then); - self.print_else(e.as_deref()) - } - // Final `else` block. - ast::ExprKind::Block(ref b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); - self.word(" else "); - self.print_block(b) - } - // Constraints would be great here! - _ => { - panic!("print_if saw if with weird alternative"); - } - } - } - } - - crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { - self.head("if"); - self.print_expr_as_cond(test); - self.space(); - self.print_block(blk); - self.print_else(elseopt) - } - crate fn print_mac(&mut self, m: &ast::MacCall) { self.print_mac_common( Some(MacHeader::Path(&m.path)), @@ -1730,477 +1696,6 @@ impl<'a> State<'a> { ); } - fn print_call_post(&mut self, args: &[P]) { - self.popen(); - self.commasep_exprs(Inconsistent, args); - self.pclose() - } - - crate fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec) - } - - /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in - /// `if cond { ... }`. - crate fn print_expr_as_cond(&mut self, expr: &ast::Expr) { - self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) - } - - // Does `expr` need parentheses when printed in a condition position? - // - // These cases need parens due to the parse error observed in #26461: `if return {}` - // parses as the erroneous construct `if (return {})`, not `if (return) {}`. - fn cond_needs_par(expr: &ast::Expr) -> bool { - match expr.kind { - ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true, - _ => parser::contains_exterior_struct_lit(expr), - } - } - - /// Prints `expr` or `(expr)` when `needs_par` holds. - fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { - if needs_par { - self.popen(); - } - self.print_expr(expr); - if needs_par { - self.pclose(); - } - } - - fn print_expr_vec(&mut self, exprs: &[P]) { - self.ibox(INDENT_UNIT); - self.word("["); - self.commasep_exprs(Inconsistent, exprs); - self.word("]"); - self.end(); - } - - fn print_expr_anon_const(&mut self, expr: &ast::AnonConst) { - self.ibox(INDENT_UNIT); - self.word("const"); - self.print_expr(&expr.value); - self.end(); - } - - fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { - self.ibox(INDENT_UNIT); - self.word("["); - self.print_expr(element); - self.word_space(";"); - self.print_expr(&count.value); - self.word("]"); - self.end(); - } - - fn print_expr_struct( - &mut self, - qself: &Option, - path: &ast::Path, - fields: &[ast::ExprField], - rest: &ast::StructRest, - ) { - if let Some(qself) = qself { - self.print_qpath(path, qself, true); - } else { - self.print_path(path, true, 0); - } - self.word("{"); - self.commasep_cmnt( - Consistent, - fields, - |s, field| { - s.print_outer_attributes(&field.attrs); - s.ibox(INDENT_UNIT); - if !field.is_shorthand { - s.print_ident(field.ident); - s.word_space(":"); - } - s.print_expr(&field.expr); - s.end(); - }, - |f| f.span, - ); - match rest { - ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { - self.ibox(INDENT_UNIT); - if !fields.is_empty() { - self.word(","); - self.space(); - } - self.word(".."); - if let ast::StructRest::Base(ref expr) = *rest { - self.print_expr(expr); - } - self.end(); - } - ast::StructRest::None if !fields.is_empty() => self.word(","), - _ => {} - } - self.word("}"); - } - - fn print_expr_tup(&mut self, exprs: &[P]) { - self.popen(); - self.commasep_exprs(Inconsistent, exprs); - if exprs.len() == 1 { - self.word(","); - } - self.pclose() - } - - fn print_expr_call(&mut self, func: &ast::Expr, args: &[P]) { - let prec = match func.kind { - ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, - _ => parser::PREC_POSTFIX, - }; - - self.print_expr_maybe_paren(func, prec); - self.print_call_post(args) - } - - fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P]) { - let base_args = &args[1..]; - self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); - self.word("."); - self.print_ident(segment.ident); - if let Some(ref args) = segment.args { - self.print_generic_args(args, true); - } - self.print_call_post(base_args) - } - - fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) { - let assoc_op = AssocOp::from_ast_binop(op.node); - let prec = assoc_op.precedence() as i8; - let fixity = assoc_op.fixity(); - - let (left_prec, right_prec) = match fixity { - Fixity::Left => (prec, prec + 1), - Fixity::Right => (prec + 1, prec), - Fixity::None => (prec + 1, prec + 1), - }; - - let left_prec = match (&lhs.kind, op.node) { - // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is - // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead - // of `(x as i32) < ...`. We need to convince it _not_ to do that. - (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { - parser::PREC_FORCE_PAREN - } - // We are given `(let _ = a) OP b`. - // - // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens - // as the parser will interpret this as `(let _ = a) OP b`. - // - // - Otherwise, e.g. when we have `(let a = b) < c` in AST, - // parens are required since the parser would interpret `let a = b < c` as - // `let a = (b < c)`. To achieve this, we force parens. - (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { - parser::PREC_FORCE_PAREN - } - _ => left_prec, - }; - - self.print_expr_maybe_paren(lhs, left_prec); - self.space(); - self.word_space(op.node.to_string()); - self.print_expr_maybe_paren(rhs, right_prec) - } - - fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) { - self.word(ast::UnOp::to_string(op)); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - fn print_expr_addr_of( - &mut self, - kind: ast::BorrowKind, - mutability: ast::Mutability, - expr: &ast::Expr, - ) { - self.word("&"); - match kind { - ast::BorrowKind::Ref => self.print_mutability(mutability, false), - ast::BorrowKind::Raw => { - self.word_nbsp("raw"); - self.print_mutability(mutability, true); - } - } - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) - } - - pub fn print_expr(&mut self, expr: &ast::Expr) { - self.print_expr_outer_attr_style(expr, true) - } - - fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) { - self.maybe_print_comment(expr.span.lo()); - - let attrs = &expr.attrs; - if is_inline { - self.print_outer_attributes_inline(attrs); - } else { - self.print_outer_attributes(attrs); - } - - self.ibox(INDENT_UNIT); - self.ann.pre(self, AnnNode::Expr(expr)); - match expr.kind { - ast::ExprKind::Box(ref expr) => { - self.word_space("box"); - self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); - } - ast::ExprKind::Array(ref exprs) => { - self.print_expr_vec(exprs); - } - ast::ExprKind::ConstBlock(ref anon_const) => { - self.print_expr_anon_const(anon_const); - } - ast::ExprKind::Repeat(ref element, ref count) => { - self.print_expr_repeat(element, count); - } - ast::ExprKind::Struct(ref se) => { - self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest); - } - ast::ExprKind::Tup(ref exprs) => { - self.print_expr_tup(exprs); - } - ast::ExprKind::Call(ref func, ref args) => { - self.print_expr_call(func, &args); - } - ast::ExprKind::MethodCall(ref segment, ref args, _) => { - self.print_expr_method_call(segment, &args); - } - ast::ExprKind::Binary(op, ref lhs, ref rhs) => { - self.print_expr_binary(op, lhs, rhs); - } - ast::ExprKind::Unary(op, ref expr) => { - self.print_expr_unary(op, expr); - } - ast::ExprKind::AddrOf(k, m, ref expr) => { - self.print_expr_addr_of(k, m, expr); - } - ast::ExprKind::Lit(ref lit) => { - self.print_literal(lit); - } - ast::ExprKind::Cast(ref expr, ref ty) => { - let prec = AssocOp::As.precedence() as i8; - self.print_expr_maybe_paren(expr, prec); - self.space(); - self.word_space("as"); - self.print_type(ty); - } - ast::ExprKind::Type(ref expr, ref ty) => { - let prec = AssocOp::Colon.precedence() as i8; - self.print_expr_maybe_paren(expr, prec); - self.word_space(":"); - self.print_type(ty); - } - ast::ExprKind::Let(ref pat, ref scrutinee, _) => { - self.print_let(pat, scrutinee); - } - ast::ExprKind::If(ref test, ref blk, ref elseopt) => { - self.print_if(test, blk, elseopt.as_deref()) - } - ast::ExprKind::While(ref test, ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("while"); - self.print_expr_as_cond(test); - self.space(); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("for"); - self.print_pat(pat); - self.space(); - self.word_space("in"); - self.print_expr_as_cond(iter); - self.space(); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Loop(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - self.head("loop"); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Match(ref expr, ref arms) => { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); - self.word_nbsp("match"); - self.print_expr_as_cond(expr); - self.space(); - self.bopen(); - self.print_inner_attributes_no_trailing_hardbreak(attrs); - for arm in arms { - self.print_arm(arm); - } - let empty = attrs.is_empty() && arms.is_empty(); - self.bclose(expr.span, empty); - } - ast::ExprKind::Closure( - capture_clause, - asyncness, - movability, - ref decl, - ref body, - _, - ) => { - self.print_movability(movability); - self.print_asyncness(asyncness); - self.print_capture_clause(capture_clause); - - self.print_fn_params_and_ret(decl, true); - self.space(); - self.print_expr(body); - self.end(); // need to close a box - - // a box will be closed by print_expr, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - self.ibox(0); - } - ast::ExprKind::Block(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - // containing cbox, will be closed by print-block at } - self.cbox(INDENT_UNIT); - // head-box, will be closed by print-block after { - self.ibox(0); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Async(capture_clause, _, ref blk) => { - self.word_nbsp("async"); - self.print_capture_clause(capture_clause); - // cbox/ibox in analogy to the `ExprKind::Block` arm above - self.cbox(INDENT_UNIT); - self.ibox(0); - self.print_block_with_attrs(blk, attrs); - } - ast::ExprKind::Await(ref expr) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word(".await"); - } - ast::ExprKind::Assign(ref lhs, ref rhs, _) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); - self.space(); - self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); - } - ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => { - let prec = AssocOp::Assign.precedence() as i8; - self.print_expr_maybe_paren(lhs, prec + 1); - self.space(); - self.word(op.node.to_string()); - self.word_space("="); - self.print_expr_maybe_paren(rhs, prec); - } - ast::ExprKind::Field(ref expr, ident) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word("."); - self.print_ident(ident); - } - ast::ExprKind::Index(ref expr, ref index) => { - self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); - self.word("["); - self.print_expr(index); - self.word("]"); - } - ast::ExprKind::Range(ref start, ref end, limits) => { - // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence - // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. - // Here we use a fake precedence value so that any child with lower precedence than - // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) - let fake_prec = AssocOp::LOr.precedence() as i8; - if let Some(ref e) = *start { - self.print_expr_maybe_paren(e, fake_prec); - } - if limits == ast::RangeLimits::HalfOpen { - self.word(".."); - } else { - self.word("..="); - } - if let Some(ref e) = *end { - self.print_expr_maybe_paren(e, fake_prec); - } - } - ast::ExprKind::Underscore => self.word("_"), - ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), - ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), - ast::ExprKind::Break(opt_label, ref opt_expr) => { - self.word("break"); - if let Some(label) = opt_label { - self.space(); - self.print_ident(label.ident); - } - if let Some(ref expr) = *opt_expr { - self.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::Continue(opt_label) => { - self.word("continue"); - if let Some(label) = opt_label { - self.space(); - self.print_ident(label.ident); - } - } - ast::ExprKind::Ret(ref result) => { - self.word("return"); - if let Some(ref expr) = *result { - self.word(" "); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::InlineAsm(ref a) => { - self.word("asm!"); - self.print_inline_asm(a); - } - ast::ExprKind::MacCall(ref m) => self.print_mac(m), - ast::ExprKind::Paren(ref e) => { - self.popen(); - self.print_expr(e); - self.pclose(); - } - ast::ExprKind::Yield(ref e) => { - self.word("yield"); - - if let Some(ref expr) = *e { - self.space(); - self.print_expr_maybe_paren(expr, parser::PREC_JUMP); - } - } - ast::ExprKind::Try(ref e) => { - self.print_expr_maybe_paren(e, parser::PREC_POSTFIX); - self.word("?") - } - ast::ExprKind::TryBlock(ref blk) => { - self.head("try"); - self.print_block_with_attrs(blk, attrs) - } - ast::ExprKind::Err => { - self.popen(); - self.word("/*ERROR*/"); - self.pclose() - } - } - self.ann.post(self, AnnNode::Expr(expr)); - self.end(); - } - fn print_inline_asm(&mut self, asm: &ast::InlineAsm) { enum AsmArg<'a> { Template(String), @@ -2496,48 +1991,6 @@ impl<'a> State<'a> { self.ann.post(self, AnnNode::Pat(pat)) } - fn print_arm(&mut self, arm: &ast::Arm) { - // Note, I have no idea why this check is necessary, but here it is. - if arm.attrs.is_empty() { - self.space(); - } - self.cbox(INDENT_UNIT); - self.ibox(0); - self.maybe_print_comment(arm.pat.span.lo()); - self.print_outer_attributes(&arm.attrs); - self.print_pat(&arm.pat); - self.space(); - if let Some(ref e) = arm.guard { - self.word_space("if"); - self.print_expr(e); - self.space(); - } - self.word_space("=>"); - - match arm.body.kind { - ast::ExprKind::Block(ref blk, opt_label) => { - if let Some(label) = opt_label { - self.print_ident(label.ident); - self.word_space(":"); - } - - // The block will close the pattern's ibox. - self.print_block_unclosed_indent(blk); - - // If it is a user-provided unsafe block, print a comma after it. - if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { - self.word(","); - } - } - _ => { - self.end(); // Close the ibox for the pattern. - self.print_expr(&arm.body); - self.word(","); - } - } - self.end(); // Close enclosing cbox. - } - fn print_explicit_self(&mut self, explicit_self: &ast::ExplicitSelf) { match explicit_self.node { SelfKind::Value(m) => { @@ -2608,26 +2061,12 @@ impl<'a> State<'a> { self.print_fn_ret_ty(&decl.output) } - crate fn print_movability(&mut self, movability: ast::Movability) { - match movability { - ast::Movability::Static => self.word_space("static"), - ast::Movability::Movable => {} - } - } - crate fn print_asyncness(&mut self, asyncness: ast::Async) { if asyncness.is_async() { self.word_nbsp("async"); } } - crate fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) { - match capture_clause { - ast::CaptureBy::Value => self.word_space("move"), - ast::CaptureBy::Ref => {} - } - } - pub fn print_type_bounds(&mut self, prefix: &'static str, bounds: &[ast::GenericBound]) { if !bounds.is_empty() { self.word(prefix); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs new file mode 100644 index 0000000000000..956200d60f507 --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -0,0 +1,571 @@ +use crate::pp::Breaks::{Consistent, Inconsistent}; +use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; + +use rustc_ast::ptr::P; +use rustc_ast::util::parser::{self, AssocOp, Fixity}; +use rustc_ast::{self as ast, BlockCheckMode}; + +impl<'a> State<'a> { + fn print_else(&mut self, els: Option<&ast::Expr>) { + if let Some(_else) = els { + match _else.kind { + // Another `else if` block. + ast::ExprKind::If(ref i, ref then, ref e) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.word(" else if "); + self.print_expr_as_cond(i); + self.space(); + self.print_block(then); + self.print_else(e.as_deref()) + } + // Final `else` block. + ast::ExprKind::Block(ref b, _) => { + self.cbox(INDENT_UNIT - 1); + self.ibox(0); + self.word(" else "); + self.print_block(b) + } + // Constraints would be great here! + _ => { + panic!("print_if saw if with weird alternative"); + } + } + } + } + + fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { + self.head("if"); + self.print_expr_as_cond(test); + self.space(); + self.print_block(blk); + self.print_else(elseopt) + } + + fn print_call_post(&mut self, args: &[P]) { + self.popen(); + self.commasep_exprs(Inconsistent, args); + self.pclose() + } + + fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8) { + self.print_expr_cond_paren(expr, expr.precedence().order() < prec) + } + + /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in + /// `if cond { ... }`. + fn print_expr_as_cond(&mut self, expr: &ast::Expr) { + self.print_expr_cond_paren(expr, Self::cond_needs_par(expr)) + } + + // Does `expr` need parentheses when printed in a condition position? + // + // These cases need parens due to the parse error observed in #26461: `if return {}` + // parses as the erroneous construct `if (return {})`, not `if (return) {}`. + pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool { + match expr.kind { + ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true, + _ => parser::contains_exterior_struct_lit(expr), + } + } + + /// Prints `expr` or `(expr)` when `needs_par` holds. + pub(super) fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) { + if needs_par { + self.popen(); + } + self.print_expr(expr); + if needs_par { + self.pclose(); + } + } + + fn print_expr_vec(&mut self, exprs: &[P]) { + self.ibox(INDENT_UNIT); + self.word("["); + self.commasep_exprs(Inconsistent, exprs); + self.word("]"); + self.end(); + } + + pub(super) fn print_expr_anon_const(&mut self, expr: &ast::AnonConst) { + self.ibox(INDENT_UNIT); + self.word("const"); + self.print_expr(&expr.value); + self.end(); + } + + fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { + self.ibox(INDENT_UNIT); + self.word("["); + self.print_expr(element); + self.word_space(";"); + self.print_expr(&count.value); + self.word("]"); + self.end(); + } + + fn print_expr_struct( + &mut self, + qself: &Option, + path: &ast::Path, + fields: &[ast::ExprField], + rest: &ast::StructRest, + ) { + if let Some(qself) = qself { + self.print_qpath(path, qself, true); + } else { + self.print_path(path, true, 0); + } + self.word("{"); + self.commasep_cmnt( + Consistent, + fields, + |s, field| { + s.print_outer_attributes(&field.attrs); + s.ibox(INDENT_UNIT); + if !field.is_shorthand { + s.print_ident(field.ident); + s.word_space(":"); + } + s.print_expr(&field.expr); + s.end(); + }, + |f| f.span, + ); + match rest { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { + self.ibox(INDENT_UNIT); + if !fields.is_empty() { + self.word(","); + self.space(); + } + self.word(".."); + if let ast::StructRest::Base(ref expr) = *rest { + self.print_expr(expr); + } + self.end(); + } + ast::StructRest::None if !fields.is_empty() => self.word(","), + _ => {} + } + self.word("}"); + } + + fn print_expr_tup(&mut self, exprs: &[P]) { + self.popen(); + self.commasep_exprs(Inconsistent, exprs); + if exprs.len() == 1 { + self.word(","); + } + self.pclose() + } + + fn print_expr_call(&mut self, func: &ast::Expr, args: &[P]) { + let prec = match func.kind { + ast::ExprKind::Field(..) => parser::PREC_FORCE_PAREN, + _ => parser::PREC_POSTFIX, + }; + + self.print_expr_maybe_paren(func, prec); + self.print_call_post(args) + } + + fn print_expr_method_call(&mut self, segment: &ast::PathSegment, args: &[P]) { + let base_args = &args[1..]; + self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX); + self.word("."); + self.print_ident(segment.ident); + if let Some(ref args) = segment.args { + self.print_generic_args(args, true); + } + self.print_call_post(base_args) + } + + fn print_expr_binary(&mut self, op: ast::BinOp, lhs: &ast::Expr, rhs: &ast::Expr) { + let assoc_op = AssocOp::from_ast_binop(op.node); + let prec = assoc_op.precedence() as i8; + let fixity = assoc_op.fixity(); + + let (left_prec, right_prec) = match fixity { + Fixity::Left => (prec, prec + 1), + Fixity::Right => (prec + 1, prec), + Fixity::None => (prec + 1, prec + 1), + }; + + let left_prec = match (&lhs.kind, op.node) { + // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is + // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead + // of `(x as i32) < ...`. We need to convince it _not_ to do that. + (&ast::ExprKind::Cast { .. }, ast::BinOpKind::Lt | ast::BinOpKind::Shl) => { + parser::PREC_FORCE_PAREN + } + // We are given `(let _ = a) OP b`. + // + // - When `OP <= LAnd` we should print `let _ = a OP b` to avoid redundant parens + // as the parser will interpret this as `(let _ = a) OP b`. + // + // - Otherwise, e.g. when we have `(let a = b) < c` in AST, + // parens are required since the parser would interpret `let a = b < c` as + // `let a = (b < c)`. To achieve this, we force parens. + (&ast::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => { + parser::PREC_FORCE_PAREN + } + _ => left_prec, + }; + + self.print_expr_maybe_paren(lhs, left_prec); + self.space(); + self.word_space(op.node.to_string()); + self.print_expr_maybe_paren(rhs, right_prec) + } + + fn print_expr_unary(&mut self, op: ast::UnOp, expr: &ast::Expr) { + self.word(ast::UnOp::to_string(op)); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + fn print_expr_addr_of( + &mut self, + kind: ast::BorrowKind, + mutability: ast::Mutability, + expr: &ast::Expr, + ) { + self.word("&"); + match kind { + ast::BorrowKind::Ref => self.print_mutability(mutability, false), + ast::BorrowKind::Raw => { + self.word_nbsp("raw"); + self.print_mutability(mutability, true); + } + } + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX) + } + + pub fn print_expr(&mut self, expr: &ast::Expr) { + self.print_expr_outer_attr_style(expr, true) + } + + pub(super) fn print_expr_outer_attr_style(&mut self, expr: &ast::Expr, is_inline: bool) { + self.maybe_print_comment(expr.span.lo()); + + let attrs = &expr.attrs; + if is_inline { + self.print_outer_attributes_inline(attrs); + } else { + self.print_outer_attributes(attrs); + } + + self.ibox(INDENT_UNIT); + self.ann.pre(self, AnnNode::Expr(expr)); + match expr.kind { + ast::ExprKind::Box(ref expr) => { + self.word_space("box"); + self.print_expr_maybe_paren(expr, parser::PREC_PREFIX); + } + ast::ExprKind::Array(ref exprs) => { + self.print_expr_vec(exprs); + } + ast::ExprKind::ConstBlock(ref anon_const) => { + self.print_expr_anon_const(anon_const); + } + ast::ExprKind::Repeat(ref element, ref count) => { + self.print_expr_repeat(element, count); + } + ast::ExprKind::Struct(ref se) => { + self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest); + } + ast::ExprKind::Tup(ref exprs) => { + self.print_expr_tup(exprs); + } + ast::ExprKind::Call(ref func, ref args) => { + self.print_expr_call(func, &args); + } + ast::ExprKind::MethodCall(ref segment, ref args, _) => { + self.print_expr_method_call(segment, &args); + } + ast::ExprKind::Binary(op, ref lhs, ref rhs) => { + self.print_expr_binary(op, lhs, rhs); + } + ast::ExprKind::Unary(op, ref expr) => { + self.print_expr_unary(op, expr); + } + ast::ExprKind::AddrOf(k, m, ref expr) => { + self.print_expr_addr_of(k, m, expr); + } + ast::ExprKind::Lit(ref lit) => { + self.print_literal(lit); + } + ast::ExprKind::Cast(ref expr, ref ty) => { + let prec = AssocOp::As.precedence() as i8; + self.print_expr_maybe_paren(expr, prec); + self.space(); + self.word_space("as"); + self.print_type(ty); + } + ast::ExprKind::Type(ref expr, ref ty) => { + let prec = AssocOp::Colon.precedence() as i8; + self.print_expr_maybe_paren(expr, prec); + self.word_space(":"); + self.print_type(ty); + } + ast::ExprKind::Let(ref pat, ref scrutinee, _) => { + self.print_let(pat, scrutinee); + } + ast::ExprKind::If(ref test, ref blk, ref elseopt) => { + self.print_if(test, blk, elseopt.as_deref()) + } + ast::ExprKind::While(ref test, ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.head("while"); + self.print_expr_as_cond(test); + self.space(); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::ForLoop(ref pat, ref iter, ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.head("for"); + self.print_pat(pat); + self.space(); + self.word_space("in"); + self.print_expr_as_cond(iter); + self.space(); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Loop(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + self.head("loop"); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Match(ref expr, ref arms) => { + self.cbox(INDENT_UNIT); + self.ibox(INDENT_UNIT); + self.word_nbsp("match"); + self.print_expr_as_cond(expr); + self.space(); + self.bopen(); + self.print_inner_attributes_no_trailing_hardbreak(attrs); + for arm in arms { + self.print_arm(arm); + } + let empty = attrs.is_empty() && arms.is_empty(); + self.bclose(expr.span, empty); + } + ast::ExprKind::Closure( + capture_clause, + asyncness, + movability, + ref decl, + ref body, + _, + ) => { + self.print_movability(movability); + self.print_asyncness(asyncness); + self.print_capture_clause(capture_clause); + + self.print_fn_params_and_ret(decl, true); + self.space(); + self.print_expr(body); + self.end(); // need to close a box + + // a box will be closed by print_expr, but we didn't want an overall + // wrapper so we closed the corresponding opening. so create an + // empty box to satisfy the close. + self.ibox(0); + } + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + // containing cbox, will be closed by print-block at } + self.cbox(INDENT_UNIT); + // head-box, will be closed by print-block after { + self.ibox(0); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Async(capture_clause, _, ref blk) => { + self.word_nbsp("async"); + self.print_capture_clause(capture_clause); + // cbox/ibox in analogy to the `ExprKind::Block` arm above + self.cbox(INDENT_UNIT); + self.ibox(0); + self.print_block_with_attrs(blk, attrs); + } + ast::ExprKind::Await(ref expr) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word(".await"); + } + ast::ExprKind::Assign(ref lhs, ref rhs, _) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(lhs, prec + 1); + self.space(); + self.word_space("="); + self.print_expr_maybe_paren(rhs, prec); + } + ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => { + let prec = AssocOp::Assign.precedence() as i8; + self.print_expr_maybe_paren(lhs, prec + 1); + self.space(); + self.word(op.node.to_string()); + self.word_space("="); + self.print_expr_maybe_paren(rhs, prec); + } + ast::ExprKind::Field(ref expr, ident) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word("."); + self.print_ident(ident); + } + ast::ExprKind::Index(ref expr, ref index) => { + self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); + self.word("["); + self.print_expr(index); + self.word("]"); + } + ast::ExprKind::Range(ref start, ref end, limits) => { + // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence + // than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`. + // Here we use a fake precedence value so that any child with lower precedence than + // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.) + let fake_prec = AssocOp::LOr.precedence() as i8; + if let Some(ref e) = *start { + self.print_expr_maybe_paren(e, fake_prec); + } + if limits == ast::RangeLimits::HalfOpen { + self.word(".."); + } else { + self.word("..="); + } + if let Some(ref e) = *end { + self.print_expr_maybe_paren(e, fake_prec); + } + } + ast::ExprKind::Underscore => self.word("_"), + ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), + ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), + ast::ExprKind::Break(opt_label, ref opt_expr) => { + self.word("break"); + if let Some(label) = opt_label { + self.space(); + self.print_ident(label.ident); + } + if let Some(ref expr) = *opt_expr { + self.space(); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::Continue(opt_label) => { + self.word("continue"); + if let Some(label) = opt_label { + self.space(); + self.print_ident(label.ident); + } + } + ast::ExprKind::Ret(ref result) => { + self.word("return"); + if let Some(ref expr) = *result { + self.word(" "); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::InlineAsm(ref a) => { + self.word("asm!"); + self.print_inline_asm(a); + } + ast::ExprKind::MacCall(ref m) => self.print_mac(m), + ast::ExprKind::Paren(ref e) => { + self.popen(); + self.print_expr(e); + self.pclose(); + } + ast::ExprKind::Yield(ref e) => { + self.word("yield"); + + if let Some(ref expr) = *e { + self.space(); + self.print_expr_maybe_paren(expr, parser::PREC_JUMP); + } + } + ast::ExprKind::Try(ref e) => { + self.print_expr_maybe_paren(e, parser::PREC_POSTFIX); + self.word("?") + } + ast::ExprKind::TryBlock(ref blk) => { + self.head("try"); + self.print_block_with_attrs(blk, attrs) + } + ast::ExprKind::Err => { + self.popen(); + self.word("/*ERROR*/"); + self.pclose() + } + } + self.ann.post(self, AnnNode::Expr(expr)); + self.end(); + } + + fn print_arm(&mut self, arm: &ast::Arm) { + // Note, I have no idea why this check is necessary, but here it is. + if arm.attrs.is_empty() { + self.space(); + } + self.cbox(INDENT_UNIT); + self.ibox(0); + self.maybe_print_comment(arm.pat.span.lo()); + self.print_outer_attributes(&arm.attrs); + self.print_pat(&arm.pat); + self.space(); + if let Some(ref e) = arm.guard { + self.word_space("if"); + self.print_expr(e); + self.space(); + } + self.word_space("=>"); + + match arm.body.kind { + ast::ExprKind::Block(ref blk, opt_label) => { + if let Some(label) = opt_label { + self.print_ident(label.ident); + self.word_space(":"); + } + + // The block will close the pattern's ibox. + self.print_block_unclosed_indent(blk); + + // If it is a user-provided unsafe block, print a comma after it. + if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { + self.word(","); + } + } + _ => { + self.end(); // Close the ibox for the pattern. + self.print_expr(&arm.body); + self.word(","); + } + } + self.end(); // Close enclosing cbox. + } + + fn print_movability(&mut self, movability: ast::Movability) { + match movability { + ast::Movability::Static => self.word_space("static"), + ast::Movability::Movable => {} + } + } + + fn print_capture_clause(&mut self, capture_clause: ast::CaptureBy) { + match capture_clause { + ast::CaptureBy::Value => self.word_space("move"), + ast::CaptureBy::Ref => {} + } + } +} From 07a0325137b3e7e1c036d5301e03cf590ea1ce1e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 14 Jan 2022 20:59:54 -0800 Subject: [PATCH 10/18] Move item-related pretty printing functions to module --- compiler/rustc_ast_pretty/src/pprust/state.rs | 638 +---------------- .../rustc_ast_pretty/src/pprust/state/item.rs | 644 ++++++++++++++++++ 2 files changed, 646 insertions(+), 636 deletions(-) create mode 100644 compiler/rustc_ast_pretty/src/pprust/state/item.rs diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 70202a380cc8b..487451466f1f0 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1,4 +1,5 @@ mod expr; +mod item; use crate::pp::Breaks::{Consistent, Inconsistent}; use crate::pp::{self, Breaks}; @@ -11,7 +12,7 @@ use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle}; use rustc_ast::util::parser; use rustc_ast::{self as ast, BlockCheckMode, PatKind, RangeEnd, RangeSyntax}; use rustc_ast::{attr, Term}; -use rustc_ast::{GenericArg, MacArgs, ModKind}; +use rustc_ast::{GenericArg, MacArgs}; use rustc_ast::{GenericBound, SelfKind, TraitBoundModifier}; use rustc_ast::{InlineAsmOperand, InlineAsmRegOrRegClass}; use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; @@ -212,10 +213,6 @@ pub fn literal_to_string(lit: token::Lit) -> String { out } -fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { - format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) -} - impl std::ops::Deref for State<'_> { type Target = pp::Printer; fn deref(&self) -> &Self::Target { @@ -940,13 +937,6 @@ impl<'a> State<'a> { self.commasep_cmnt(b, exprs, |s, e| s.print_expr(e), |e| e.span) } - crate fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) { - self.print_inner_attributes(attrs); - for item in &nmod.items { - self.print_foreign_item(item); - } - } - pub fn print_opt_lifetime(&mut self, lifetime: &Option) { if let Some(lt) = *lifetime { self.print_lifetime(lt); @@ -1059,343 +1049,6 @@ impl<'a> State<'a> { self.end(); } - crate fn print_foreign_item(&mut self, item: &ast::ForeignItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; - self.ann.pre(self, AnnNode::SubItem(id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(span.lo()); - self.print_outer_attributes(attrs); - match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); - } - ast::ForeignItemKind::Static(ty, mutbl, body) => { - let def = ast::Defaultness::Final; - self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); - } - ast::ForeignItemKind::TyAlias(box ast::TyAlias { - defaultness, - generics, - bounds, - ty, - }) => { - self.print_associated_type( - ident, - generics, - bounds, - ty.as_deref(), - vis, - *defaultness, - ); - } - ast::ForeignItemKind::MacCall(m) => { - self.print_mac(m); - if m.args.need_semicolon() { - self.word(";"); - } - } - } - self.ann.post(self, AnnNode::SubItem(id)) - } - - fn print_item_const( - &mut self, - ident: Ident, - mutbl: Option, - ty: &ast::Ty, - body: Option<&ast::Expr>, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - ) { - self.head(""); - self.print_visibility(vis); - self.print_defaultness(defaultness); - let leading = match mutbl { - None => "const", - Some(ast::Mutability::Not) => "static", - Some(ast::Mutability::Mut) => "static mut", - }; - self.word_space(leading); - self.print_ident(ident); - self.word_space(":"); - self.print_type(ty); - if body.is_some() { - self.space(); - } - self.end(); // end the head-ibox - if let Some(body) = body { - self.word_space("="); - self.print_expr(body); - } - self.word(";"); - self.end(); // end the outer cbox - } - - fn print_associated_type( - &mut self, - ident: Ident, - generics: &ast::Generics, - bounds: &ast::GenericBounds, - ty: Option<&ast::Ty>, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - ) { - self.head(""); - self.print_visibility(vis); - self.print_defaultness(defaultness); - self.word_space("type"); - self.print_ident(ident); - self.print_generic_params(&generics.params); - self.print_type_bounds(":", bounds); - self.print_where_clause(&generics.where_clause); - if let Some(ty) = ty { - self.space(); - self.word_space("="); - self.print_type(ty); - } - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - - /// Pretty-prints an item. - crate fn print_item(&mut self, item: &ast::Item) { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(item.span.lo()); - self.print_outer_attributes(&item.attrs); - self.ann.pre(self, AnnNode::Item(item)); - match item.kind { - ast::ItemKind::ExternCrate(orig_name) => { - self.head(visibility_qualified(&item.vis, "extern crate")); - if let Some(orig_name) = orig_name { - self.print_name(orig_name); - self.space(); - self.word("as"); - self.space(); - } - self.print_ident(item.ident); - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - ast::ItemKind::Use(ref tree) => { - self.head(visibility_qualified(&item.vis, "use")); - self.print_use_tree(tree); - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - ast::ItemKind::Static(ref ty, mutbl, ref body) => { - let def = ast::Defaultness::Final; - self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def); - } - ast::ItemKind::Const(def, ref ty, ref body) => { - self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def); - } - ast::ItemKind::Fn(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => { - let body = body.as_deref(); - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - defaultness, - body, - &item.attrs, - ); - } - ast::ItemKind::Mod(unsafety, ref mod_kind) => { - self.head(Self::to_string(|s| { - s.print_visibility(&item.vis); - s.print_unsafety(unsafety); - s.word("mod"); - })); - self.print_ident(item.ident); - - match mod_kind { - ModKind::Loaded(items, ..) => { - self.nbsp(); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for item in items { - self.print_item(item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ModKind::Unloaded => { - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - } - } - ast::ItemKind::ForeignMod(ref nmod) => { - self.head(Self::to_string(|s| { - s.print_unsafety(nmod.unsafety); - s.word("extern"); - })); - if let Some(abi) = nmod.abi { - self.print_literal(&abi.as_lit()); - self.nbsp(); - } - self.bopen(); - self.print_foreign_mod(nmod, &item.attrs); - let empty = item.attrs.is_empty() && nmod.items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::GlobalAsm(ref asm) => { - self.head(visibility_qualified(&item.vis, "global_asm!")); - self.print_inline_asm(asm); - self.end(); - } - ast::ItemKind::TyAlias(box ast::TyAlias { - defaultness, - ref generics, - ref bounds, - ref ty, - }) => { - let ty = ty.as_deref(); - self.print_associated_type( - item.ident, - generics, - bounds, - ty, - &item.vis, - defaultness, - ); - } - ast::ItemKind::Enum(ref enum_definition, ref params) => { - self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); - } - ast::ItemKind::Struct(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "struct")); - self.print_struct(struct_def, generics, item.ident, item.span, true); - } - ast::ItemKind::Union(ref struct_def, ref generics) => { - self.head(visibility_qualified(&item.vis, "union")); - self.print_struct(struct_def, generics, item.ident, item.span, true); - } - ast::ItemKind::Impl(box ast::Impl { - unsafety, - polarity, - defaultness, - constness, - ref generics, - ref of_trait, - ref self_ty, - ref items, - }) => { - self.head(""); - self.print_visibility(&item.vis); - self.print_defaultness(defaultness); - self.print_unsafety(unsafety); - self.word("impl"); - - if generics.params.is_empty() { - self.nbsp(); - } else { - self.print_generic_params(&generics.params); - self.space(); - } - - self.print_constness(constness); - - if let ast::ImplPolarity::Negative(_) = polarity { - self.word("!"); - } - - if let Some(ref t) = *of_trait { - self.print_trait_ref(t); - self.space(); - self.word_space("for"); - } - - self.print_type(self_ty); - self.print_where_clause(&generics.where_clause); - - self.space(); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for impl_item in items { - self.print_assoc_item(impl_item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::Trait(box ast::Trait { - is_auto, - unsafety, - ref generics, - ref bounds, - ref items, - .. - }) => { - self.head(""); - self.print_visibility(&item.vis); - self.print_unsafety(unsafety); - self.print_is_auto(is_auto); - self.word_nbsp("trait"); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b.clone()); - } - } - self.print_type_bounds(":", &real_bounds); - self.print_where_clause(&generics.where_clause); - self.word(" "); - self.bopen(); - self.print_inner_attributes(&item.attrs); - for trait_item in items { - self.print_assoc_item(trait_item); - } - let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); - } - ast::ItemKind::TraitAlias(ref generics, ref bounds) => { - self.head(visibility_qualified(&item.vis, "trait")); - self.print_ident(item.ident); - self.print_generic_params(&generics.params); - let mut real_bounds = Vec::with_capacity(bounds.len()); - // FIXME(durka) this seems to be some quite outdated syntax - for b in bounds.iter() { - if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { - self.space(); - self.word_space("for ?"); - self.print_trait_ref(&ptr.trait_ref); - } else { - real_bounds.push(b.clone()); - } - } - self.nbsp(); - self.print_type_bounds("=", &real_bounds); - self.print_where_clause(&generics.where_clause); - self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block - } - ast::ItemKind::MacCall(ref mac) => { - self.print_mac(mac); - if mac.args.need_semicolon() { - self.word(";"); - } - } - ast::ItemKind::MacroDef(ref macro_def) => { - self.print_mac_def(macro_def, &item.ident, &item.span, |state| { - state.print_visibility(&item.vis) - }); - } - } - self.ann.post(self, AnnNode::Item(item)) - } - fn print_trait_ref(&mut self, t: &ast::TraitRef) { self.print_path(&t.path, false, 0) } @@ -1413,167 +1066,6 @@ impl<'a> State<'a> { self.print_trait_ref(&t.trait_ref) } - crate fn print_enum_def( - &mut self, - enum_definition: &ast::EnumDef, - generics: &ast::Generics, - ident: Ident, - span: rustc_span::Span, - visibility: &ast::Visibility, - ) { - self.head(visibility_qualified(visibility, "enum")); - self.print_ident(ident); - self.print_generic_params(&generics.params); - self.print_where_clause(&generics.where_clause); - self.space(); - self.print_variants(&enum_definition.variants, span) - } - - crate fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { - self.bopen(); - for v in variants { - self.space_if_not_bol(); - self.maybe_print_comment(v.span.lo()); - self.print_outer_attributes(&v.attrs); - self.ibox(INDENT_UNIT); - self.print_variant(v); - self.word(","); - self.end(); - self.maybe_print_trailing_comment(v.span, None); - } - let empty = variants.is_empty(); - self.bclose(span, empty) - } - - crate fn print_visibility(&mut self, vis: &ast::Visibility) { - match vis.kind { - ast::VisibilityKind::Public => self.word_nbsp("pub"), - ast::VisibilityKind::Crate(sugar) => match sugar { - ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), - ast::CrateSugar::JustCrate => self.word_nbsp("crate"), - }, - ast::VisibilityKind::Restricted { ref path, .. } => { - let path = Self::to_string(|s| s.print_path(path, false, 0)); - if path == "self" || path == "super" { - self.word_nbsp(format!("pub({})", path)) - } else { - self.word_nbsp(format!("pub(in {})", path)) - } - } - ast::VisibilityKind::Inherited => {} - } - } - - crate fn print_defaultness(&mut self, defaultness: ast::Defaultness) { - if let ast::Defaultness::Default(_) = defaultness { - self.word_nbsp("default"); - } - } - - crate fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { - self.nbsp(); - self.bopen(); - - let empty = fields.is_empty(); - if !empty { - self.hardbreak_if_not_bol(); - - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.word(","); - } - } - - self.bclose(span, empty); - } - - crate fn print_struct( - &mut self, - struct_def: &ast::VariantData, - generics: &ast::Generics, - ident: Ident, - span: rustc_span::Span, - print_finalizer: bool, - ) { - self.print_ident(ident); - self.print_generic_params(&generics.params); - match struct_def { - ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { - if let ast::VariantData::Tuple(..) = struct_def { - self.popen(); - self.commasep(Inconsistent, struct_def.fields(), |s, field| { - s.maybe_print_comment(field.span.lo()); - s.print_outer_attributes(&field.attrs); - s.print_visibility(&field.vis); - s.print_type(&field.ty) - }); - self.pclose(); - } - self.print_where_clause(&generics.where_clause); - if print_finalizer { - self.word(";"); - } - self.end(); - self.end(); // Close the outer-box. - } - ast::VariantData::Struct(ref fields, ..) => { - self.print_where_clause(&generics.where_clause); - self.print_record_struct_body(fields, span); - } - } - } - - crate fn print_variant(&mut self, v: &ast::Variant) { - self.head(""); - self.print_visibility(&v.vis); - let generics = ast::Generics::default(); - self.print_struct(&v.data, &generics, v.ident, v.span, false); - if let Some(ref d) = v.disr_expr { - self.space(); - self.word_space("="); - self.print_expr(&d.value) - } - } - - crate fn print_assoc_item(&mut self, item: &ast::AssocItem) { - let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; - self.ann.pre(self, AnnNode::SubItem(id)); - self.hardbreak_if_not_bol(); - self.maybe_print_comment(span.lo()); - self.print_outer_attributes(attrs); - match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); - } - ast::AssocItemKind::Const(def, ty, body) => { - self.print_item_const(ident, None, ty, body.as_deref(), vis, *def); - } - ast::AssocItemKind::TyAlias(box ast::TyAlias { defaultness, generics, bounds, ty }) => { - self.print_associated_type( - ident, - generics, - bounds, - ty.as_deref(), - vis, - *defaultness, - ); - } - ast::AssocItemKind::MacCall(m) => { - self.print_mac(m); - if m.args.need_semicolon() { - self.word(";"); - } - } - } - self.ann.post(self, AnnNode::SubItem(id)) - } - crate fn print_stmt(&mut self, st: &ast::Stmt) { self.maybe_print_comment(st.span.lo()); match st.kind { @@ -2012,55 +1504,6 @@ impl<'a> State<'a> { } } - fn print_fn_full( - &mut self, - sig: &ast::FnSig, - name: Ident, - generics: &ast::Generics, - vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, - attrs: &[ast::Attribute], - ) { - if body.is_some() { - self.head(""); - } - self.print_visibility(vis); - self.print_defaultness(defaultness); - self.print_fn(&sig.decl, sig.header, Some(name), generics); - if let Some(body) = body { - self.nbsp(); - self.print_block_with_attrs(body, attrs); - } else { - self.word(";"); - } - } - - crate fn print_fn( - &mut self, - decl: &ast::FnDecl, - header: ast::FnHeader, - name: Option, - generics: &ast::Generics, - ) { - self.print_fn_header_info(header); - if let Some(name) = name { - self.nbsp(); - self.print_ident(name); - } - self.print_generic_params(&generics.params); - self.print_fn_params_and_ret(decl, false); - self.print_where_clause(&generics.where_clause) - } - - crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { - let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") }; - self.word(open); - self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure)); - self.word(close); - self.print_fn_ret_ty(&decl.output) - } - crate fn print_asyncness(&mut self, asyncness: ast::Async) { if asyncness.is_async() { self.word_nbsp("async"); @@ -2161,83 +1604,6 @@ impl<'a> State<'a> { self.word(">"); } - crate fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { - if where_clause.predicates.is_empty() && !where_clause.has_where_token { - return; - } - - self.space(); - self.word_space("where"); - - for (i, predicate) in where_clause.predicates.iter().enumerate() { - if i != 0 { - self.word_space(","); - } - - self.print_where_predicate(predicate); - } - } - - pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { - match predicate { - ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - bound_generic_params, - bounded_ty, - bounds, - .. - }) => { - self.print_formal_generic_params(bound_generic_params); - self.print_type(bounded_ty); - self.print_type_bounds(":", bounds); - } - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { - lifetime, - bounds, - .. - }) => { - self.print_lifetime_bounds(*lifetime, bounds); - } - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { - self.print_type(lhs_ty); - self.space(); - self.word_space("="); - self.print_type(rhs_ty); - } - } - } - - crate fn print_use_tree(&mut self, tree: &ast::UseTree) { - match tree.kind { - ast::UseTreeKind::Simple(rename, ..) => { - self.print_path(&tree.prefix, false, 0); - if let Some(rename) = rename { - self.space(); - self.word_space("as"); - self.print_ident(rename); - } - } - ast::UseTreeKind::Glob => { - if !tree.prefix.segments.is_empty() { - self.print_path(&tree.prefix, false, 0); - self.word("::"); - } - self.word("*"); - } - ast::UseTreeKind::Nested(ref items) => { - if tree.prefix.segments.is_empty() { - self.word("{"); - } else { - self.print_path(&tree.prefix, false, 0); - self.word("::{"); - } - self.commasep(Inconsistent, &items, |this, &(ref tree, _)| { - this.print_use_tree(tree) - }); - self.word("}"); - } - } - } - pub fn print_mutability(&mut self, mutbl: ast::Mutability, print_const: bool) { match mutbl { ast::Mutability::Mut => self.word_nbsp("mut"), diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs new file mode 100644 index 0000000000000..c756b946b1e4a --- /dev/null +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -0,0 +1,644 @@ +use crate::pp::Breaks::Inconsistent; +use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT}; + +use rustc_ast as ast; +use rustc_ast::GenericBound; +use rustc_ast::ModKind; +use rustc_span::symbol::Ident; + +fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String { + format!("{}{}", State::to_string(|s| s.print_visibility(vis)), s) +} + +impl<'a> State<'a> { + fn print_foreign_mod(&mut self, nmod: &ast::ForeignMod, attrs: &[ast::Attribute]) { + self.print_inner_attributes(attrs); + for item in &nmod.items { + self.print_foreign_item(item); + } + } + + fn print_foreign_item(&mut self, item: &ast::ForeignItem) { + let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + self.ann.pre(self, AnnNode::SubItem(id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(span.lo()); + self.print_outer_attributes(attrs); + match kind { + ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + } + ast::ForeignItemKind::Static(ty, mutbl, body) => { + let def = ast::Defaultness::Final; + self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); + } + ast::ForeignItemKind::TyAlias(box ast::TyAlias { + defaultness, + generics, + bounds, + ty, + }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); + } + ast::ForeignItemKind::MacCall(m) => { + self.print_mac(m); + if m.args.need_semicolon() { + self.word(";"); + } + } + } + self.ann.post(self, AnnNode::SubItem(id)) + } + + fn print_item_const( + &mut self, + ident: Ident, + mutbl: Option, + ty: &ast::Ty, + body: Option<&ast::Expr>, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + ) { + self.head(""); + self.print_visibility(vis); + self.print_defaultness(defaultness); + let leading = match mutbl { + None => "const", + Some(ast::Mutability::Not) => "static", + Some(ast::Mutability::Mut) => "static mut", + }; + self.word_space(leading); + self.print_ident(ident); + self.word_space(":"); + self.print_type(ty); + if body.is_some() { + self.space(); + } + self.end(); // end the head-ibox + if let Some(body) = body { + self.word_space("="); + self.print_expr(body); + } + self.word(";"); + self.end(); // end the outer cbox + } + + fn print_associated_type( + &mut self, + ident: Ident, + generics: &ast::Generics, + bounds: &ast::GenericBounds, + ty: Option<&ast::Ty>, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + ) { + self.head(""); + self.print_visibility(vis); + self.print_defaultness(defaultness); + self.word_space("type"); + self.print_ident(ident); + self.print_generic_params(&generics.params); + self.print_type_bounds(":", bounds); + self.print_where_clause(&generics.where_clause); + if let Some(ty) = ty { + self.space(); + self.word_space("="); + self.print_type(ty); + } + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + + /// Pretty-prints an item. + crate fn print_item(&mut self, item: &ast::Item) { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(item.span.lo()); + self.print_outer_attributes(&item.attrs); + self.ann.pre(self, AnnNode::Item(item)); + match item.kind { + ast::ItemKind::ExternCrate(orig_name) => { + self.head(visibility_qualified(&item.vis, "extern crate")); + if let Some(orig_name) = orig_name { + self.print_name(orig_name); + self.space(); + self.word("as"); + self.space(); + } + self.print_ident(item.ident); + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + ast::ItemKind::Use(ref tree) => { + self.head(visibility_qualified(&item.vis, "use")); + self.print_use_tree(tree); + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + ast::ItemKind::Static(ref ty, mutbl, ref body) => { + let def = ast::Defaultness::Final; + self.print_item_const(item.ident, Some(mutbl), ty, body.as_deref(), &item.vis, def); + } + ast::ItemKind::Const(def, ref ty, ref body) => { + self.print_item_const(item.ident, None, ty, body.as_deref(), &item.vis, def); + } + ast::ItemKind::Fn(box ast::Fn { defaultness, ref sig, ref generics, ref body }) => { + let body = body.as_deref(); + self.print_fn_full( + sig, + item.ident, + generics, + &item.vis, + defaultness, + body, + &item.attrs, + ); + } + ast::ItemKind::Mod(unsafety, ref mod_kind) => { + self.head(Self::to_string(|s| { + s.print_visibility(&item.vis); + s.print_unsafety(unsafety); + s.word("mod"); + })); + self.print_ident(item.ident); + + match mod_kind { + ModKind::Loaded(items, ..) => { + self.nbsp(); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for item in items { + self.print_item(item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ModKind::Unloaded => { + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + } + } + ast::ItemKind::ForeignMod(ref nmod) => { + self.head(Self::to_string(|s| { + s.print_unsafety(nmod.unsafety); + s.word("extern"); + })); + if let Some(abi) = nmod.abi { + self.print_literal(&abi.as_lit()); + self.nbsp(); + } + self.bopen(); + self.print_foreign_mod(nmod, &item.attrs); + let empty = item.attrs.is_empty() && nmod.items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::GlobalAsm(ref asm) => { + self.head(visibility_qualified(&item.vis, "global_asm!")); + self.print_inline_asm(asm); + self.end(); + } + ast::ItemKind::TyAlias(box ast::TyAlias { + defaultness, + ref generics, + ref bounds, + ref ty, + }) => { + let ty = ty.as_deref(); + self.print_associated_type( + item.ident, + generics, + bounds, + ty, + &item.vis, + defaultness, + ); + } + ast::ItemKind::Enum(ref enum_definition, ref params) => { + self.print_enum_def(enum_definition, params, item.ident, item.span, &item.vis); + } + ast::ItemKind::Struct(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "struct")); + self.print_struct(struct_def, generics, item.ident, item.span, true); + } + ast::ItemKind::Union(ref struct_def, ref generics) => { + self.head(visibility_qualified(&item.vis, "union")); + self.print_struct(struct_def, generics, item.ident, item.span, true); + } + ast::ItemKind::Impl(box ast::Impl { + unsafety, + polarity, + defaultness, + constness, + ref generics, + ref of_trait, + ref self_ty, + ref items, + }) => { + self.head(""); + self.print_visibility(&item.vis); + self.print_defaultness(defaultness); + self.print_unsafety(unsafety); + self.word("impl"); + + if generics.params.is_empty() { + self.nbsp(); + } else { + self.print_generic_params(&generics.params); + self.space(); + } + + self.print_constness(constness); + + if let ast::ImplPolarity::Negative(_) = polarity { + self.word("!"); + } + + if let Some(ref t) = *of_trait { + self.print_trait_ref(t); + self.space(); + self.word_space("for"); + } + + self.print_type(self_ty); + self.print_where_clause(&generics.where_clause); + + self.space(); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for impl_item in items { + self.print_assoc_item(impl_item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::Trait(box ast::Trait { + is_auto, + unsafety, + ref generics, + ref bounds, + ref items, + .. + }) => { + self.head(""); + self.print_visibility(&item.vis); + self.print_unsafety(unsafety); + self.print_is_auto(is_auto); + self.word_nbsp("trait"); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { + self.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b.clone()); + } + } + self.print_type_bounds(":", &real_bounds); + self.print_where_clause(&generics.where_clause); + self.word(" "); + self.bopen(); + self.print_inner_attributes(&item.attrs); + for trait_item in items { + self.print_assoc_item(trait_item); + } + let empty = item.attrs.is_empty() && items.is_empty(); + self.bclose(item.span, empty); + } + ast::ItemKind::TraitAlias(ref generics, ref bounds) => { + self.head(visibility_qualified(&item.vis, "trait")); + self.print_ident(item.ident); + self.print_generic_params(&generics.params); + let mut real_bounds = Vec::with_capacity(bounds.len()); + // FIXME(durka) this seems to be some quite outdated syntax + for b in bounds.iter() { + if let GenericBound::Trait(ref ptr, ast::TraitBoundModifier::Maybe) = *b { + self.space(); + self.word_space("for ?"); + self.print_trait_ref(&ptr.trait_ref); + } else { + real_bounds.push(b.clone()); + } + } + self.nbsp(); + self.print_type_bounds("=", &real_bounds); + self.print_where_clause(&generics.where_clause); + self.word(";"); + self.end(); // end inner head-block + self.end(); // end outer head-block + } + ast::ItemKind::MacCall(ref mac) => { + self.print_mac(mac); + if mac.args.need_semicolon() { + self.word(";"); + } + } + ast::ItemKind::MacroDef(ref macro_def) => { + self.print_mac_def(macro_def, &item.ident, &item.span, |state| { + state.print_visibility(&item.vis) + }); + } + } + self.ann.post(self, AnnNode::Item(item)) + } + + fn print_enum_def( + &mut self, + enum_definition: &ast::EnumDef, + generics: &ast::Generics, + ident: Ident, + span: rustc_span::Span, + visibility: &ast::Visibility, + ) { + self.head(visibility_qualified(visibility, "enum")); + self.print_ident(ident); + self.print_generic_params(&generics.params); + self.print_where_clause(&generics.where_clause); + self.space(); + self.print_variants(&enum_definition.variants, span) + } + + fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { + self.bopen(); + for v in variants { + self.space_if_not_bol(); + self.maybe_print_comment(v.span.lo()); + self.print_outer_attributes(&v.attrs); + self.ibox(INDENT_UNIT); + self.print_variant(v); + self.word(","); + self.end(); + self.maybe_print_trailing_comment(v.span, None); + } + let empty = variants.is_empty(); + self.bclose(span, empty) + } + + crate fn print_visibility(&mut self, vis: &ast::Visibility) { + match vis.kind { + ast::VisibilityKind::Public => self.word_nbsp("pub"), + ast::VisibilityKind::Crate(sugar) => match sugar { + ast::CrateSugar::PubCrate => self.word_nbsp("pub(crate)"), + ast::CrateSugar::JustCrate => self.word_nbsp("crate"), + }, + ast::VisibilityKind::Restricted { ref path, .. } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if path == "self" || path == "super" { + self.word_nbsp(format!("pub({})", path)) + } else { + self.word_nbsp(format!("pub(in {})", path)) + } + } + ast::VisibilityKind::Inherited => {} + } + } + + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { + if let ast::Defaultness::Default(_) = defaultness { + self.word_nbsp("default"); + } + } + + fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { + self.nbsp(); + self.bopen(); + + let empty = fields.is_empty(); + if !empty { + self.hardbreak_if_not_bol(); + + for field in fields { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident.unwrap()); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.word(","); + } + } + + self.bclose(span, empty); + } + + fn print_struct( + &mut self, + struct_def: &ast::VariantData, + generics: &ast::Generics, + ident: Ident, + span: rustc_span::Span, + print_finalizer: bool, + ) { + self.print_ident(ident); + self.print_generic_params(&generics.params); + match struct_def { + ast::VariantData::Tuple(..) | ast::VariantData::Unit(..) => { + if let ast::VariantData::Tuple(..) = struct_def { + self.popen(); + self.commasep(Inconsistent, struct_def.fields(), |s, field| { + s.maybe_print_comment(field.span.lo()); + s.print_outer_attributes(&field.attrs); + s.print_visibility(&field.vis); + s.print_type(&field.ty) + }); + self.pclose(); + } + self.print_where_clause(&generics.where_clause); + if print_finalizer { + self.word(";"); + } + self.end(); + self.end(); // Close the outer-box. + } + ast::VariantData::Struct(ref fields, ..) => { + self.print_where_clause(&generics.where_clause); + self.print_record_struct_body(fields, span); + } + } + } + + crate fn print_variant(&mut self, v: &ast::Variant) { + self.head(""); + self.print_visibility(&v.vis); + let generics = ast::Generics::default(); + self.print_struct(&v.data, &generics, v.ident, v.span, false); + if let Some(ref d) = v.disr_expr { + self.space(); + self.word_space("="); + self.print_expr(&d.value) + } + } + + fn print_assoc_item(&mut self, item: &ast::AssocItem) { + let ast::Item { id, span, ident, ref attrs, ref kind, ref vis, tokens: _ } = *item; + self.ann.pre(self, AnnNode::SubItem(id)); + self.hardbreak_if_not_bol(); + self.maybe_print_comment(span.lo()); + self.print_outer_attributes(attrs); + match kind { + ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { + self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + } + ast::AssocItemKind::Const(def, ty, body) => { + self.print_item_const(ident, None, ty, body.as_deref(), vis, *def); + } + ast::AssocItemKind::TyAlias(box ast::TyAlias { defaultness, generics, bounds, ty }) => { + self.print_associated_type( + ident, + generics, + bounds, + ty.as_deref(), + vis, + *defaultness, + ); + } + ast::AssocItemKind::MacCall(m) => { + self.print_mac(m); + if m.args.need_semicolon() { + self.word(";"); + } + } + } + self.ann.post(self, AnnNode::SubItem(id)) + } + + fn print_fn_full( + &mut self, + sig: &ast::FnSig, + name: Ident, + generics: &ast::Generics, + vis: &ast::Visibility, + defaultness: ast::Defaultness, + body: Option<&ast::Block>, + attrs: &[ast::Attribute], + ) { + if body.is_some() { + self.head(""); + } + self.print_visibility(vis); + self.print_defaultness(defaultness); + self.print_fn(&sig.decl, sig.header, Some(name), generics); + if let Some(body) = body { + self.nbsp(); + self.print_block_with_attrs(body, attrs); + } else { + self.word(";"); + } + } + + crate fn print_fn( + &mut self, + decl: &ast::FnDecl, + header: ast::FnHeader, + name: Option, + generics: &ast::Generics, + ) { + self.print_fn_header_info(header); + if let Some(name) = name { + self.nbsp(); + self.print_ident(name); + } + self.print_generic_params(&generics.params); + self.print_fn_params_and_ret(decl, false); + self.print_where_clause(&generics.where_clause) + } + + crate fn print_fn_params_and_ret(&mut self, decl: &ast::FnDecl, is_closure: bool) { + let (open, close) = if is_closure { ("|", "|") } else { ("(", ")") }; + self.word(open); + self.commasep(Inconsistent, &decl.inputs, |s, param| s.print_param(param, is_closure)); + self.word(close); + self.print_fn_ret_ty(&decl.output) + } + + fn print_where_clause(&mut self, where_clause: &ast::WhereClause) { + if where_clause.predicates.is_empty() && !where_clause.has_where_token { + return; + } + + self.space(); + self.word_space("where"); + + for (i, predicate) in where_clause.predicates.iter().enumerate() { + if i != 0 { + self.word_space(","); + } + + self.print_where_predicate(predicate); + } + } + + pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { + match predicate { + ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + bound_generic_params, + bounded_ty, + bounds, + .. + }) => { + self.print_formal_generic_params(bound_generic_params); + self.print_type(bounded_ty); + self.print_type_bounds(":", bounds); + } + ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + lifetime, + bounds, + .. + }) => { + self.print_lifetime_bounds(*lifetime, bounds); + } + ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + self.print_type(lhs_ty); + self.space(); + self.word_space("="); + self.print_type(rhs_ty); + } + } + } + + fn print_use_tree(&mut self, tree: &ast::UseTree) { + match tree.kind { + ast::UseTreeKind::Simple(rename, ..) => { + self.print_path(&tree.prefix, false, 0); + if let Some(rename) = rename { + self.space(); + self.word_space("as"); + self.print_ident(rename); + } + } + ast::UseTreeKind::Glob => { + if !tree.prefix.segments.is_empty() { + self.print_path(&tree.prefix, false, 0); + self.word("::"); + } + self.word("*"); + } + ast::UseTreeKind::Nested(ref items) => { + if tree.prefix.segments.is_empty() { + self.word("{"); + } else { + self.print_path(&tree.prefix, false, 0); + self.word("::{"); + } + self.commasep(Inconsistent, &items, |this, &(ref tree, _)| { + this.print_use_tree(tree) + }); + self.word("}"); + } + } + } +} From 282224edf19fb8a511c5a9e279f18b804ff61b25 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 18 Jan 2022 22:17:34 +0100 Subject: [PATCH 11/18] Add Option::is_some_with. --- library/core/src/option.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 8adfb6f4bcf52..d9ee289f216b0 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -551,6 +551,27 @@ impl Option { matches!(*self, Some(_)) } + /// Returns `true` if the option is a [`Some`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// let x: Option = Some(2); + /// assert_eq!(x.is_some_with(|x| x > 1), true); + /// + /// let x: Option = Some(0); + /// assert_eq!(x.is_some_with(|x| x > 1), false); + /// + /// let x: Option = None; + /// assert_eq!(x.is_some_with(|x| x > 1), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "none")] + pub fn is_some_with(&self, f: impl FnOnce(&T) -> bool) -> bool { + matches!(self, Some(x) if f(x)) + } + /// Returns `true` if the option is a [`None`] value. /// /// # Examples From aaebae973f7a4fafd24cb6c9fad7a6beba205b74 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 18 Jan 2022 22:17:44 +0100 Subject: [PATCH 12/18] Add Result::{is_ok_with, is_err_with}. --- library/core/src/result.rs | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index b8f0d84746cce..f4f3d84e62bfd 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -542,6 +542,27 @@ impl Result { matches!(*self, Ok(_)) } + /// Returns `true` if the result is [`Ok`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// let x: Result = Ok(2); + /// assert_eq!(x.is_ok_with(|x| x > 1), true); + /// + /// let x: Result = Ok(0); + /// assert_eq!(x.is_ok_with(|x| x > 1), false); + /// + /// let x: Result = Err("hey"); + /// assert_eq!(x.is_ok_with(|x| x > 1), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "none")] + pub fn is_ok_with(&self, f: impl FnOnce(&T) -> bool) -> bool { + matches!(self, Ok(x) if f(x)) + } + /// Returns `true` if the result is [`Err`]. /// /// # Examples @@ -563,6 +584,27 @@ impl Result { !self.is_ok() } + /// Returns `true` if the result is [`Err`] wrapping a value matching the predicate. + /// + /// # Examples + /// + /// ``` + /// let x: Result = Err("abc"); + /// assert_eq!(x.is_err_with(|x| x.len() > 1), true); + /// + /// let x: Result = Err(""); + /// assert_eq!(x.is_ok_with(|x| x.len() > 1), false); + /// + /// let x: Result = Ok(123); + /// assert_eq!(x.is_ok_with(|x| x.len() > 1), false); + /// ``` + #[must_use] + #[inline] + #[unstable(feature = "is_some_with", issue = "none")] + pub fn is_err_with(&self, f: impl FnOnce(&E) -> bool) -> bool { + matches!(self, Err(x) if f(x)) + } + ///////////////////////////////////////////////////////////////////////// // Adapter for each variant ///////////////////////////////////////////////////////////////////////// From 148234ff73aa649178f92f529b9f5b0fc5adf3ed Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 18 Jan 2022 22:18:16 +0100 Subject: [PATCH 13/18] Add is_some_with tracking issue number. --- library/core/src/option.rs | 2 +- library/core/src/result.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index d9ee289f216b0..e8850176f7cd3 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -567,7 +567,7 @@ impl Option { /// ``` #[must_use] #[inline] - #[unstable(feature = "is_some_with", issue = "none")] + #[unstable(feature = "is_some_with", issue = "93050")] pub fn is_some_with(&self, f: impl FnOnce(&T) -> bool) -> bool { matches!(self, Some(x) if f(x)) } diff --git a/library/core/src/result.rs b/library/core/src/result.rs index f4f3d84e62bfd..82d99cef45860 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -558,7 +558,7 @@ impl Result { /// ``` #[must_use] #[inline] - #[unstable(feature = "is_some_with", issue = "none")] + #[unstable(feature = "is_some_with", issue = "93050")] pub fn is_ok_with(&self, f: impl FnOnce(&T) -> bool) -> bool { matches!(self, Ok(x) if f(x)) } @@ -600,7 +600,7 @@ impl Result { /// ``` #[must_use] #[inline] - #[unstable(feature = "is_some_with", issue = "none")] + #[unstable(feature = "is_some_with", issue = "93050")] pub fn is_err_with(&self, f: impl FnOnce(&E) -> bool) -> bool { matches!(self, Err(x) if f(x)) } From 45dee47fec4b04fc642361cbf1f2f9fc0e0ad53d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 18 Jan 2022 22:53:43 +0100 Subject: [PATCH 14/18] Improve is_err_with example. --- library/core/src/result.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 82d99cef45860..9991112e7f553 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -589,14 +589,16 @@ impl Result { /// # Examples /// /// ``` - /// let x: Result = Err("abc"); - /// assert_eq!(x.is_err_with(|x| x.len() > 1), true); + /// use std::io::{Error, ErrorKind}; /// - /// let x: Result = Err(""); - /// assert_eq!(x.is_ok_with(|x| x.len() > 1), false); + /// let x: Result = Err(Error::new(ErrorKind::NotFound, "!")); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), true); /// - /// let x: Result = Ok(123); - /// assert_eq!(x.is_ok_with(|x| x.len() > 1), false); + /// let x: Result = Err(Error::new(ErrorKind::PermissionDenied, "!")); + /// assert_eq!(x.is_ok_with(|x| x.kind() == ErrorKind::NotFound), false); + /// + /// let x: Result = Ok(123); + /// assert_eq!(x.is_ok_with(|x| x.kind() == ErrorKind::NotFound), false); /// ``` #[must_use] #[inline] From 5f74ef4fb1d86d80a3052e772010e613353dbfa7 Mon Sep 17 00:00:00 2001 From: Caio Date: Tue, 18 Jan 2022 19:38:17 -0300 Subject: [PATCH 15/18] Formally implement let chains --- compiler/rustc_ast_lowering/src/expr.rs | 18 +- compiler/rustc_ast_passes/src/feature_gate.rs | 6 +- compiler/rustc_feature/src/active.rs | 2 +- compiler/rustc_middle/src/thir.rs | 2 +- .../rustc_mir_build/src/build/expr/into.rs | 16 +- .../rustc_mir_build/src/build/matches/mod.rs | 19 ++ compiler/rustc_mir_build/src/build/scope.rs | 2 +- compiler/rustc_mir_build/src/thir/cx/expr.rs | 1 - .../src/thir/pattern/check_match.rs | 43 +++- src/test/ui/expr/if/attrs/let-chains-attr.rs | 2 +- .../ui/expr/if/attrs/let-chains-attr.stderr | 11 - src/test/ui/mir/mir_let_chains_drop_order.rs | 93 ++++++++ src/test/ui/pattern/issue-82290.rs | 9 - src/test/ui/pattern/issue-82290.stderr | 21 -- .../ui/rfc-2294-if-let-guard/feature-gate.rs | 32 +-- .../rfc-2294-if-let-guard/feature-gate.stderr | 48 ++-- .../ast-lowering-does-not-wrap-let-chains.rs | 15 ++ .../ast-pretty-check.rs | 2 +- .../ast-pretty-check.stdout | 2 +- .../chains-without-let.rs | 20 ++ .../chains-without-let.stderr | 21 ++ .../disallowed-positions.rs | 1 - .../disallowed-positions.stderr | 221 +++++++++--------- .../ui/rfc-2497-if-let-chains/feature-gate.rs | 64 ++--- .../feature-gate.stderr | 96 +++----- .../irrefutable-lets.rs | 27 +++ .../ui/rfc-2497-if-let-chains/issue-88498.rs | 16 ++ .../ui/rfc-2497-if-let-chains/issue-90722.rs | 11 + .../ui/rfc-2497-if-let-chains/issue-92145.rs | 11 + .../no-double-assigments.rs | 9 + .../then-else-blocks.rs | 35 +++ 31 files changed, 536 insertions(+), 340 deletions(-) delete mode 100644 src/test/ui/expr/if/attrs/let-chains-attr.stderr create mode 100644 src/test/ui/mir/mir_let_chains_drop_order.rs delete mode 100644 src/test/ui/pattern/issue-82290.rs delete mode 100644 src/test/ui/pattern/issue-82290.stderr create mode 100644 src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/chains-without-let.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/chains-without-let.stderr create mode 100644 src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-88498.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-90722.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/issue-92145.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/no-double-assigments.rs create mode 100644 src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 470e9114217c6..6c172d59f837b 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -392,14 +392,20 @@ impl<'hir> LoweringContext<'_, 'hir> { // If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond` // in a temporary block. fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { - match cond.kind { - hir::ExprKind::Let(..) => cond, - _ => { - let span_block = - self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None); - self.expr_drop_temps(span_block, cond, AttrVec::new()) + fn has_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool { + match expr.kind { + hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), + hir::ExprKind::Let(..) => true, + _ => false, } } + if has_let_expr(cond) { + cond + } else { + let reason = DesugaringKind::CondTemporary; + let span_block = self.mark_span_with_reason(reason, cond.span, None); + self.expr_drop_temps(span_block, cond, AttrVec::new()) + } } // We desugar: `'label: while $cond $body` into: diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 89671788255a9..a6ecfa4520608 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -707,11 +707,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { "`if let` guards are experimental", "you can write `if matches!(, )` instead of `if let = `" ); - gate_all!( - let_chains, - "`let` expressions in this position are experimental", - "you can write `matches!(, )` instead of `let = `" - ); + gate_all!(let_chains, "`let` expressions in this position are unstable"); gate_all!( async_closure, "async closures are unstable", diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 47010ea3ab613..0b65a5ff3ecc3 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -415,7 +415,7 @@ declare_features! ( // Allows setting the threshold for the `large_assignments` lint. (active, large_assignments, "1.52.0", Some(83518), None), /// Allows `if/while p && let q = r && ...` chains. - (incomplete, let_chains, "1.37.0", Some(53667), None), + (active, let_chains, "1.37.0", Some(53667), None), /// Allows `let...else` statements. (active, let_else, "1.56.0", Some(87335), None), /// Allows `#[link(..., cfg(..))]`. diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index eef42666f2a07..11dc69ab71566 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -213,7 +213,7 @@ pub struct Expr<'tcx> { #[derive(Debug, HashStable)] pub enum ExprKind<'tcx> { - /// `Scope`s are used to explicitely mark destruction scopes, + /// `Scope`s are used to explicitly mark destruction scopes, /// and to track the `HirId` of the expressions within the scope. Scope { region_scope: region::Scope, diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 43060ecfced12..da8fbdbf3bce4 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -90,17 +90,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; let join_block = this.cfg.start_new_block(); - this.cfg.terminate( - then_blk, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - this.cfg.terminate( - else_blk, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - + this.cfg.goto(then_blk, source_info, join_block); + this.cfg.goto(else_blk, source_info, join_block); join_block.unit() } ExprKind::Let { expr, ref pat } => { @@ -109,8 +100,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.lower_let_expr(block, &this.thir[expr], pat, scope, expr_span) }); - let join_block = this.cfg.start_new_block(); - this.cfg.push_assign_constant( true_block, source_info, @@ -133,6 +122,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); + let join_block = this.cfg.start_new_block(); this.cfg.goto(true_block, source_info, join_block); this.cfg.goto(false_block, source_info, join_block); join_block.unit() diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index e3a05e01ea8f0..85950d8241940 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -47,6 +47,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let expr_span = expr.span; match expr.kind { + ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => { + let lhs_then_block = unpack!(this.then_else_break( + block, + &this.thir[lhs], + temp_scope_override, + break_scope, + variable_scope_span, + )); + + let rhs_then_block = unpack!(this.then_else_break( + lhs_then_block, + &this.thir[rhs], + temp_scope_override, + break_scope, + variable_scope_span, + )); + + rhs_then_block.unit() + } ExprKind::Scope { region_scope, lint_level, value } => { let region_scope = (region_scope, this.source_info(expr_span)); this.in_scope(region_scope, lint_level, |this| { diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index fc46c54c2fc35..84d6c1d2db87f 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -498,7 +498,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// if let Some(x) = a && let Some(y) = b && let Some(z) = c { ... } /// - /// there are three possible ways the condition can be false and we may have + /// There are three possible ways the condition can be false and we may have /// to drop `x`, `x` and `y`, or neither depending on which binding fails. /// To handle this correctly we use a `DropTree` in a similar way to a /// `loop` expression and 'break' out on all of the 'else' paths. diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 750677f161e44..a43388808cd5c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -315,7 +315,6 @@ impl<'tcx> Cx<'tcx> { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs), }, - _ => { let op = bin_op(op.node); ExprKind::Binary { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 868dd195f3a63..34204c3852ad0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -17,6 +17,7 @@ use rustc_session::lint::builtin::{ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, }; use rustc_session::Session; +use rustc_span::source_map::Spanned; use rustc_span::{DesugaringKind, ExpnKind, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { @@ -445,6 +446,10 @@ fn check_let_reachability<'p, 'tcx>( pat: &'p DeconstructedPat<'p, 'tcx>, span: Span, ) { + if is_let_chain(cx.tcx, pat_id) { + return; + } + let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }]; let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty()); @@ -764,8 +769,11 @@ pub enum LetSource { fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { let hir = tcx.hir(); + let parent = hir.get_parent_node(pat_id); - match hir.get(parent) { + let parent_node = hir.get(parent); + + match parent_node { hir::Node::Arm(hir::Arm { guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)), .. @@ -780,6 +788,7 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { } _ => {} } + let parent_parent = hir.get_parent_node(parent); let parent_parent_node = hir.get(parent_parent); @@ -792,12 +801,30 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource { .. }) = parent_parent_parent_parent_node { - LetSource::WhileLet - } else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) = - parent_parent_node - { - LetSource::IfLet - } else { - LetSource::GenericLet + return LetSource::WhileLet; + } + + if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(..), .. }) = parent_parent_node { + return LetSource::IfLet; } + + LetSource::GenericLet +} + +// Since this function is called within a let context, it is reasonable to assume that any parent +// `&&` infers a let chain +fn is_let_chain(tcx: TyCtxt<'_>, pat_id: HirId) -> bool { + let hir = tcx.hir(); + let parent = hir.get_parent_node(pat_id); + let parent_parent = hir.get_parent_node(parent); + matches!( + hir.get(parent_parent), + hir::Node::Expr( + hir::Expr { + kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, ..), + .. + }, + .. + ) + ) } diff --git a/src/test/ui/expr/if/attrs/let-chains-attr.rs b/src/test/ui/expr/if/attrs/let-chains-attr.rs index 5237a9ff3961a..2cd8731141af7 100644 --- a/src/test/ui/expr/if/attrs/let-chains-attr.rs +++ b/src/test/ui/expr/if/attrs/let-chains-attr.rs @@ -1,6 +1,6 @@ // check-pass -#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete +#![feature(let_chains)] #[cfg(FALSE)] fn foo() { diff --git a/src/test/ui/expr/if/attrs/let-chains-attr.stderr b/src/test/ui/expr/if/attrs/let-chains-attr.stderr deleted file mode 100644 index 8b9874715342c..0000000000000 --- a/src/test/ui/expr/if/attrs/let-chains-attr.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/let-chains-attr.rs:3:12 - | -LL | #![feature(let_chains)] - | ^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53667 for more information - -warning: 1 warning emitted - diff --git a/src/test/ui/mir/mir_let_chains_drop_order.rs b/src/test/ui/mir/mir_let_chains_drop_order.rs new file mode 100644 index 0000000000000..01f943c87dd7f --- /dev/null +++ b/src/test/ui/mir/mir_let_chains_drop_order.rs @@ -0,0 +1,93 @@ +// run-pass +// needs-unwind +// ignore-wasm32-bare compiled with panic=abort by default + +// See `mir_drop_order.rs` for more information + +#![feature(let_chains)] + +use std::cell::RefCell; +use std::panic; + +pub struct DropLogger<'a, T> { + extra: T, + id: usize, + log: &'a panic::AssertUnwindSafe>> +} + +impl<'a, T> Drop for DropLogger<'a, T> { + fn drop(&mut self) { + self.log.0.borrow_mut().push(self.id); + } +} + +struct InjectedFailure; + +#[allow(unreachable_code)] +fn main() { + let log = panic::AssertUnwindSafe(RefCell::new(vec![])); + let d = |id, extra| DropLogger { extra, id: id, log: &log }; + let get = || -> Vec<_> { + let mut m = log.0.borrow_mut(); + let n = m.drain(..); + n.collect() + }; + + { + let _x = ( + d( + 0, + d( + 1, + if let Some(_) = d(2, Some(true)).extra && let DropLogger { .. } = d(3, None) { + None + } else { + Some(true) + } + ).extra + ), + d(4, None), + &d(5, None), + d(6, None), + if let DropLogger { .. } = d(7, None) && let DropLogger { .. } = d(8, None) { + d(9, None) + } + else { + // 10 is not constructed + d(10, None) + } + ); + assert_eq!(get(), vec![3, 8, 7, 1, 2]); + } + assert_eq!(get(), vec![0, 4, 6, 9, 5]); + + let _ = std::panic::catch_unwind(|| { + ( + d( + 11, + d( + 12, + if let Some(_) = d(13, Some(true)).extra + && let DropLogger { .. } = d(14, None) + { + None + } else { + Some(true) + } + ).extra + ), + d(15, None), + &d(16, None), + d(17, None), + if let DropLogger { .. } = d(18, None) && let DropLogger { .. } = d(19, None) { + d(20, None) + } + else { + // 10 is not constructed + d(21, None) + }, + panic::panic_any(InjectedFailure) + ); + }); + assert_eq!(get(), vec![14, 19, 20, 17, 15, 11, 18, 16, 12, 13]); +} diff --git a/src/test/ui/pattern/issue-82290.rs b/src/test/ui/pattern/issue-82290.rs deleted file mode 100644 index d8da0ac8aa6c5..0000000000000 --- a/src/test/ui/pattern/issue-82290.rs +++ /dev/null @@ -1,9 +0,0 @@ -// check-pass - -#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete - -fn main() { - if true && let x = 1 { //~ WARN irrefutable `let` pattern - let _ = x; - } -} diff --git a/src/test/ui/pattern/issue-82290.stderr b/src/test/ui/pattern/issue-82290.stderr deleted file mode 100644 index 0a3cf2c794f47..0000000000000 --- a/src/test/ui/pattern/issue-82290.stderr +++ /dev/null @@ -1,21 +0,0 @@ -warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-82290.rs:3:12 - | -LL | #![feature(let_chains)] - | ^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53667 for more information - -warning: irrefutable `let` pattern - --> $DIR/issue-82290.rs:6:16 - | -LL | if true && let x = 1 { - | ^^^^^^^^^ - | - = note: `#[warn(irrefutable_let_patterns)]` on by default - = note: this pattern will always match, so the `let` is useless - = help: consider removing `let` - -warning: 2 warnings emitted - diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs index 2a2c0be52630b..34d2d84da934f 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs @@ -8,36 +8,36 @@ fn _if_let_guard() { //~^ ERROR `if let` guards are experimental () if (let 0 = 1) => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if (((let 0 = 1))) => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if true && let 0 = 1 => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if let 0 = 1 && true => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if (let 0 = 1) && true => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if true && (let 0 = 1) => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable () if (let 0 = 1) && (let 0 = 1) => {} - //~^ ERROR `let` expressions in this position are experimental - //~| ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} - //~^ ERROR `let` expressions in this position are experimental - //~| ERROR `let` expressions in this position are experimental - //~| ERROR `let` expressions in this position are experimental - //~| ERROR `let` expressions in this position are experimental - //~| ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable + //~| ERROR `let` expressions in this position are unstable () if let Range { start: _, end: _ } = (true..true) && false => {} - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable _ => {} } } @@ -52,9 +52,9 @@ fn _macros() { } } use_expr!((let 0 = 1 && 0 == 0)); - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable use_expr!((let 0 = 1)); - //~^ ERROR `let` expressions in this position are experimental + //~^ ERROR `let` expressions in this position are unstable match () { #[cfg(FALSE)] () if let 0 = 1 => {} diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr index bedcdcb019ba9..0cda6ba9a9927 100644 --- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr +++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr @@ -27,7 +27,7 @@ LL | () if let 0 = 1 => {} = help: add `#![feature(if_let_guard)]` to the crate attributes to enable = help: you can write `if matches!(, )` instead of `if let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:10:16 | LL | () if (let 0 = 1) => {} @@ -35,9 +35,8 @@ LL | () if (let 0 = 1) => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:13:18 | LL | () if (((let 0 = 1))) => {} @@ -45,9 +44,8 @@ LL | () if (((let 0 = 1))) => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:16:23 | LL | () if true && let 0 = 1 => {} @@ -55,9 +53,8 @@ LL | () if true && let 0 = 1 => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:19:15 | LL | () if let 0 = 1 && true => {} @@ -65,9 +62,8 @@ LL | () if let 0 = 1 && true => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:22:16 | LL | () if (let 0 = 1) && true => {} @@ -75,9 +71,8 @@ LL | () if (let 0 = 1) && true => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:25:24 | LL | () if true && (let 0 = 1) => {} @@ -85,9 +80,8 @@ LL | () if true && (let 0 = 1) => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:28:16 | LL | () if (let 0 = 1) && (let 0 = 1) => {} @@ -95,9 +89,8 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:28:31 | LL | () if (let 0 = 1) && (let 0 = 1) => {} @@ -105,9 +98,8 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:15 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} @@ -115,9 +107,8 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:28 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} @@ -125,9 +116,8 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:42 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} @@ -135,9 +125,8 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:55 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} @@ -145,9 +134,8 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:68 | LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {} @@ -155,9 +143,8 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:39:15 | LL | () if let Range { start: _, end: _ } = (true..true) && false => {} @@ -165,9 +152,8 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:54:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); @@ -175,9 +161,8 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:56:16 | LL | use_expr!((let 0 = 1)); @@ -185,7 +170,6 @@ LL | use_expr!((let 0 = 1)); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` error: aborting due to 19 previous errors diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs b/src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs new file mode 100644 index 0000000000000..708bcdd0aefe3 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/ast-lowering-does-not-wrap-let-chains.rs @@ -0,0 +1,15 @@ +// run-pass + +#![feature(let_chains)] + +fn main() { + let first = Some(1); + let second = Some(2); + let mut n = 0; + if let x = first && let y = second && 1 == 1 { + assert_eq!(x, first); + assert_eq!(y, second); + n = 1; + } + assert_eq!(n, 1); +} diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs index 710fdd57ed7b3..69bc189dd3579 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs +++ b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.rs @@ -1,4 +1,4 @@ -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags: -Z unpretty=expanded fn main() { diff --git a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout index 6052ea95d0f85..e737ef26e9b38 100644 --- a/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout +++ b/src/test/ui/rfc-2497-if-let-chains/ast-pretty-check.stdout @@ -4,7 +4,7 @@ use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; -// build-pass (FIXME(62277): could be check-pass?) +// check-pass // compile-flags: -Z unpretty=expanded fn main() { if let 0 = 1 {} } diff --git a/src/test/ui/rfc-2497-if-let-chains/chains-without-let.rs b/src/test/ui/rfc-2497-if-let-chains/chains-without-let.rs new file mode 100644 index 0000000000000..a7e108d72d1ff --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/chains-without-let.rs @@ -0,0 +1,20 @@ +fn and_chain() { + let z; + if true && { z = 3; true} && z == 3 {} + //~^ ERROR use of possibly-uninitialized +} + +fn and_chain_2() { + let z; + true && { z = 3; true} && z == 3; + //~^ ERROR use of possibly-uninitialized +} + +fn or_chain() { + let z; + if false || { z = 3; false} || z == 3 {} + //~^ ERROR use of possibly-uninitialized +} + +fn main() { +} diff --git a/src/test/ui/rfc-2497-if-let-chains/chains-without-let.stderr b/src/test/ui/rfc-2497-if-let-chains/chains-without-let.stderr new file mode 100644 index 0000000000000..3c47040cc8c24 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/chains-without-let.stderr @@ -0,0 +1,21 @@ +error[E0381]: use of possibly-uninitialized variable: `z` + --> $DIR/chains-without-let.rs:3:34 + | +LL | if true && { z = 3; true} && z == 3 {} + | ^ use of possibly-uninitialized `z` + +error[E0381]: use of possibly-uninitialized variable: `z` + --> $DIR/chains-without-let.rs:9:31 + | +LL | true && { z = 3; true} && z == 3; + | ^ use of possibly-uninitialized `z` + +error[E0381]: use of possibly-uninitialized variable: `z` + --> $DIR/chains-without-let.rs:15:36 + | +LL | if false || { z = 3; false} || z == 3 {} + | ^ use of possibly-uninitialized `z` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0381`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index b0b3464c61017..5b2693d07a790 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -18,7 +18,6 @@ // To that end, we check some positions which is not part of the language above. #![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test. -//~^ WARN the feature `let_chains` is incomplete #![allow(irrefutable_let_patterns)] diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 1433a16d7274a..4c830554d435c 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1,5 +1,5 @@ error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:233:9 + --> $DIR/disallowed-positions.rs:232:9 | LL | true && let 1 = 1 | ^^^^^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | { true && let 1 = 1 } | + + error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:30:9 + --> $DIR/disallowed-positions.rs:29:9 | LL | if &let 0 = 0 {} | ^^^^^^^^^ @@ -19,7 +19,7 @@ LL | if &let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:33:9 + --> $DIR/disallowed-positions.rs:32:9 | LL | if !let 0 = 0 {} | ^^^^^^^^^ @@ -28,7 +28,7 @@ LL | if !let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:34:9 + --> $DIR/disallowed-positions.rs:33:9 | LL | if *let 0 = 0 {} | ^^^^^^^^^ @@ -37,7 +37,7 @@ LL | if *let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:36:9 + --> $DIR/disallowed-positions.rs:35:9 | LL | if -let 0 = 0 {} | ^^^^^^^^^ @@ -46,7 +46,7 @@ LL | if -let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:44:9 + --> $DIR/disallowed-positions.rs:43:9 | LL | if (let 0 = 0)? {} | ^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if (let 0 = 0)? {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:48:16 + --> $DIR/disallowed-positions.rs:47:16 | LL | if true || let 0 = 0 {} | ^^^^^^^^^ @@ -64,7 +64,7 @@ LL | if true || let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:49:17 + --> $DIR/disallowed-positions.rs:48:17 | LL | if (true || let 0 = 0) {} | ^^^^^^^^^ @@ -73,7 +73,7 @@ LL | if (true || let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:50:25 + --> $DIR/disallowed-positions.rs:49:25 | LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -82,7 +82,7 @@ LL | if true && (true || let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:51:25 + --> $DIR/disallowed-positions.rs:50:25 | LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -91,7 +91,7 @@ LL | if true || (true && let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:54:12 + --> $DIR/disallowed-positions.rs:53:12 | LL | if x = let 0 = 0 {} | ^^^^^^^^^ @@ -100,7 +100,7 @@ LL | if x = let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:57:15 + --> $DIR/disallowed-positions.rs:56:15 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^ @@ -109,7 +109,7 @@ LL | if true..(let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:59:11 + --> $DIR/disallowed-positions.rs:58:11 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^ @@ -118,7 +118,7 @@ LL | if ..(let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:61:9 + --> $DIR/disallowed-positions.rs:60:9 | LL | if (let 0 = 0).. {} | ^^^^^^^^^ @@ -127,7 +127,7 @@ LL | if (let 0 = 0).. {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:65:8 + --> $DIR/disallowed-positions.rs:64:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:69:8 + --> $DIR/disallowed-positions.rs:68:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:76:8 + --> $DIR/disallowed-positions.rs:75:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let Range { start: F, end } = F..|| true {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:84:8 + --> $DIR/disallowed-positions.rs:83:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -163,7 +163,7 @@ LL | if let Range { start: true, end } = t..&&false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:90:19 + --> $DIR/disallowed-positions.rs:89:19 | LL | if let true = let true = true {} | ^^^^^^^^^^^^^^^ @@ -172,7 +172,7 @@ LL | if let true = let true = true {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:94:12 + --> $DIR/disallowed-positions.rs:93:12 | LL | while &let 0 = 0 {} | ^^^^^^^^^ @@ -181,7 +181,7 @@ LL | while &let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:97:12 + --> $DIR/disallowed-positions.rs:96:12 | LL | while !let 0 = 0 {} | ^^^^^^^^^ @@ -190,7 +190,7 @@ LL | while !let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:98:12 + --> $DIR/disallowed-positions.rs:97:12 | LL | while *let 0 = 0 {} | ^^^^^^^^^ @@ -199,7 +199,7 @@ LL | while *let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:100:12 + --> $DIR/disallowed-positions.rs:99:12 | LL | while -let 0 = 0 {} | ^^^^^^^^^ @@ -208,7 +208,7 @@ LL | while -let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:108:12 + --> $DIR/disallowed-positions.rs:107:12 | LL | while (let 0 = 0)? {} | ^^^^^^^^^ @@ -217,7 +217,7 @@ LL | while (let 0 = 0)? {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:112:19 + --> $DIR/disallowed-positions.rs:111:19 | LL | while true || let 0 = 0 {} | ^^^^^^^^^ @@ -226,7 +226,7 @@ LL | while true || let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:113:20 + --> $DIR/disallowed-positions.rs:112:20 | LL | while (true || let 0 = 0) {} | ^^^^^^^^^ @@ -235,7 +235,7 @@ LL | while (true || let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:114:28 + --> $DIR/disallowed-positions.rs:113:28 | LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ @@ -244,7 +244,7 @@ LL | while true && (true || let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:115:28 + --> $DIR/disallowed-positions.rs:114:28 | LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ @@ -253,7 +253,7 @@ LL | while true || (true && let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:118:15 + --> $DIR/disallowed-positions.rs:117:15 | LL | while x = let 0 = 0 {} | ^^^^^^^^^ @@ -262,7 +262,7 @@ LL | while x = let 0 = 0 {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:121:18 + --> $DIR/disallowed-positions.rs:120:18 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^ @@ -271,7 +271,7 @@ LL | while true..(let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:123:14 + --> $DIR/disallowed-positions.rs:122:14 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^ @@ -280,7 +280,7 @@ LL | while ..(let 0 = 0) {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:125:12 + --> $DIR/disallowed-positions.rs:124:12 | LL | while (let 0 = 0).. {} | ^^^^^^^^^ @@ -289,7 +289,7 @@ LL | while (let 0 = 0).. {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:129:11 + --> $DIR/disallowed-positions.rs:128:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -298,7 +298,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:133:11 + --> $DIR/disallowed-positions.rs:132:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -307,7 +307,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:140:11 + --> $DIR/disallowed-positions.rs:139:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -316,7 +316,7 @@ LL | while let Range { start: F, end } = F..|| true {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:148:11 + --> $DIR/disallowed-positions.rs:147:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -325,7 +325,7 @@ LL | while let Range { start: true, end } = t..&&false {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:154:22 + --> $DIR/disallowed-positions.rs:153:22 | LL | while let true = let true = true {} | ^^^^^^^^^^^^^^^ @@ -334,7 +334,7 @@ LL | while let true = let true = true {} = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:168:6 + --> $DIR/disallowed-positions.rs:167:6 | LL | &let 0 = 0; | ^^^^^^^^^ @@ -343,7 +343,7 @@ LL | &let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:170:6 + --> $DIR/disallowed-positions.rs:169:6 | LL | !let 0 = 0; | ^^^^^^^^^ @@ -352,7 +352,7 @@ LL | !let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:171:6 + --> $DIR/disallowed-positions.rs:170:6 | LL | *let 0 = 0; | ^^^^^^^^^ @@ -361,7 +361,7 @@ LL | *let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:173:6 + --> $DIR/disallowed-positions.rs:172:6 | LL | -let 0 = 0; | ^^^^^^^^^ @@ -370,7 +370,7 @@ LL | -let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:181:6 + --> $DIR/disallowed-positions.rs:180:6 | LL | (let 0 = 0)?; | ^^^^^^^^^ @@ -379,7 +379,7 @@ LL | (let 0 = 0)?; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:185:13 + --> $DIR/disallowed-positions.rs:184:13 | LL | true || let 0 = 0; | ^^^^^^^^^ @@ -388,7 +388,7 @@ LL | true || let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:186:14 + --> $DIR/disallowed-positions.rs:185:14 | LL | (true || let 0 = 0); | ^^^^^^^^^ @@ -397,7 +397,7 @@ LL | (true || let 0 = 0); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:187:22 + --> $DIR/disallowed-positions.rs:186:22 | LL | true && (true || let 0 = 0); | ^^^^^^^^^ @@ -406,7 +406,7 @@ LL | true && (true || let 0 = 0); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:190:9 + --> $DIR/disallowed-positions.rs:189:9 | LL | x = let 0 = 0; | ^^^^^^^^^ @@ -415,7 +415,7 @@ LL | x = let 0 = 0; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:192:12 + --> $DIR/disallowed-positions.rs:191:12 | LL | true..(let 0 = 0); | ^^^^^^^^^ @@ -424,7 +424,7 @@ LL | true..(let 0 = 0); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:193:8 + --> $DIR/disallowed-positions.rs:192:8 | LL | ..(let 0 = 0); | ^^^^^^^^^ @@ -433,7 +433,7 @@ LL | ..(let 0 = 0); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:194:6 + --> $DIR/disallowed-positions.rs:193:6 | LL | (let 0 = 0)..; | ^^^^^^^^^ @@ -442,7 +442,7 @@ LL | (let 0 = 0)..; = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:196:6 + --> $DIR/disallowed-positions.rs:195:6 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -451,7 +451,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:200:6 + --> $DIR/disallowed-positions.rs:199:6 | LL | (let true = let true = true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -460,7 +460,7 @@ LL | (let true = let true = true); = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:204:6 + --> $DIR/disallowed-positions.rs:203:6 | LL | &let 0 = 0 | ^^^^^^^^^ @@ -469,7 +469,7 @@ LL | &let 0 = 0 = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:215:17 + --> $DIR/disallowed-positions.rs:214:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -478,7 +478,7 @@ LL | true && let 1 = 1 = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:219:17 + --> $DIR/disallowed-positions.rs:218:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -487,7 +487,7 @@ LL | true && let 1 = 1 = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:223:17 + --> $DIR/disallowed-positions.rs:222:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -496,7 +496,7 @@ LL | true && let 1 = 1 = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here - --> $DIR/disallowed-positions.rs:233:17 + --> $DIR/disallowed-positions.rs:232:17 | LL | true && let 1 = 1 | ^^^^^^^^^ @@ -504,17 +504,8 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if`- and `while`-expressions = note: as well as when nested within `&&` and parentheses in those conditions -warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/disallowed-positions.rs:20:12 - | -LL | #![feature(let_chains)] // Avoid inflating `.stderr` with overzealous gates in this test. - | ^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - = note: see issue #53667 for more information - error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:30:8 + --> $DIR/disallowed-positions.rs:29:8 | LL | if &let 0 = 0 {} | ^^^^^^^^^^ expected `bool`, found `&bool` @@ -526,19 +517,19 @@ LL + if let 0 = 0 {} | error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:34:8 + --> $DIR/disallowed-positions.rs:33:8 | LL | if *let 0 = 0 {} | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:36:8 + --> $DIR/disallowed-positions.rs:35:8 | LL | if -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:44:8 + --> $DIR/disallowed-positions.rs:43:8 | LL | if (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -546,7 +537,7 @@ LL | if (let 0 = 0)? {} = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:44:19 + --> $DIR/disallowed-positions.rs:43:19 | LL | / fn nested_within_if_expr() { LL | | if &let 0 = 0 {} @@ -563,7 +554,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:54:8 + --> $DIR/disallowed-positions.rs:53:8 | LL | if x = let 0 = 0 {} | ^^^^^^^^^^^^^ expected `bool`, found `()` @@ -574,7 +565,7 @@ LL | if x == let 0 = 0 {} | ~~ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:57:8 + --> $DIR/disallowed-positions.rs:56:8 | LL | if true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -583,7 +574,7 @@ LL | if true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:59:8 + --> $DIR/disallowed-positions.rs:58:8 | LL | if ..(let 0 = 0) {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` @@ -592,7 +583,7 @@ LL | if ..(let 0 = 0) {} found struct `RangeTo` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:61:8 + --> $DIR/disallowed-positions.rs:60:8 | LL | if (let 0 = 0).. {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` @@ -601,7 +592,7 @@ LL | if (let 0 = 0).. {} found struct `RangeFrom` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:65:12 + --> $DIR/disallowed-positions.rs:64:12 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -612,7 +603,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:65:8 + --> $DIR/disallowed-positions.rs:64:8 | LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -621,7 +612,7 @@ LL | if let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:69:12 + --> $DIR/disallowed-positions.rs:68:12 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -632,7 +623,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:69:8 + --> $DIR/disallowed-positions.rs:68:8 | LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -641,7 +632,7 @@ LL | if let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:76:12 + --> $DIR/disallowed-positions.rs:75:12 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -652,16 +643,16 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:76:41 + --> $DIR/disallowed-positions.rs:75:41 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^ expected `bool`, found closure | = note: expected type `bool` - found closure `[closure@$DIR/disallowed-positions.rs:76:41: 76:48]` + found closure `[closure@$DIR/disallowed-positions.rs:75:41: 75:48]` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:76:8 + --> $DIR/disallowed-positions.rs:75:8 | LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -670,7 +661,7 @@ LL | if let Range { start: F, end } = F..|| true {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:84:12 + --> $DIR/disallowed-positions.rs:83:12 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -681,13 +672,13 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:84:44 + --> $DIR/disallowed-positions.rs:83:44 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:84:8 + --> $DIR/disallowed-positions.rs:83:8 | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -696,7 +687,7 @@ LL | if let Range { start: true, end } = t..&&false {} found struct `std::ops::Range` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:40:20 + --> $DIR/disallowed-positions.rs:39:20 | LL | if let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -704,7 +695,7 @@ LL | if let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:94:11 + --> $DIR/disallowed-positions.rs:93:11 | LL | while &let 0 = 0 {} | ^^^^^^^^^^ expected `bool`, found `&bool` @@ -716,19 +707,19 @@ LL + while let 0 = 0 {} | error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:98:11 + --> $DIR/disallowed-positions.rs:97:11 | LL | while *let 0 = 0 {} | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:100:11 + --> $DIR/disallowed-positions.rs:99:11 | LL | while -let 0 = 0 {} | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:108:11 + --> $DIR/disallowed-positions.rs:107:11 | LL | while (let 0 = 0)? {} | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -736,7 +727,7 @@ LL | while (let 0 = 0)? {} = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:108:22 + --> $DIR/disallowed-positions.rs:107:22 | LL | / fn nested_within_while_expr() { LL | | while &let 0 = 0 {} @@ -753,7 +744,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:118:11 + --> $DIR/disallowed-positions.rs:117:11 | LL | while x = let 0 = 0 {} | ^^^^^^^^^^^^^ expected `bool`, found `()` @@ -764,7 +755,7 @@ LL | while x == let 0 = 0 {} | ~~ error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:121:11 + --> $DIR/disallowed-positions.rs:120:11 | LL | while true..(let 0 = 0) {} | ^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -773,7 +764,7 @@ LL | while true..(let 0 = 0) {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:123:11 + --> $DIR/disallowed-positions.rs:122:11 | LL | while ..(let 0 = 0) {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeTo` @@ -782,7 +773,7 @@ LL | while ..(let 0 = 0) {} found struct `RangeTo` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:125:11 + --> $DIR/disallowed-positions.rs:124:11 | LL | while (let 0 = 0).. {} | ^^^^^^^^^^^^^ expected `bool`, found struct `RangeFrom` @@ -791,7 +782,7 @@ LL | while (let 0 = 0).. {} found struct `RangeFrom` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:129:15 + --> $DIR/disallowed-positions.rs:128:15 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -802,7 +793,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:129:11 + --> $DIR/disallowed-positions.rs:128:11 | LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -811,7 +802,7 @@ LL | while let Range { start: _, end: _ } = true..true && false {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:133:15 + --> $DIR/disallowed-positions.rs:132:15 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -822,7 +813,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:133:11 + --> $DIR/disallowed-positions.rs:132:11 | LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -831,7 +822,7 @@ LL | while let Range { start: _, end: _ } = true..true || false {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:140:15 + --> $DIR/disallowed-positions.rs:139:15 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` @@ -842,16 +833,16 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:140:44 + --> $DIR/disallowed-positions.rs:139:44 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^ expected `bool`, found closure | = note: expected type `bool` - found closure `[closure@$DIR/disallowed-positions.rs:140:44: 140:51]` + found closure `[closure@$DIR/disallowed-positions.rs:139:44: 139:51]` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:140:11 + --> $DIR/disallowed-positions.rs:139:11 | LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -860,7 +851,7 @@ LL | while let Range { start: F, end } = F..|| true {} found struct `std::ops::Range` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:148:15 + --> $DIR/disallowed-positions.rs:147:15 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` @@ -871,13 +862,13 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:148:47 + --> $DIR/disallowed-positions.rs:147:47 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:148:11 + --> $DIR/disallowed-positions.rs:147:11 | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found struct `std::ops::Range` @@ -886,7 +877,7 @@ LL | while let Range { start: true, end } = t..&&false {} found struct `std::ops::Range` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:104:23 + --> $DIR/disallowed-positions.rs:103:23 | LL | while let 0 = 0? {} | ^^ the `?` operator cannot be applied to type `{integer}` @@ -894,19 +885,19 @@ LL | while let 0 = 0? {} = help: the trait `Try` is not implemented for `{integer}` error[E0614]: type `bool` cannot be dereferenced - --> $DIR/disallowed-positions.rs:171:5 + --> $DIR/disallowed-positions.rs:170:5 | LL | *let 0 = 0; | ^^^^^^^^^^ error[E0600]: cannot apply unary operator `-` to type `bool` - --> $DIR/disallowed-positions.rs:173:5 + --> $DIR/disallowed-positions.rs:172:5 | LL | -let 0 = 0; | ^^^^^^^^^^ cannot apply unary operator `-` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:181:5 + --> $DIR/disallowed-positions.rs:180:5 | LL | (let 0 = 0)?; | ^^^^^^^^^^^^ the `?` operator cannot be applied to type `bool` @@ -914,7 +905,7 @@ LL | (let 0 = 0)?; = help: the trait `Try` is not implemented for `bool` error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> $DIR/disallowed-positions.rs:181:16 + --> $DIR/disallowed-positions.rs:180:16 | LL | / fn outside_if_and_while_expr() { LL | | &let 0 = 0; @@ -931,7 +922,7 @@ LL | | } = help: the trait `FromResidual<_>` is not implemented for `()` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:196:10 + --> $DIR/disallowed-positions.rs:195:10 | LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` @@ -942,7 +933,7 @@ LL | (let Range { start: _, end: _ } = true..true || false); found struct `std::ops::Range<_>` error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:204:5 + --> $DIR/disallowed-positions.rs:203:5 | LL | fn outside_if_and_while_expr() { | - help: try adding a return type: `-> &bool` @@ -951,14 +942,14 @@ LL | &let 0 = 0 | ^^^^^^^^^^ expected `()`, found `&bool` error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:177:17 + --> $DIR/disallowed-positions.rs:176:17 | LL | let 0 = 0?; | ^^ the `?` operator cannot be applied to type `{integer}` | = help: the trait `Try` is not implemented for `{integer}` -error: aborting due to 103 previous errors; 1 warning emitted +error: aborting due to 103 previous errors Some errors have detailed explanations: E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs index 2b4259e9dc150..53fec8316e7e7 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs @@ -12,79 +12,79 @@ fn _if() { if let 0 = 1 {} // Stable! if (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if (((let 0 = 1))) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if true && let 0 = 1 {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if let 0 = 1 && true {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if (let 0 = 1) && true {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if true && (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] if (let 0 = 1) && (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] if let Range { start: _, end: _ } = (true..true) && false {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] } fn _while() { while let 0 = 1 {} // Stable! while (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while (((let 0 = 1))) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while true && let 0 = 1 {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while let 0 = 1 && true {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while (let 0 = 1) && true {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while true && (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] while (let 0 = 1) && (let 0 = 1) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - //~^ ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] - //~| ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] + //~| ERROR `let` expressions in this position are unstable [E0658] while let Range { start: _, end: _ } = (true..true) && false {} - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] } fn _macros() { macro_rules! noop_expr { ($e:expr) => {}; } noop_expr!((let 0 = 1)); - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] macro_rules! use_expr { ($e:expr) => { @@ -93,11 +93,11 @@ fn _macros() { } } use_expr!((let 0 = 1 && 0 == 0)); - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] use_expr!((let 0 = 1)); - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] #[cfg(FALSE)] (let 0 = 1); - //~^ ERROR `let` expressions in this position are experimental [E0658] + //~^ ERROR `let` expressions in this position are unstable [E0658] use_expr!(let 0 = 1); //~^ ERROR no rules expected the token `let` // ^--- FIXME(53667): Consider whether `Let` can be added to `ident_can_begin_expr`. diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr index 180eee0cadfe6..458826498fe2d 100644 --- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr @@ -7,7 +7,7 @@ LL | macro_rules! use_expr { LL | use_expr!(let 0 = 1); | ^^^ no rules expected this token in macro call -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:14:9 | LL | if (let 0 = 1) {} @@ -15,9 +15,8 @@ LL | if (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:17:11 | LL | if (((let 0 = 1))) {} @@ -25,9 +24,8 @@ LL | if (((let 0 = 1))) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:20:16 | LL | if true && let 0 = 1 {} @@ -35,9 +33,8 @@ LL | if true && let 0 = 1 {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:23:8 | LL | if let 0 = 1 && true {} @@ -45,9 +42,8 @@ LL | if let 0 = 1 && true {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:26:9 | LL | if (let 0 = 1) && true {} @@ -55,9 +51,8 @@ LL | if (let 0 = 1) && true {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:29:17 | LL | if true && (let 0 = 1) {} @@ -65,9 +60,8 @@ LL | if true && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:9 | LL | if (let 0 = 1) && (let 0 = 1) {} @@ -75,9 +69,8 @@ LL | if (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:32:24 | LL | if (let 0 = 1) && (let 0 = 1) {} @@ -85,9 +78,8 @@ LL | if (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:36:8 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -95,9 +87,8 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:36:21 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -105,9 +96,8 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:36:35 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -115,9 +105,8 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:36:48 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -125,9 +114,8 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:36:61 | LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -135,9 +123,8 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:43:8 | LL | if let Range { start: _, end: _ } = (true..true) && false {} @@ -145,9 +132,8 @@ LL | if let Range { start: _, end: _ } = (true..true) && false {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:50:12 | LL | while (let 0 = 1) {} @@ -155,9 +141,8 @@ LL | while (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:53:14 | LL | while (((let 0 = 1))) {} @@ -165,9 +150,8 @@ LL | while (((let 0 = 1))) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:56:19 | LL | while true && let 0 = 1 {} @@ -175,9 +159,8 @@ LL | while true && let 0 = 1 {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:59:11 | LL | while let 0 = 1 && true {} @@ -185,9 +168,8 @@ LL | while let 0 = 1 && true {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:62:12 | LL | while (let 0 = 1) && true {} @@ -195,9 +177,8 @@ LL | while (let 0 = 1) && true {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:65:20 | LL | while true && (let 0 = 1) {} @@ -205,9 +186,8 @@ LL | while true && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:68:12 | LL | while (let 0 = 1) && (let 0 = 1) {} @@ -215,9 +195,8 @@ LL | while (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:68:27 | LL | while (let 0 = 1) && (let 0 = 1) {} @@ -225,9 +204,8 @@ LL | while (let 0 = 1) && (let 0 = 1) {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:72:11 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -235,9 +213,8 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:72:24 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -245,9 +222,8 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:72:38 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -255,9 +231,8 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:72:51 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -265,9 +240,8 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:72:64 | LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} @@ -275,9 +249,8 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) { | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:79:11 | LL | while let Range { start: _, end: _ } = (true..true) && false {} @@ -285,9 +258,8 @@ LL | while let Range { start: _, end: _ } = (true..true) && false {} | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:99:20 | LL | #[cfg(FALSE)] (let 0 = 1); @@ -295,9 +267,8 @@ LL | #[cfg(FALSE)] (let 0 = 1); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:86:17 | LL | noop_expr!((let 0 = 1)); @@ -305,9 +276,8 @@ LL | noop_expr!((let 0 = 1)); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:95:16 | LL | use_expr!((let 0 = 1 && 0 == 0)); @@ -315,9 +285,8 @@ LL | use_expr!((let 0 = 1 && 0 == 0)); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` -error[E0658]: `let` expressions in this position are experimental +error[E0658]: `let` expressions in this position are unstable --> $DIR/feature-gate.rs:97:16 | LL | use_expr!((let 0 = 1)); @@ -325,7 +294,6 @@ LL | use_expr!((let 0 = 1)); | = note: see issue #53667 for more information = help: add `#![feature(let_chains)]` to the crate attributes to enable - = help: you can write `matches!(, )` instead of `let = ` error: aborting due to 33 previous errors diff --git a/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs new file mode 100644 index 0000000000000..5915cb9df269c --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/irrefutable-lets.rs @@ -0,0 +1,27 @@ +// check-pass + +#![feature(let_chains)] + +use std::ops::Range; + +fn main() { + let opt = Some(None..Some(1)); + + if let first = &opt && let Some(ref second) = first && let None = second.start { + } + if let Some(ref first) = opt && let second = first && let _third = second { + } + if let Some(ref first) = opt + && let Range { start: local_start, end: _ } = first + && let None = local_start { + } + + while let first = &opt && let Some(ref second) = first && let None = second.start { + } + while let Some(ref first) = opt && let second = first && let _third = second { + } + while let Some(ref first) = opt + && let Range { start: local_start, end: _ } = first + && let None = local_start { + } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/issue-88498.rs b/src/test/ui/rfc-2497-if-let-chains/issue-88498.rs new file mode 100644 index 0000000000000..3eb8a9ad06020 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/issue-88498.rs @@ -0,0 +1,16 @@ +// check-pass + +pub enum UnOp { + Not(Vec<()>), +} + +pub fn foo() { + if let Some(x) = None { + match x { + UnOp::Not(_) => {} + } + } +} + +fn main() { +} diff --git a/src/test/ui/rfc-2497-if-let-chains/issue-90722.rs b/src/test/ui/rfc-2497-if-let-chains/issue-90722.rs new file mode 100644 index 0000000000000..6b7d883565085 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/issue-90722.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(let_chains)] + +fn main() { + let x = Some(vec!["test"]); + + if let Some(v) = x && v.is_empty() { + println!("x == Some([])"); + } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/issue-92145.rs b/src/test/ui/rfc-2497-if-let-chains/issue-92145.rs new file mode 100644 index 0000000000000..7c7e31f4db400 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/issue-92145.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(let_chains)] + +fn main() { + let opt = Some("foo bar"); + + if true && let Some(x) = opt { + println!("{}", x); + } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/no-double-assigments.rs b/src/test/ui/rfc-2497-if-let-chains/no-double-assigments.rs new file mode 100644 index 0000000000000..6b91c455e0e97 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/no-double-assigments.rs @@ -0,0 +1,9 @@ +// check-pass + +fn main() { + loop { + // [1][0] should leave top scope + if true && [1][0] == 1 && true { + } + } +} diff --git a/src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs b/src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs new file mode 100644 index 0000000000000..0856a10520636 --- /dev/null +++ b/src/test/ui/rfc-2497-if-let-chains/then-else-blocks.rs @@ -0,0 +1,35 @@ +// run-pass + +#![feature(let_chains)] + +fn check_if_let(opt: Option>>, value: i32) -> bool { + if let Some(first) = opt + && let Some(second) = first + && let Some(third) = second + && third == value + { + true + } + else { + false + } +} + +fn check_while_let(opt: Option>>, value: i32) -> bool { + while let Some(first) = opt + && let Some(second) = first + && let Some(third) = second + && third == value + { + return true; + } + false +} + +fn main() { + assert_eq!(check_if_let(Some(Some(Some(1))), 1), true); + assert_eq!(check_if_let(Some(Some(Some(1))), 9), false); + + assert_eq!(check_while_let(Some(Some(Some(1))), 1), true); + assert_eq!(check_while_let(Some(Some(Some(1))), 9), false); +} From 5fee3e7a9c65445e3585eb4a162c774c788a02d5 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 19 Jan 2022 00:09:59 +0100 Subject: [PATCH 16/18] Fix is_some_with tests. --- library/core/src/option.rs | 8 +++++--- library/core/src/result.rs | 13 ++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/library/core/src/option.rs b/library/core/src/option.rs index e8850176f7cd3..611f4ab38ab33 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -556,14 +556,16 @@ impl Option { /// # Examples /// /// ``` + /// #![feature(is_some_with)] + /// /// let x: Option = Some(2); - /// assert_eq!(x.is_some_with(|x| x > 1), true); + /// assert_eq!(x.is_some_with(|&x| x > 1), true); /// /// let x: Option = Some(0); - /// assert_eq!(x.is_some_with(|x| x > 1), false); + /// assert_eq!(x.is_some_with(|&x| x > 1), false); /// /// let x: Option = None; - /// assert_eq!(x.is_some_with(|x| x > 1), false); + /// assert_eq!(x.is_some_with(|&x| x > 1), false); /// ``` #[must_use] #[inline] diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 9991112e7f553..fbd6d419236ae 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -547,14 +547,16 @@ impl Result { /// # Examples /// /// ``` + /// #![feature(is_some_with)] + /// /// let x: Result = Ok(2); - /// assert_eq!(x.is_ok_with(|x| x > 1), true); + /// assert_eq!(x.is_ok_with(|&x| x > 1), true); /// /// let x: Result = Ok(0); - /// assert_eq!(x.is_ok_with(|x| x > 1), false); + /// assert_eq!(x.is_ok_with(|&x| x > 1), false); /// /// let x: Result = Err("hey"); - /// assert_eq!(x.is_ok_with(|x| x > 1), false); + /// assert_eq!(x.is_ok_with(|&x| x > 1), false); /// ``` #[must_use] #[inline] @@ -589,16 +591,17 @@ impl Result { /// # Examples /// /// ``` + /// #![feature(is_some_with)] /// use std::io::{Error, ErrorKind}; /// /// let x: Result = Err(Error::new(ErrorKind::NotFound, "!")); /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), true); /// /// let x: Result = Err(Error::new(ErrorKind::PermissionDenied, "!")); - /// assert_eq!(x.is_ok_with(|x| x.kind() == ErrorKind::NotFound), false); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), false); /// /// let x: Result = Ok(123); - /// assert_eq!(x.is_ok_with(|x| x.kind() == ErrorKind::NotFound), false); + /// assert_eq!(x.is_err_with(|x| x.kind() == ErrorKind::NotFound), false); /// ``` #[must_use] #[inline] From fa9a8430eac7b57d683d58a10f36819ad9bd5fdb Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Tue, 11 Jan 2022 15:31:40 -0800 Subject: [PATCH 17/18] Remove horizontal lines at top of page They are not needed to separate the search bar and the title, which are visually distinct on their own. --- src/librustdoc/html/static/css/rustdoc.css | 3 --- src/librustdoc/html/static/css/themes/ayu.css | 3 --- src/librustdoc/html/static/css/themes/dark.css | 3 --- src/librustdoc/html/static/css/themes/light.css | 3 --- src/test/rustdoc-gui/headings.goml | 4 ---- src/test/rustdoc-gui/toggle-docs-mobile.goml | 12 ++++++------ 6 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index dbc068ce6b13b..44a9a571fa1f8 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -148,8 +148,6 @@ h1.fqn { } .main-heading { display: flex; - border-bottom: 1px dashed #DDDDDD; - padding-bottom: 6px; margin-bottom: 15px; /* workaround to keep flex from breaking below 700 px width due to the float: right on the nav @@ -785,7 +783,6 @@ nav.sub { nav.sub { flex-grow: 1; - padding-bottom: 10px; margin-bottom: 25px; } .source nav.sub { diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css index 82a2be67ceb0c..69097b81b9f1c 100644 --- a/src/librustdoc/html/static/css/themes/ayu.css +++ b/src/librustdoc/html/static/css/themes/ayu.css @@ -194,9 +194,6 @@ pre, .rustdoc.source .example-wrap { pre.rust .comment { color: #788797; } pre.rust .doccomment { color: #a1ac88; } -nav:not(.sidebar) { - border-bottom-color: #424c57; -} nav.main .current { border-top-color: #5c6773; border-bottom-color: #5c6773; diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index 761bf50dd36b1..39165b2fc058f 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -151,9 +151,6 @@ a.result-keyword:focus { background-color: #884719; } pre.rust .comment { color: #8d8d8b; } pre.rust .doccomment { color: #8ca375; } -nav:not(.sidebar) { - border-bottom-color: #4e4e4e; -} nav.main .current { border-top-color: #eee; border-bottom-color: #eee; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 7cca7d4004b5e..448c9ac603c82 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -148,9 +148,6 @@ a.result-keyword:focus { background-color: #afc6e4; } .content .fnname { color: #AD7C37; } .content span.keyword, .content a.keyword, .block a.current.keyword { color: #3873AD; } -nav:not(.sidebar) { - border-bottom-color: #e0e0e0; -} nav.main .current { border-top-color: #000; border-bottom-color: #000; diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml index 34ff2de3a9b2b..48e0156f1b81b 100644 --- a/src/test/rustdoc-gui/headings.goml +++ b/src/test/rustdoc-gui/headings.goml @@ -15,7 +15,6 @@ goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -55,7 +54,6 @@ assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "15.2p goto: file://|DOC_PATH|/test_docs/enum.HeavilyDocumentedEnum.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -115,7 +113,6 @@ assert-css: (".sidebar .others h3", {"border-bottom-width": "0px"}, ALL) goto: file://|DOC_PATH|/test_docs/union.HeavilyDocumentedUnion.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) @@ -148,7 +145,6 @@ assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": " goto: file://|DOC_PATH|/test_docs/macro.heavily_documented_macro.html assert-css: ("h1.fqn", {"font-size": "24px"}) -assert-css: (".main-heading", {"border-bottom-width": "1px"}) assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"}) assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"}) diff --git a/src/test/rustdoc-gui/toggle-docs-mobile.goml b/src/test/rustdoc-gui/toggle-docs-mobile.goml index 67b9164cfec71..4c83fd6c0e31c 100644 --- a/src/test/rustdoc-gui/toggle-docs-mobile.goml +++ b/src/test/rustdoc-gui/toggle-docs-mobile.goml @@ -1,12 +1,12 @@ goto: file://|DOC_PATH|/test_docs/struct.Foo.html size: (433, 600) assert-attribute: (".top-doc", {"open": ""}) -click: (4, 260) // This is the position of the top doc comment toggle +click: (4, 250) // This is the position of the top doc comment toggle assert-attribute-false: (".top-doc", {"open": ""}) -click: (4, 260) +click: (4, 250) assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. -click: (3, 260) +click: (3, 250) assert-attribute: (".top-doc", {"open": ""}) // Assert the position of the toggle on the top doc block. @@ -22,10 +22,10 @@ assert-position: ( // Now we do the same but with a little bigger width size: (600, 600) assert-attribute: (".top-doc", {"open": ""}) -click: (4, 260) // New Y position since all search elements are back on one line. +click: (4, 250) // New Y position since all search elements are back on one line. assert-attribute-false: (".top-doc", {"open": ""}) -click: (4, 260) +click: (4, 250) assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. -click: (3, 260) +click: (3, 250) assert-attribute: (".top-doc", {"open": ""}) From 84e0d9db6cc31d54245bc8618cfcf60f40e5d4ad Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Tue, 18 Jan 2022 19:23:45 -0800 Subject: [PATCH 18/18] Update books --- src/doc/book | 2 +- src/doc/nomicon | 2 +- src/doc/reference | 2 +- src/doc/rustc-dev-guide | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/book b/src/doc/book index d3740fb7aad0e..f17df27fc1469 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit d3740fb7aad0ea4a80ae20f64dee3a8cfc0c5c3c +Subproject commit f17df27fc14696912c48b8b7a7a8fa49e648088d diff --git a/src/doc/nomicon b/src/doc/nomicon index c05c452b36358..66d097d3d80e8 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit c05c452b36358821bf4122f9c418674edd1d713d +Subproject commit 66d097d3d80e8f88c288c6879c7c2b909ecf8ad4 diff --git a/src/doc/reference b/src/doc/reference index f8ba2f12df60e..4dee6eb63d728 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit f8ba2f12df60ee19b96de24ae5b73af3de8a446b +Subproject commit 4dee6eb63d728ffb9e7a2ed443e9ada9275c69d2 diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide index 875464457c410..78dd6a4684cf8 160000 --- a/src/doc/rustc-dev-guide +++ b/src/doc/rustc-dev-guide @@ -1 +1 @@ -Subproject commit 875464457c4104686faf667f47848aa7b0f0a744 +Subproject commit 78dd6a4684cf8d6b72275fab6d0429ea40b66338