diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 6a863ff369a87..2afd9e0c07237 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -81,12 +81,10 @@ pub trait Any: 'static { /// # Examples /// /// ``` - /// #![feature(get_type_id)] - /// /// use std::any::{Any, TypeId}; /// /// fn is_string(s: &dyn Any) -> bool { - /// TypeId::of::() == s.get_type_id() + /// TypeId::of::() == s.type_id() /// } /// /// fn main() { @@ -94,15 +92,13 @@ pub trait Any: 'static { /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// } /// ``` - #[unstable(feature = "get_type_id", - reason = "this method will likely be replaced by an associated static", - issue = "27745")] - fn get_type_id(&self) -> TypeId; + #[stable(feature = "get_type_id", since = "1.34.0")] + fn type_id(&self) -> TypeId; } #[stable(feature = "rust1", since = "1.0.0")] impl Any for T { - fn get_type_id(&self) -> TypeId { TypeId::of::() } + fn type_id(&self) -> TypeId { TypeId::of::() } } /////////////////////////////////////////////////////////////////////////////// @@ -161,10 +157,10 @@ impl dyn Any { let t = TypeId::of::(); // Get TypeId of the type in the trait object - let boxed = self.get_type_id(); + let concrete = self.type_id(); // Compare both TypeIds on equality - t == boxed + t == concrete } /// Returns some reference to the boxed value if it is of type `T`, or diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 9f9515e6d9b7c..848e906e37ac8 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1582,6 +1582,153 @@ impl [T] { sort::quicksort(self, |a, b| f(a).lt(&f(b))); } + /// Reorder the slice such that the element at `index` is at its final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index`. Additionally, this reordering is + /// unstable (i.e. any number of equal elements may end up at position `index`), in-place + /// (i.e. does not allocate), and `O(n)` worst-case. This function is also/ known as "kth + /// element" in other libraries. It returns a triplet of the following values: all elements less + /// than the one at the given index, the value at the given index, and all elements greater than + /// the one at the given index. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Find the median + /// v.partition_at_index(2); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [-3, -5, 1, 2, 4] || + /// v == [-5, -3, 1, 2, 4] || + /// v == [-3, -5, 1, 4, 2] || + /// v == [-5, -3, 1, 4, 2]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index(&mut self, index: usize) -> (&mut [T], &mut T, &mut [T]) + where T: Ord + { + let mut f = |a: &T, b: &T| a.lt(b); + sort::partition_at_index(self, index, &mut f) + } + + /// Reorder the slice with a comparator function such that the element at `index` is at its + /// final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the comparator function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and `O(n)` worst-case. This function + /// is also known as "kth element" in other libraries. It returns a triplet of the following + /// values: all elements less than the one at the given index, the value at the given index, + /// and all elements greater than the one at the given index, using the provided comparator + /// function. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Find the median as if the slice were sorted in descending order. + /// v.partition_at_index_by(2, |a, b| b.cmp(a)); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [2, 4, 1, -5, -3] || + /// v == [2, 4, 1, -3, -5] || + /// v == [4, 2, 1, -5, -3] || + /// v == [4, 2, 1, -3, -5]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index_by(&mut self, index: usize, mut compare: F) + -> (&mut [T], &mut T, &mut [T]) + where F: FnMut(&T, &T) -> Ordering + { + let mut f = |a: &T, b: &T| compare(a, b) == Less; + sort::partition_at_index(self, index, &mut f) + } + + /// Reorder the slice with a key extraction function such that the element at `index` is at its + /// final sorted position. + /// + /// This reordering has the additional property that any value at position `i < index` will be + /// less than or equal to any value at a position `j > index` using the key extraction function. + /// Additionally, this reordering is unstable (i.e. any number of equal elements may end up at + /// position `index`), in-place (i.e. does not allocate), and `O(n)` worst-case. This function + /// is also known as "kth element" in other libraries. It returns a triplet of the following + /// values: all elements less than the one at the given index, the value at the given index, and + /// all elements greater than the one at the given index, using the provided key extraction + /// function. + /// + /// # Current implementation + /// + /// The current algorithm is based on the quickselect portion of the same quicksort algorithm + /// used for [`sort_unstable`]. + /// + /// [`sort_unstable`]: #method.sort_unstable + /// + /// # Panics + /// + /// Panics when `index >= len()`, meaning it always panics on empty slices. + /// + /// # Examples + /// + /// ``` + /// #![feature(slice_partition_at_index)] + /// + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// // Return the median as if the array were sorted according to absolute value. + /// v.partition_at_index_by_key(2, |a| a.abs()); + /// + /// // We are only guaranteed the slice will be one of the following, based on the way we sort + /// // about the specified index. + /// assert!(v == [1, 2, -3, 4, -5] || + /// v == [1, 2, -3, -5, 4] || + /// v == [2, 1, -3, 4, -5] || + /// v == [2, 1, -3, -5, 4]); + /// ``` + #[unstable(feature = "slice_partition_at_index", issue = "55300")] + #[inline] + pub fn partition_at_index_by_key(&mut self, index: usize, mut f: F) + -> (&mut [T], &mut T, &mut [T]) + where F: FnMut(&T) -> K, K: Ord + { + let mut g = |a: &T, b: &T| f(a).lt(&f(b)); + sort::partition_at_index(self, index, &mut g) + } + /// Moves all consecutive repeated elements to the end of the slice according to the /// [`PartialEq`] trait implementation. /// diff --git a/src/libcore/slice/sort.rs b/src/libcore/slice/sort.rs index dd9b49fb7a002..738f4e460deae 100644 --- a/src/libcore/slice/sort.rs +++ b/src/libcore/slice/sort.rs @@ -691,3 +691,92 @@ pub fn quicksort(v: &mut [T], mut is_less: F) recurse(v, &mut is_less, None, limit); } + +fn partition_at_index_loop<'a, T, F>( mut v: &'a mut [T], mut index: usize, is_less: &mut F + , mut pred: Option<&'a T>) where F: FnMut(&T, &T) -> bool +{ + loop { + // For slices of up to this length it's probably faster to simply sort them. + const MAX_INSERTION: usize = 10; + if v.len() <= MAX_INSERTION { + insertion_sort(v, is_less); + return; + } + + // Choose a pivot + let (pivot, _) = choose_pivot(v, is_less); + + // If the chosen pivot is equal to the predecessor, then it's the smallest element in the + // slice. Partition the slice into elements equal to and elements greater than the pivot. + // This case is usually hit when the slice contains many duplicate elements. + if let Some(p) = pred { + if !is_less(p, &v[pivot]) { + let mid = partition_equal(v, pivot, is_less); + + // If we've passed our index, then we're good. + if mid > index { + return; + } + + // Otherwise, continue sorting elements greater than the pivot. + v = &mut v[mid..]; + index = index - mid; + pred = None; + continue; + } + } + + let (mid, _) = partition(v, pivot, is_less); + + // Split the slice into `left`, `pivot`, and `right`. + let (left, right) = {v}.split_at_mut(mid); + let (pivot, right) = right.split_at_mut(1); + let pivot = &pivot[0]; + + if mid < index { + v = right; + index = index - mid - 1; + pred = Some(pivot); + } else if mid > index { + v = left; + } else { + // If mid == index, then we're done, since partition() guaranteed that all elements + // after mid are greater than or equal to mid. + return; + } + } +} + +pub fn partition_at_index(v: &mut [T], index: usize, mut is_less: F) + -> (&mut [T], &mut T, &mut [T]) where F: FnMut(&T, &T) -> bool +{ + use cmp::Ordering::Less; + use cmp::Ordering::Greater; + + if index >= v.len() { + panic!("partition_at_index index {} greater than length of slice {}", index, v.len()); + } + + if mem::size_of::() == 0 { + // Sorting has no meaningful behavior on zero-sized types. Do nothing. + } else if index == v.len() - 1 { + // Find max element and place it in the last position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let (max_index, _) = v.iter().enumerate().max_by( + |&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater }).unwrap(); + v.swap(max_index, index); + } else if index == 0 { + // Find min element and place it in the first position of the array. We're free to use + // `unwrap()` here because we know v must not be empty. + let (min_index, _) = v.iter().enumerate().min_by( + |&(_, x), &(_, y)| if is_less(x, y) { Less } else { Greater }).unwrap(); + v.swap(min_index, index); + } else { + partition_at_index_loop(v, index, &mut is_less, None); + } + + let (left, right) = v.split_at_mut(index); + let (pivot, right) = right.split_at_mut(1); + let pivot = &mut pivot[0]; + (left, pivot, right) +} diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 3e8549f8ae366..a815db07af9cd 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -22,6 +22,7 @@ #![feature(refcell_replace_swap)] #![feature(slice_patterns)] #![feature(sort_internals)] +#![feature(slice_partition_at_index)] #![feature(specialization)] #![feature(step_trait)] #![feature(str_internals)] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index e210e83122c47..4203faec4ceb6 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -1079,6 +1079,122 @@ fn sort_unstable() { assert!(v == [0xDEADBEEF]); } +#[test] +fn partition_at_index() { + use core::cmp::Ordering::{Equal, Greater, Less}; + use rand::rngs::SmallRng; + use rand::seq::SliceRandom; + use rand::{FromEntropy, Rng}; + + let mut rng = SmallRng::from_entropy(); + + for len in (2..21).chain(500..501) { + let mut orig = vec![0; len]; + + for &modulus in &[5, 10, 1000] { + for _ in 0..10 { + for i in 0..len { + orig[i] = rng.gen::() % modulus; + } + + let v_sorted = { + let mut v = orig.clone(); + v.sort(); + v + }; + + // Sort in default order. + for pivot in 0..len { + let mut v = orig.clone(); + v.partition_at_index(pivot); + + assert_eq!(v_sorted[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[i] <= v[j]); + } + } + } + + // Sort in ascending order. + for pivot in 0..len { + let mut v = orig.clone(); + let (left, pivot, right) = v.partition_at_index_by(pivot, |a, b| a.cmp(b)); + + assert_eq!(left.len() + right.len(), len - 1); + + for l in left { + assert!(l <= pivot); + for r in right.iter_mut() { + assert!(l <= r); + assert!(pivot <= r); + } + } + } + + // Sort in descending order. + let sort_descending_comparator = |a: &i32, b: &i32| b.cmp(a); + let v_sorted_descending = { + let mut v = orig.clone(); + v.sort_by(sort_descending_comparator); + v + }; + + for pivot in 0..len { + let mut v = orig.clone(); + v.partition_at_index_by(pivot, sort_descending_comparator); + + assert_eq!(v_sorted_descending[pivot], v[pivot]); + for i in 0..pivot { + for j in pivot..len { + assert!(v[j] <= v[i]); + } + } + } + } + } + } + + // Sort at index using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + let mut v = [0; 500]; + for i in 0..v.len() { + v[i] = i as i32; + } + + for pivot in 0..v.len() { + v.partition_at_index_by(pivot, |_, _| *[Less, Equal, Greater].choose(&mut rng).unwrap()); + v.sort(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + } + + // Should not panic. + [(); 10].partition_at_index(0); + [(); 10].partition_at_index(5); + [(); 10].partition_at_index(9); + [(); 100].partition_at_index(0); + [(); 100].partition_at_index(50); + [(); 100].partition_at_index(99); + + let mut v = [0xDEADBEEFu64]; + v.partition_at_index(0); + assert!(v == [0xDEADBEEF]); +} + +#[test] +#[should_panic(expected = "index 0 greater than length of slice")] +fn partition_at_index_zero_length() { + [0i32; 0].partition_at_index(0); +} + +#[test] +#[should_panic(expected = "index 20 greater than length of slice")] +fn partition_at_index_past_length() { + [0i32; 10].partition_at_index(20); +} + pub mod memchr { use core::slice::memchr::{memchr, memrchr}; diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 3d0e46d998622..d1a3d7c1f81e0 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -6,6 +6,7 @@ // This pass is supposed to perform only simple checks not requiring name resolution // or type checking or some other kind of complex analysis. +use std::mem; use rustc::lint; use rustc::session::Session; use syntax::ast::*; @@ -20,9 +21,73 @@ use errors::Applicability; struct AstValidator<'a> { session: &'a Session, + + // Used to ban nested `impl Trait`, e.g., `impl Into`. + // Nested `impl Trait` _is_ allowed in associated type position, + // e.g `impl Iterator` + outer_impl_trait: Option, + + // Used to ban `impl Trait` in path projections like `::Item` + // or `Foo::Bar` + is_impl_trait_banned: bool, } impl<'a> AstValidator<'a> { + fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.is_impl_trait_banned, true); + f(self); + self.is_impl_trait_banned = old; + } + + fn with_impl_trait(&mut self, outer_impl_trait: Option, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.outer_impl_trait, outer_impl_trait); + f(self); + self.outer_impl_trait = old; + } + + // Mirrors visit::walk_ty, but tracks relevant state + fn walk_ty(&mut self, t: &'a Ty) { + match t.node { + TyKind::ImplTrait(..) => { + self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)) + } + TyKind::Path(ref qself, ref path) => { + // We allow these: + // - `Option` + // - `option::Option` + // - `option::Option::Foo + // + // But not these: + // - `::Foo` + // - `option::Option::Foo`. + // + // To implement this, we disallow `impl Trait` from `qself` + // (for cases like `::Foo>`) + // but we allow `impl Trait` in `GenericArgs` + // iff there are no more PathSegments. + if let Some(ref qself) = *qself { + // `impl Trait` in `qself` is always illegal + self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty)); + } + + // Note that there should be a call to visit_path here, + // so if any logic is added to process `Path`s a call to it should be + // added both in visit_path and here. This code mirrors visit::walk_path. + for (i, segment) in path.segments.iter().enumerate() { + // Allow `impl Trait` iff we're on the final path segment + if i == path.segments.len() - 1 { + self.visit_path_segment(path.span, segment); + } else { + self.with_banned_impl_trait(|this| { + this.visit_path_segment(path.span, segment) + }); + } + } + } + _ => visit::walk_ty(self, t), + } + } + fn err_handler(&self) -> &errors::Handler { &self.session.diagnostic() } @@ -267,6 +332,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(_, ref bounds) => { + if self.is_impl_trait_banned { + struct_span_err!(self.session, ty.span, E0667, + "`impl Trait` is not allowed in path parameters").emit(); + } + + if let Some(outer_impl_trait) = self.outer_impl_trait { + struct_span_err!(self.session, ty.span, E0666, + "nested `impl Trait` is not allowed") + .span_label(outer_impl_trait, "outer `impl Trait`") + .span_label(ty.span, "nested `impl Trait` here") + .emit(); + + } if !bounds.iter() .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) { self.err_handler().span_err(ty.span, "at least one trait must be specified"); @@ -275,7 +353,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { _ => {} } - visit::walk_ty(self, ty) + self.walk_ty(ty) } fn visit_label(&mut self, label: &'a Label) { @@ -414,6 +492,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_foreign_item(self, fi) } + // Mirrors visit::walk_generic_args, but tracks relevant state + fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { + match *generic_args { + GenericArgs::AngleBracketed(ref data) => { + walk_list!(self, visit_generic_arg, &data.args); + // Type bindings such as `Item=impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| { + walk_list!(this, visit_assoc_type_binding, &data.bindings); + }); + } + GenericArgs::Parenthesized(ref data) => { + walk_list!(self, visit_ty, &data.inputs); + if let Some(ref type_) = data.output { + // `-> Foo` syntax is essentially an associated type binding, + // so it is also allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); + } + } + } + } + fn visit_generics(&mut self, generics: &'a Generics) { let mut seen_non_lifetime_param = false; let mut seen_default = None; @@ -490,148 +590,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } -// Bans nested `impl Trait`, e.g., `impl Into`. -// Nested `impl Trait` _is_ allowed in associated type position, -// e.g `impl Iterator` -struct NestedImplTraitVisitor<'a> { - session: &'a Session, - outer_impl_trait: Option, -} - -impl<'a> NestedImplTraitVisitor<'a> { - fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) - where F: FnOnce(&mut NestedImplTraitVisitor<'a>) - { - let old_outer_impl_trait = self.outer_impl_trait; - self.outer_impl_trait = outer_impl_trait; - f(self); - self.outer_impl_trait = old_outer_impl_trait; - } -} - - -impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'a Ty) { - if let TyKind::ImplTrait(..) = t.node { - if let Some(outer_impl_trait) = self.outer_impl_trait { - struct_span_err!(self.session, t.span, E0666, - "nested `impl Trait` is not allowed") - .span_label(outer_impl_trait, "outer `impl Trait`") - .span_label(t.span, "nested `impl Trait` here") - .emit(); - - } - self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)); - } else { - visit::walk_ty(self, t); - } - } - fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { - match *generic_args { - GenericArgs::AngleBracketed(ref data) => { - for arg in &data.args { - self.visit_generic_arg(arg) - } - for type_binding in &data.bindings { - // Type bindings such as `Item=impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| visit::walk_ty(this, &type_binding.ty)); - } - } - GenericArgs::Parenthesized(ref data) => { - for type_ in &data.inputs { - self.visit_ty(type_); - } - if let Some(ref type_) = data.output { - // `-> Foo` syntax is essentially an associated type binding, - // so it is also allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); - } - } - } - } - - fn visit_mac(&mut self, _mac: &Spanned) { - // covered in AstValidator - } -} - -// Bans `impl Trait` in path projections like `::Item` or `Foo::Bar`. -struct ImplTraitProjectionVisitor<'a> { - session: &'a Session, - is_banned: bool, -} - -impl<'a> ImplTraitProjectionVisitor<'a> { - fn with_ban(&mut self, f: F) - where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>) - { - let old_is_banned = self.is_banned; - self.is_banned = true; - f(self); - self.is_banned = old_is_banned; - } -} - -impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> { - fn visit_ty(&mut self, t: &'a Ty) { - match t.node { - TyKind::ImplTrait(..) => { - if self.is_banned { - struct_span_err!(self.session, t.span, E0667, - "`impl Trait` is not allowed in path parameters").emit(); - } - } - TyKind::Path(ref qself, ref path) => { - // We allow these: - // - `Option` - // - `option::Option` - // - `option::Option::Foo - // - // But not these: - // - `::Foo` - // - `option::Option::Foo`. - // - // To implement this, we disallow `impl Trait` from `qself` - // (for cases like `::Foo>`) - // but we allow `impl Trait` in `GenericArgs` - // iff there are no more PathSegments. - if let Some(ref qself) = *qself { - // `impl Trait` in `qself` is always illegal - self.with_ban(|this| this.visit_ty(&qself.ty)); - } - - for (i, segment) in path.segments.iter().enumerate() { - // Allow `impl Trait` iff we're on the final path segment - if i == path.segments.len() - 1 { - visit::walk_path_segment(self, path.span, segment); - } else { - self.with_ban(|this| - visit::walk_path_segment(this, path.span, segment)); - } - } - } - _ => visit::walk_ty(self, t), - } - } - - fn visit_mac(&mut self, _mac: &Spanned) { - // covered in AstValidator - } -} - pub fn check_crate(session: &Session, krate: &Crate) { - visit::walk_crate( - &mut NestedImplTraitVisitor { - session, - outer_impl_trait: None, - }, krate); - - visit::walk_crate( - &mut ImplTraitProjectionVisitor { - session, - is_banned: false, - }, krate); - - visit::walk_crate(&mut AstValidator { session }, krate) + visit::walk_crate(&mut AstValidator { + session, + outer_impl_trait: None, + is_impl_trait_banned: false, + }, krate) } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index dd63b4f20fa55..d1dfe9469fb77 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1199,7 +1199,6 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> (self.final_ty.unwrap_or(self.expected_ty), expression_ty) }; - let reason_label = "expected because of this statement"; let mut db; match cause.code { ObligationCauseCode::ReturnNoExpression => { @@ -1209,63 +1208,20 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> db.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id) => { - db = fcx.report_mismatched_types(cause, expected, found, err); - - let expr = expression.unwrap_or_else(|| { - span_bug!(cause.span, - "supposed to be part of a block tail expression, but the \ - expression is empty"); - }); - let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( - &mut db, - expr, + let parent_id = fcx.tcx.hir().get_parent_node(blk_id); + db = self.report_return_mismatched_types( + cause, expected, found, - cause.span, - blk_id, + err, + fcx, + parent_id, + expression.map(|expr| (expr, blk_id)), ); - // FIXME: replace with navigating up the chain until hitting an fn or - // bailing if no "pass-through" Node is found, in order to provide a - // suggestion when encountering something like: - // ``` - // fn foo(a: bool) -> impl Debug { - // if a { - // bar()?; - // } - // { - // let x = unsafe { bar() }; - // x - // } - // } - // ``` - // - // Verify that this is a tail expression of a function, otherwise the - // label pointing out the cause for the type coercion will be wrong - // as prior return coercions would not be relevant (#57664). - let parent_id = fcx.tcx.hir().get_parent_node(blk_id); - let parent = fcx.tcx.hir().get(fcx.tcx.hir().get_parent_node(parent_id)); - if fcx.get_node_fn_decl(parent).is_some() && !pointing_at_return_type { - if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { - db.span_label(*sp, reason_label); - } - } } - ObligationCauseCode::ReturnType(_id) => { - db = fcx.report_mismatched_types(cause, expected, found, err); - let _id = fcx.tcx.hir().get_parent_node(_id); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = fcx.get_fn_decl(_id) { - pointing_at_return_type = fcx.suggest_missing_return_type( - &mut db, &fn_decl, expected, found, can_suggest); - } - if let (Some(sp), false) = ( - fcx.ret_coercion_span.borrow().as_ref(), - pointing_at_return_type, - ) { - if !sp.overlaps(cause.span) { - db.span_label(*sp, reason_label); - } - } + ObligationCauseCode::ReturnType(id) => { + db = self.report_return_mismatched_types( + cause, expected, found, err, fcx, id, None); } _ => { db = fcx.report_mismatched_types(cause, expected, found, err); @@ -1283,6 +1239,59 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> } } + fn report_return_mismatched_types<'a>( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + err: TypeError<'tcx>, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + id: syntax::ast::NodeId, + expression: Option<(&'gcx hir::Expr, syntax::ast::NodeId)>, + ) -> DiagnosticBuilder<'a> { + let mut db = fcx.report_mismatched_types(cause, expected, found, err); + + let mut pointing_at_return_type = false; + let mut return_sp = None; + + // Verify that this is a tail expression of a function, otherwise the + // label pointing out the cause for the type coercion will be wrong + // as prior return coercions would not be relevant (#57664). + let parent_id = fcx.tcx.hir().get_parent_node(id); + let fn_decl = if let Some((expr, blk_id)) = expression { + pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( + &mut db, + expr, + expected, + found, + cause.span, + blk_id, + ); + let parent = fcx.tcx.hir().get(parent_id); + fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + } else { + fcx.get_fn_decl(parent_id) + }; + + if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) { + if expression.is_none() { + pointing_at_return_type |= fcx.suggest_missing_return_type( + &mut db, &fn_decl, expected, found, can_suggest); + } + if !pointing_at_return_type { + return_sp = Some(fn_decl.output.span()); // `impl Trait` return type + } + } + if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) { + db.span_label(return_sp, "expected because this return type..."); + db.span_label( *sp, format!( + "...is found to be `{}` here", + fcx.resolve_type_vars_with_obligations(expected), + )); + } + db + } + pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { if let Some(final_ty) = self.final_ty { final_ty diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 23bcd88d6afb5..f71a163cee261 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -304,7 +304,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ); if let Some(suggestion) = suggestion { // enum variant - err.help(&format!("did you mean `{}`?", suggestion)); + err.span_suggestion_with_applicability( + item_name.span, + "did you mean", + suggestion.to_string(), + Applicability::MaybeIncorrect, + ); } err } @@ -440,7 +445,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } if let Some(lev_candidate) = lev_candidate { - err.help(&format!("did you mean `{}`?", lev_candidate.ident)); + err.span_suggestion_with_applicability( + span, + "did you mean", + lev_candidate.ident.to_string(), + Applicability::MaybeIncorrect, + ); } err.emit(); } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 83db3f347a761..82f3463dba0fc 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -7,11 +7,9 @@ //! primitives](#primitives), [standard macros](#macros), [I/O] and //! [multithreading], among [many other things][other]. //! -//! `std` is available to all Rust crates by default, just as if each one -//! contained an `extern crate std;` import at the [crate root]. Therefore the +//! `std` is available to all Rust crates by default. Therefore the //! standard library can be accessed in [`use`] statements through the path -//! `std`, as in [`use std::env`], or in expressions through the absolute path -//! `::std`, as in [`::std::env::args`]. +//! `std`, as in [`use std::env`]. //! //! # How to read this documentation //! @@ -157,7 +155,6 @@ //! [TCP]: net/struct.TcpStream.html //! [The Rust Prelude]: prelude/index.html //! [UDP]: net/struct.UdpSocket.html -//! [`::std::env::args`]: env/fn.args.html //! [`Arc`]: sync/struct.Arc.html //! [owned slice]: boxed/index.html //! [`Cell`]: cell/struct.Cell.html @@ -191,7 +188,6 @@ //! [`thread`]: thread/index.html //! [`use std::env`]: env/index.html //! [`use`]: ../book/ch07-02-modules-and-use-to-control-scope-and-privacy.html#the-use-keyword-to-bring-paths-into-a-scope -//! [crate root]: ../book/ch07-01-packages-and-crates-for-making-libraries-and-executables.html //! [crates.io]: https://crates.io //! [deref-coercions]: ../book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods //! [files]: fs/struct.File.html diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 798f14dcba955..af521848e9057 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -140,6 +140,20 @@ pub enum GenericArgs { } impl GenericArgs { + pub fn is_parenthesized(&self) -> bool { + match *self { + Parenthesized(..) => true, + _ => false, + } + } + + pub fn is_angle_bracketed(&self) -> bool { + match *self { + AngleBracketed(..) => true, + _ => false, + } + } + pub fn span(&self) -> Span { match *self { AngleBracketed(ref data) => data.span, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 09ea099525326..c7e33a1656428 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -100,6 +100,7 @@ pub enum PathStyle { enum SemiColonMode { Break, Ignore, + Comma, } #[derive(Clone, Copy, PartialEq, Debug)] @@ -1988,6 +1989,44 @@ impl<'a> Parser<'a> { result.unwrap() } + token::Dot if self.look_ahead(1, |t| match t { + token::Literal(parse::token::Lit::Integer(_) , _) => true, + _ => false, + }) => { // recover from `let x = .4;` + let lo = self.span; + self.bump(); + if let token::Literal( + parse::token::Lit::Integer(val), + suffix, + ) = self.token { + let suffix = suffix.and_then(|s| { + let s = s.as_str().get(); + if ["f32", "f64"].contains(&s) { + Some(s) + } else { + None + } + }).unwrap_or(""); + self.bump(); + let sp = lo.to(self.prev_span); + let mut err = self.diagnostic() + .struct_span_err(sp, "float literals must have an integer part"); + err.span_suggestion_with_applicability( + sp, + "must have an integer part", + format!("0.{}{}", val, suffix), + Applicability::MachineApplicable, + ); + err.emit(); + return Ok(match suffix { + "f32" => ast::LitKind::Float(val, ast::FloatTy::F32), + "f64" => ast::LitKind::Float(val, ast::FloatTy::F64), + _ => ast::LitKind::FloatUnsuffixed(val), + }); + } else { + unreachable!(); + }; + } _ => { return self.unexpected_last(&self.token); } }; @@ -2149,7 +2188,27 @@ impl<'a> Parser<'a> { enable_warning: bool) -> PResult<'a, ()> { loop { - segments.push(self.parse_path_segment(style, enable_warning)?); + let segment = self.parse_path_segment(style, enable_warning)?; + if style == PathStyle::Expr { + // In order to check for trailing angle brackets, we must have finished + // recursing (`parse_path_segment` can indirectly call this function), + // that is, the next token must be the highlighted part of the below example: + // + // `Foo::>::Qux` + // ^ here + // + // As opposed to the below highlight (if we had only finished the first + // recursion): + // + // `Foo::>::Qux` + // ^ here + // + // `PathStyle::Expr` is only provided at the root invocation and never in + // `parse_path_segment` to recurse and therefore can be checked to maintain + // this invariant. + self.check_trailing_angle_brackets(&segment, token::ModSep); + } + segments.push(segment); if self.is_import_coupler() || !self.eat(&token::ModSep) { return Ok(()); @@ -2656,8 +2715,24 @@ impl<'a> Parser<'a> { break; } + let mut recovery_field = None; + if let token::Ident(ident, _) = self.token { + if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) { + // Use in case of error after field-looking code: `S { foo: () with a }` + let mut ident = ident.clone(); + ident.span = self.span; + recovery_field = Some(ast::Field { + ident, + span: self.span, + expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()), + is_shorthand: false, + attrs: ThinVec::new(), + }); + } + } + let mut parsed_field = None; match self.parse_field() { - Ok(f) => fields.push(f), + Ok(f) => parsed_field = Some(f), Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); @@ -2666,19 +2741,28 @@ impl<'a> Parser<'a> { // what comes next as additional fields, rather than // bailing out until next `}`. if self.token != token::Comma { - self.recover_stmt(); - break; + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); + if self.token != token::Comma { + break; + } } } } match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) { - Ok(()) => {} + Ok(()) => if let Some(f) = parsed_field.or(recovery_field) { + // only include the field if there's no parse error for the field name + fields.push(f); + } Err(mut e) => { + if let Some(f) = recovery_field { + fields.push(f); + } + e.span_label(struct_sp, "while parsing this struct"); e.emit(); - self.recover_stmt(); - break; + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); + self.eat(&token::Comma); } } } @@ -2757,6 +2841,8 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { let segment = self.parse_path_segment(PathStyle::Expr, true)?; + self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); + Ok(match self.token { token::OpenDelim(token::Paren) => { // Method call `expr.f()` @@ -2784,6 +2870,116 @@ impl<'a> Parser<'a> { }) } + /// This function checks if there are trailing angle brackets and produces + /// a diagnostic to suggest removing them. + /// + /// ```ignore (diagnostic) + /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); + /// ^^ help: remove extra angle brackets + /// ``` + fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: token::Token) { + // This function is intended to be invoked after parsing a path segment where there are two + // cases: + // + // 1. A specific token is expected after the path segment. + // eg. `x.foo(`, `x.foo::(` (parenthesis - method call), + // `Foo::`, or `Foo::::` (mod sep - continued path). + // 2. No specific token is expected after the path segment. + // eg. `x.foo` (field access) + // + // This function is called after parsing `.foo` and before parsing the token `end` (if + // present). This includes any angle bracket arguments, such as `.foo::` or + // `Foo::`. + + // We only care about trailing angle brackets if we previously parsed angle bracket + // arguments. This helps stop us incorrectly suggesting that extra angle brackets be + // removed in this case: + // + // `x.foo >> (3)` (where `x.foo` is a `u32` for example) + // + // This case is particularly tricky as we won't notice it just looking at the tokens - + // it will appear the same (in terms of upcoming tokens) as below (since the `::` will + // have already been parsed): + // + // `x.foo::>>(3)` + let parsed_angle_bracket_args = segment.args + .as_ref() + .map(|args| args.is_angle_bracketed()) + .unwrap_or(false); + + debug!( + "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", + parsed_angle_bracket_args, + ); + if !parsed_angle_bracket_args { + return; + } + + // Keep the span at the start so we can highlight the sequence of `>` characters to be + // removed. + let lo = self.span; + + // We need to look-ahead to see if we have `>` characters without moving the cursor forward + // (since we might have the field access case and the characters we're eating are + // actual operators and not trailing characters - ie `x.foo >> 3`). + let mut position = 0; + + // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how + // many of each (so we can correctly pluralize our error messages) and continue to + // advance. + let mut number_of_shr = 0; + let mut number_of_gt = 0; + while self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + if *t == token::BinOp(token::BinOpToken::Shr) { + number_of_shr += 1; + true + } else if *t == token::Gt { + number_of_gt += 1; + true + } else { + false + } + }) { + position += 1; + } + + // If we didn't find any trailing `>` characters, then we have nothing to error about. + debug!( + "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", + number_of_gt, number_of_shr, + ); + if number_of_gt < 1 && number_of_shr < 1 { + return; + } + + // Finally, double check that we have our end token as otherwise this is the + // second case. + if self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + *t == end + }) { + // Eat from where we started until the end token so that parsing can continue + // as if we didn't have those extra angle brackets. + self.eat_to_tokens(&[&end]); + let span = lo.until(self.span); + + let plural = number_of_gt > 1 || number_of_shr >= 1; + self.diagnostic() + .struct_span_err( + span, + &format!("unmatched angle bracket{}", if plural { "s" } else { "" }), + ) + .span_suggestion_with_applicability( + span, + &format!("remove extra angle bracket{}", if plural { "s" } else { "" }), + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + } + fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: Span) -> PResult<'a, P> { let mut e = e0; let mut hi; @@ -4542,13 +4738,13 @@ impl<'a> Parser<'a> { token::CloseDelim(token::DelimToken::Brace) => { if brace_depth == 0 { debug!("recover_stmt_ return - close delim {:?}", self.token); - return; + break; } brace_depth -= 1; self.bump(); if in_block && bracket_depth == 0 && brace_depth == 0 { debug!("recover_stmt_ return - block end {:?}", self.token); - return; + break; } } token::CloseDelim(token::DelimToken::Bracket) => { @@ -4560,7 +4756,7 @@ impl<'a> Parser<'a> { } token::Eof => { debug!("recover_stmt_ return - Eof"); - return; + break; } token::Semi => { self.bump(); @@ -4568,7 +4764,17 @@ impl<'a> Parser<'a> { brace_depth == 0 && bracket_depth == 0 { debug!("recover_stmt_ return - Semi"); - return; + break; + } + } + token::Comma => { + if break_on_semi == SemiColonMode::Comma && + brace_depth == 0 && + bracket_depth == 0 { + debug!("recover_stmt_ return - Semi"); + break; + } else { + self.bump(); } } _ => { @@ -7275,9 +7481,16 @@ impl<'a> Parser<'a> { // CONST ITEM if self.eat_keyword(keywords::Mut) { let prev_span = self.prev_span; - self.diagnostic().struct_span_err(prev_span, "const globals cannot be mutable") - .help("did you mean to declare a static?") - .emit(); + let mut err = self.diagnostic() + .struct_span_err(prev_span, "const globals cannot be mutable"); + err.span_label(prev_span, "cannot be mutable"); + err.span_suggestion_with_applicability( + const_span, + "you might want to declare a static instead", + "static".to_owned(), + Applicability::MaybeIncorrect, + ); + err.emit(); } let (ident, item_, extra_attrs) = self.parse_item_const(None)?; let prev_span = self.prev_span; diff --git a/src/test/ui/auto-ref-slice-plus-ref.stderr b/src/test/ui/auto-ref-slice-plus-ref.stderr index ab57fec0e7337..356e24d18a78f 100644 --- a/src/test/ui/auto-ref-slice-plus-ref.stderr +++ b/src/test/ui/auto-ref-slice-plus-ref.stderr @@ -2,12 +2,11 @@ error[E0599]: no method named `test_mut` found for type `std::vec::Vec<{integer} --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); //~ ERROR no method named `test_mut` found - | ^^^^^^^^ + | ^^^^^^^^ help: did you mean: `get_mut` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `test_mut`, perhaps you need to implement it: candidate #1: `MyIter` - = help: did you mean `get_mut`? error[E0599]: no method named `test` found for type `std::vec::Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:8:7 diff --git a/src/test/ui/block-result/issue-3563.stderr b/src/test/ui/block-result/issue-3563.stderr index 7f386630de590..a6346a5233f4c 100644 --- a/src/test/ui/block-result/issue-3563.stderr +++ b/src/test/ui/block-result/issue-3563.stderr @@ -2,9 +2,7 @@ error[E0599]: no method named `b` found for type `&Self` in the current scope --> $DIR/issue-3563.rs:3:17 | LL | || self.b() - | ^ - | - = help: did you mean `a`? + | ^ help: did you mean: `a` error: aborting due to previous error diff --git a/src/test/ui/empty/empty-struct-braces-expr.stderr b/src/test/ui/empty/empty-struct-braces-expr.stderr index e595e0ccb9293..19844503a4804 100644 --- a/src/test/ui/empty/empty-struct-braces-expr.stderr +++ b/src/test/ui/empty/empty-struct-braces-expr.stderr @@ -51,20 +51,18 @@ error[E0599]: no variant named `Empty3` found for type `empty_struct::XE` in the | LL | let xe3 = XE::Empty3; //~ ERROR no variant named `Empty3` found for type | ----^^^^^^ - | | + | | | + | | help: did you mean: `XEmpty3` | variant not found in `empty_struct::XE` - | - = help: did you mean `XEmpty3`? error[E0599]: no variant named `Empty3` found for type `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:23:19 | LL | let xe3 = XE::Empty3(); //~ ERROR no variant named `Empty3` found for type | ----^^^^^^ - | | + | | | + | | help: did you mean: `XEmpty3` | variant not found in `empty_struct::XE` - | - = help: did you mean `XEmpty3`? error: aborting due to 8 previous errors diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 57bd70de7c68f..6cd9d07748c27 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -1,8 +1,11 @@ error[E0308]: mismatched types --> $DIR/equality.rs:15:5 | +LL | fn two(x: bool) -> impl Foo { + | -------- expected because this return type... +LL | if x { LL | return 1_i32; - | ----- expected because of this statement + | ----- ...is found to be `i32` here LL | } LL | 0_u32 | ^^^^^ expected i32, found u32 diff --git a/src/test/ui/issues/issue-23217.stderr b/src/test/ui/issues/issue-23217.stderr index 208d0cc499a80..9cad002036fff 100644 --- a/src/test/ui/issues/issue-23217.stderr +++ b/src/test/ui/issues/issue-23217.stderr @@ -5,10 +5,9 @@ LL | pub enum SomeEnum { | ----------------- variant `A` not found here LL | B = SomeEnum::A, | ----------^ - | | + | | | + | | help: did you mean: `B` | variant not found in `SomeEnum` - | - = help: did you mean `B`? error: aborting due to previous error diff --git a/src/test/ui/issues/issue-28344.stderr b/src/test/ui/issues/issue-28344.stderr index 146ebad6ce175..b6f520c644b32 100644 --- a/src/test/ui/issues/issue-28344.stderr +++ b/src/test/ui/issues/issue-28344.stderr @@ -11,8 +11,7 @@ LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | --------^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | - = help: did you mean `bitxor`? + | help: did you mean: `bitxor` error[E0191]: the value of the associated type `Output` (from the trait `std::ops::BitXor`) must be specified --> $DIR/issue-28344.rs:8:13 @@ -27,8 +26,7 @@ LL | let g = BitXor::bitor; | --------^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | - = help: did you mean `bitxor`? + | help: did you mean: `bitxor` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-28971.stderr b/src/test/ui/issues/issue-28971.stderr index d5dbd5f64885c..77d0b53ad216b 100644 --- a/src/test/ui/issues/issue-28971.stderr +++ b/src/test/ui/issues/issue-28971.stderr @@ -5,9 +5,10 @@ LL | enum Foo { | -------- variant `Baz` not found here ... LL | Foo::Baz(..) => (), - | -----^^^---- variant not found in `Foo` - | - = help: did you mean `Bar`? + | -----^^^---- + | | | + | | help: did you mean: `Bar` + | variant not found in `Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr index 99c4a5b03b320..96d6814b0fe93 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr @@ -2,11 +2,10 @@ error[E0599]: no method named `deref_err` found for type `std::result::Result<_, --> $DIR/result-deref-err.rs:4:28 | LL | let _result = &Err(41).deref_err(); - | ^^^^^^^^^ + | ^^^^^^^^^ help: did you mean: `deref_ok` | = note: the method `deref_err` exists but the following trait bounds were not satisfied: `{integer} : std::ops::Deref` - = help: did you mean `deref_ok`? error: aborting due to previous error diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs new file mode 100644 index 0000000000000..4e9453653735a --- /dev/null +++ b/src/test/ui/issues/issue-52496.rs @@ -0,0 +1,12 @@ +struct Foo { bar: f64, baz: i64, bat: i64 } + +fn main() { + let _ = Foo { bar: .5, baz: 42 }; + //~^ ERROR float literals must have an integer part + //~| ERROR missing field `bat` in initializer of `Foo` + let bar = 1.5f32; + let _ = Foo { bar.into(), bat: -1, . }; + //~^ ERROR expected one of + //~| ERROR missing fields `bar`, `baz` in initializer of `Foo` + //~| ERROR expected identifier, found `.` +} diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr new file mode 100644 index 0000000000000..43009a15bd49a --- /dev/null +++ b/src/test/ui/issues/issue-52496.stderr @@ -0,0 +1,37 @@ +error: float literals must have an integer part + --> $DIR/issue-52496.rs:4:24 + | +LL | let _ = Foo { bar: .5, baz: 42 }; + | ^^ help: must have an integer part: `0.5` + +error: expected one of `,` or `}`, found `.` + --> $DIR/issue-52496.rs:8:22 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | --- ^ expected one of `,` or `}` here + | | + | while parsing this struct + +error: expected identifier, found `.` + --> $DIR/issue-52496.rs:8:40 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | --- ^ expected identifier + | | + | while parsing this struct + +error[E0063]: missing field `bat` in initializer of `Foo` + --> $DIR/issue-52496.rs:4:13 + | +LL | let _ = Foo { bar: .5, baz: 42 }; + | ^^^ missing `bat` + +error[E0063]: missing fields `bar`, `baz` in initializer of `Foo` + --> $DIR/issue-52496.rs:8:13 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | ^^^ missing `bar`, `baz` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/issues/issue-54521-1.rs b/src/test/ui/issues/issue-54521-1.rs new file mode 100644 index 0000000000000..d6a14a6e11f67 --- /dev/null +++ b/src/test/ui/issues/issue-54521-1.rs @@ -0,0 +1,16 @@ +// compile-pass + +// This test checks that the `remove extra angle brackets` error doesn't happen for some +// potential edge-cases.. + +struct X { + len: u32, +} + +fn main() { + let x = X { len: 3 }; + + let _ = x.len > (3); + + let _ = x.len >> (3); +} diff --git a/src/test/ui/issues/issue-54521-2.fixed b/src/test/ui/issues/issue-54521-2.fixed new file mode 100644 index 0000000000000..a91c4fe43ea46 --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = Vec::>>::new(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521-2.rs b/src/test/ui/issues/issue-54521-2.rs new file mode 100644 index 0000000000000..3639aac87ee7f --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.rs @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = Vec::>>::new(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = Vec::>>>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>::new(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521-2.stderr b/src/test/ui/issues/issue-54521-2.stderr new file mode 100644 index 0000000000000..9556b83b730a4 --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.stderr @@ -0,0 +1,26 @@ +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:11:25 + | +LL | let _ = Vec::>>>>::new(); + | ^^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:14:25 + | +LL | let _ = Vec::>>>::new(); + | ^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:17:25 + | +LL | let _ = Vec::>>::new(); + | ^^ help: remove extra angle brackets + +error: unmatched angle bracket + --> $DIR/issue-54521-2.rs:20:25 + | +LL | let _ = Vec::>::new(); + | ^ help: remove extra angle bracket + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/issues/issue-54521.fixed b/src/test/ui/issues/issue-54521.fixed new file mode 100644 index 0000000000000..84ab6866cf133 --- /dev/null +++ b/src/test/ui/issues/issue-54521.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = vec![1, 2, 3].into_iter().collect::>>>(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521.rs b/src/test/ui/issues/issue-54521.rs new file mode 100644 index 0000000000000..f1d6850417880 --- /dev/null +++ b/src/test/ui/issues/issue-54521.rs @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = vec![1, 2, 3].into_iter().collect::>>>(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521.stderr b/src/test/ui/issues/issue-54521.stderr new file mode 100644 index 0000000000000..ffefbfd0348a8 --- /dev/null +++ b/src/test/ui/issues/issue-54521.stderr @@ -0,0 +1,26 @@ +error: unmatched angle brackets + --> $DIR/issue-54521.rs:11:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); + | ^^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521.rs:14:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>(); + | ^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521.rs:17:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>(); + | ^^ help: remove extra angle brackets + +error: unmatched angle bracket + --> $DIR/issue-54521.rs:20:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>(); + | ^ help: remove extra angle bracket + +error: aborting due to 4 previous errors + diff --git a/src/test/ui/parser/issue-17718-const-mut.rs b/src/test/ui/parser/issue-17718-const-mut.rs index 4e74516d6b6fb..795a8c7631d9a 100644 --- a/src/test/ui/parser/issue-17718-const-mut.rs +++ b/src/test/ui/parser/issue-17718-const-mut.rs @@ -1,6 +1,6 @@ const mut //~ ERROR: const globals cannot be mutable -//~^ HELP did you mean to declare a static? +//~^^ HELP you might want to declare a static instead FOO: usize = 3; fn main() { diff --git a/src/test/ui/parser/issue-17718-const-mut.stderr b/src/test/ui/parser/issue-17718-const-mut.stderr index 29a65ebe41889..19f9fe19ef5ab 100644 --- a/src/test/ui/parser/issue-17718-const-mut.stderr +++ b/src/test/ui/parser/issue-17718-const-mut.stderr @@ -1,10 +1,10 @@ error: const globals cannot be mutable --> $DIR/issue-17718-const-mut.rs:2:1 | +LL | const + | ----- help: you might want to declare a static instead: `static` LL | mut //~ ERROR: const globals cannot be mutable - | ^^^ - | - = help: did you mean to declare a static? + | ^^^ cannot be mutable error: aborting due to previous error diff --git a/src/test/ui/parser/removed-syntax-with-1.rs b/src/test/ui/parser/removed-syntax-with-1.rs index 57cbe8d5be655..2c1e152dcee75 100644 --- a/src/test/ui/parser/removed-syntax-with-1.rs +++ b/src/test/ui/parser/removed-syntax-with-1.rs @@ -5,7 +5,6 @@ fn main() { } let a = S { foo: (), bar: () }; - let b = S { foo: () with a }; + let b = S { foo: () with a, bar: () }; //~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with` - //~| ERROR missing field `bar` in initializer of `main::S` } diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index 77ed4fcea517c..a157873916a64 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -1,15 +1,10 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with` --> $DIR/removed-syntax-with-1.rs:8:25 | -LL | let b = S { foo: () with a }; - | ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here +LL | let b = S { foo: () with a, bar: () }; + | - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here + | | + | while parsing this struct -error[E0063]: missing field `bar` in initializer of `main::S` - --> $DIR/removed-syntax-with-1.rs:8:13 - | -LL | let b = S { foo: () with a }; - | ^ missing `bar` - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-with-2.rs b/src/test/ui/parser/removed-syntax-with-2.rs index 11db391c5489a..f666da49696ba 100644 --- a/src/test/ui/parser/removed-syntax-with-2.rs +++ b/src/test/ui/parser/removed-syntax-with-2.rs @@ -7,6 +7,5 @@ fn main() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; //~^ ERROR expected one of `,` or `}`, found `a` - //~| ERROR cannot find value `with` in this scope - //~| ERROR struct `main::S` has no field named `with` + //~| ERROR missing field `bar` in initializer of `main::S` } diff --git a/src/test/ui/parser/removed-syntax-with-2.stderr b/src/test/ui/parser/removed-syntax-with-2.stderr index 5642d2f45ffce..7717b49d3a2c7 100644 --- a/src/test/ui/parser/removed-syntax-with-2.stderr +++ b/src/test/ui/parser/removed-syntax-with-2.stderr @@ -2,23 +2,16 @@ error: expected one of `,` or `}`, found `a` --> $DIR/removed-syntax-with-2.rs:8:31 | LL | let b = S { foo: (), with a }; - | ^ expected one of `,` or `}` here + | - ^ expected one of `,` or `}` here + | | + | while parsing this struct -error[E0425]: cannot find value `with` in this scope - --> $DIR/removed-syntax-with-2.rs:8:26 +error[E0063]: missing field `bar` in initializer of `main::S` + --> $DIR/removed-syntax-with-2.rs:8:13 | LL | let b = S { foo: (), with a }; - | ^^^^ not found in this scope + | ^ missing `bar` -error[E0560]: struct `main::S` has no field named `with` - --> $DIR/removed-syntax-with-2.rs:8:26 - | -LL | let b = S { foo: (), with a }; - | ^^^^ `main::S` does not have this field - | - = note: available fields are: `foo`, `bar` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors occurred: E0425, E0560. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.rs b/src/test/ui/parser/struct-field-numeric-shorthand.rs index 914588f51e1e3..58c40b3d96a49 100644 --- a/src/test/ui/parser/struct-field-numeric-shorthand.rs +++ b/src/test/ui/parser/struct-field-numeric-shorthand.rs @@ -1,6 +1,9 @@ struct Rgb(u8, u8, u8); fn main() { - let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` - //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb` + let _ = Rgb { 0, 1, 2 }; + //~^ ERROR expected identifier, found `0` + //~| ERROR expected identifier, found `1` + //~| ERROR expected identifier, found `2` + //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb` } diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.stderr b/src/test/ui/parser/struct-field-numeric-shorthand.stderr index f5dc226934ec6..cfb1f82014754 100644 --- a/src/test/ui/parser/struct-field-numeric-shorthand.stderr +++ b/src/test/ui/parser/struct-field-numeric-shorthand.stderr @@ -1,17 +1,33 @@ error: expected identifier, found `0` --> $DIR/struct-field-numeric-shorthand.rs:4:19 | -LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` +LL | let _ = Rgb { 0, 1, 2 }; | --- ^ expected identifier | | | while parsing this struct +error: expected identifier, found `1` + --> $DIR/struct-field-numeric-shorthand.rs:4:22 + | +LL | let _ = Rgb { 0, 1, 2 }; + | --- ^ expected identifier + | | + | while parsing this struct + +error: expected identifier, found `2` + --> $DIR/struct-field-numeric-shorthand.rs:4:25 + | +LL | let _ = Rgb { 0, 1, 2 }; + | --- ^ expected identifier + | | + | while parsing this struct + error[E0063]: missing fields `0`, `1`, `2` in initializer of `Rgb` --> $DIR/struct-field-numeric-shorthand.rs:4:13 | -LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` +LL | let _ = Rgb { 0, 1, 2 }; | ^^^ missing `0`, `1`, `2` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 62da0787b02a9..5ebe00e624fc1 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -1,8 +1,11 @@ error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:5:5 | +LL | fn foo() -> impl std::fmt::Display { + | ---------------------- expected because this return type... +LL | if false { LL | return 0i32; - | ---- expected because of this statement + | ---- ...is found to be `i32` here LL | } LL | 1u32 | ^^^^ expected i32, found u32 @@ -13,8 +16,11 @@ LL | 1u32 error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:13:16 | +LL | fn bar() -> impl std::fmt::Display { + | ---------------------- expected because this return type... +LL | if false { LL | return 0i32; - | ---- expected because of this statement + | ---- ...is found to be `i32` here LL | } else { LL | return 1u32; | ^^^^ expected i32, found u32 @@ -25,10 +31,12 @@ LL | return 1u32; error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:19:5 | +LL | fn baz() -> impl std::fmt::Display { + | ---------------------- expected because this return type... LL | / if false { LL | | //~^ ERROR mismatched types LL | | return 0i32; - | | ---- expected because of this statement + | | ---- ...is found to be `i32` here LL | | } else { LL | | 1u32 LL | | } diff --git a/src/test/ui/suggestions/recover-invalid-float.rs b/src/test/ui/suggestions/recover-invalid-float.rs new file mode 100644 index 0000000000000..506ef8900b881 --- /dev/null +++ b/src/test/ui/suggestions/recover-invalid-float.rs @@ -0,0 +1,11 @@ +fn main() { + let _: usize = .3; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types + let _: usize = .42f32; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types + let _: usize = .5f64; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types +} diff --git a/src/test/ui/suggestions/recover-invalid-float.stderr b/src/test/ui/suggestions/recover-invalid-float.stderr new file mode 100644 index 0000000000000..c464676b444cc --- /dev/null +++ b/src/test/ui/suggestions/recover-invalid-float.stderr @@ -0,0 +1,42 @@ +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:2:20 + | +LL | let _: usize = .3; + | ^^ help: must have an integer part: `0.3` + +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:5:20 + | +LL | let _: usize = .42f32; + | ^^^^^^ help: must have an integer part: `0.42f32` + +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:8:20 + | +LL | let _: usize = .5f64; + | ^^^^^ help: must have an integer part: `0.5f64` + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:2:20 + | +LL | let _: usize = .3; + | ^^ expected usize, found floating-point number + | + = note: expected type `usize` + found type `{float}` + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:5:20 + | +LL | let _: usize = .42f32; + | ^^^^^^ expected usize, found f32 + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:8:20 + | +LL | let _: usize = .5f64; + | ^^^^^ expected usize, found f64 + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index 39d96a943a18a..b7727cf03a4e7 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -5,25 +5,19 @@ LL | struct Foo; | ----------- method `bat` not found for this ... LL | f.bat(1.0); //~ ERROR no method named - | ^^^ - | - = help: did you mean `bar`? + | ^^^ help: did you mean: `bar` error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope --> $DIR/suggest-methods.rs:21:15 | LL | let _ = s.is_emtpy(); //~ ERROR no method named - | ^^^^^^^^ - | - = help: did you mean `is_empty`? + | ^^^^^^^^ help: did you mean: `is_empty` error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:25:19 | LL | let _ = 63u32.count_eos(); //~ ERROR no method named - | ^^^^^^^^^ - | - = help: did you mean `count_zeros`? + | ^^^^^^^^^ help: did you mean: `count_zeros` error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:28:19 diff --git a/src/test/ui/traits/trait-privacy.rs b/src/test/ui/traits/trait-privacy.rs index 523211fea9368..6254157e25da3 100644 --- a/src/test/ui/traits/trait-privacy.rs +++ b/src/test/ui/traits/trait-privacy.rs @@ -1,5 +1,4 @@ // compile-pass -#![feature(get_type_id)] #![allow(dead_code)] mod foo { pub use self::bar::T; @@ -18,7 +17,7 @@ fn g() { fn f() { let error = ::std::thread::spawn(|| {}).join().unwrap_err(); - error.get_type_id(); // Regression test for #21670 + error.type_id(); // Regression test for #21670 }