about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/fmt/num.rs163
-rw-r--r--src/libcore/tests/fmt/num.rs80
-rw-r--r--src/librustc_mir/const_eval/eval_queries.rs5
-rw-r--r--src/librustc_parse/parser/expr.rs8
-rw-r--r--src/librustc_typeck/collect.rs4
-rw-r--r--src/libstd/ffi/c_str.rs27
-rw-r--r--src/libstd/lib.rs1
-rw-r--r--src/libstd/sys/wasi/fs.rs10
-rw-r--r--src/test/ui/consts/issue-68684.rs15
-rw-r--r--src/test/ui/macros/issue-68060.rs16
-rw-r--r--src/test/ui/macros/issue-68060.stderr24
-rw-r--r--src/test/ui/parser/removed-syntax-with-1.stderr5
-rw-r--r--src/test/ui/suggestions/struct-initializer-comma.rs13
-rw-r--r--src/test/ui/suggestions/struct-initializer-comma.stderr23
14 files changed, 387 insertions, 7 deletions
diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs
index d562639a658..c187471fb5f 100644
--- a/src/libcore/fmt/num.rs
+++ b/src/libcore/fmt/num.rs
@@ -4,6 +4,7 @@
 
 use crate::fmt;
 use crate::mem::MaybeUninit;
+use crate::num::flt2dec;
 use crate::ops::{Div, Rem, Sub};
 use crate::ptr;
 use crate::slice;
@@ -256,6 +257,161 @@ macro_rules! impl_Display {
     };
 }
 
+macro_rules! impl_Exp {
+    ($($t:ident),* as $u:ident via $conv_fn:ident named $name:ident) => {
+        fn $name(
+            mut n: $u,
+            is_nonnegative: bool,
+            upper: bool,
+            f: &mut fmt::Formatter<'_>
+        ) -> fmt::Result {
+            let (mut n, mut exponent, trailing_zeros, added_precision) = {
+                let mut exponent = 0;
+                // count and remove trailing decimal zeroes
+                while n % 10 == 0 && n >= 10 {
+                    n /= 10;
+                    exponent += 1;
+                }
+                let trailing_zeros = exponent;
+
+                let (added_precision, subtracted_precision) = match f.precision() {
+                    Some(fmt_prec) => {
+                        // number of decimal digits minus 1
+                        let mut tmp = n;
+                        let mut prec = 0;
+                        while tmp >= 10 {
+                            tmp /= 10;
+                            prec += 1;
+                        }
+                        (fmt_prec.saturating_sub(prec), prec.saturating_sub(fmt_prec))
+                    }
+                    None => (0,0)
+                };
+                for _ in 1..subtracted_precision {
+                    n/=10;
+                    exponent += 1;
+                }
+                if subtracted_precision != 0 {
+                    let rem = n % 10;
+                    n /= 10;
+                    exponent += 1;
+                    // round up last digit
+                    if rem >= 5 {
+                        n += 1;
+                    }
+                }
+                (n, exponent, trailing_zeros, added_precision)
+            };
+
+            // 39 digits (worst case u128) + . = 40
+            let mut buf = [MaybeUninit::<u8>::uninit(); 40];
+            let mut curr = buf.len() as isize; //index for buf
+            let buf_ptr = MaybeUninit::first_ptr_mut(&mut buf);
+            let lut_ptr = DEC_DIGITS_LUT.as_ptr();
+
+            // decode 2 chars at a time
+            while n >= 100 {
+                let d1 = ((n % 100) as isize) << 1;
+                curr -= 2;
+                unsafe {
+                    ptr::copy_nonoverlapping(lut_ptr.offset(d1), buf_ptr.offset(curr), 2);
+                }
+                n /= 100;
+                exponent += 2;
+            }
+            // n is <= 99, so at most 2 chars long
+            let mut n = n as isize; // possibly reduce 64bit math
+            // decode second-to-last character
+            if n >= 10 {
+                curr -= 1;
+                unsafe {
+                    *buf_ptr.offset(curr) = (n as u8 % 10_u8) + b'0';
+                }
+                n /= 10;
+                exponent += 1;
+            }
+            // add decimal point iff >1 mantissa digit will be printed
+            if exponent != trailing_zeros || added_precision != 0 {
+                curr -= 1;
+                unsafe {
+                    *buf_ptr.offset(curr) = b'.';
+                }
+            }
+
+            let buf_slice = unsafe {
+                // decode last character
+                curr -= 1;
+                *buf_ptr.offset(curr) = (n as u8) + b'0';
+
+                let len = buf.len() - curr as usize;
+                slice::from_raw_parts(buf_ptr.offset(curr), len)
+            };
+
+            // stores 'e' (or 'E') and the up to 2-digit exponent
+            let mut exp_buf = [MaybeUninit::<u8>::uninit(); 3];
+            let exp_ptr = MaybeUninit::first_ptr_mut(&mut exp_buf);
+            let exp_slice = unsafe {
+                *exp_ptr.offset(0) = if upper {b'E'} else {b'e'};
+                let len = if exponent < 10 {
+                    *exp_ptr.offset(1) = (exponent as u8) + b'0';
+                    2
+                } else {
+                    let off = exponent << 1;
+                    ptr::copy_nonoverlapping(lut_ptr.offset(off), exp_ptr.offset(1), 2);
+                    3
+                };
+                slice::from_raw_parts(exp_ptr, len)
+            };
+
+            let parts = &[
+                flt2dec::Part::Copy(buf_slice),
+                flt2dec::Part::Zero(added_precision),
+                flt2dec::Part::Copy(exp_slice)
+            ];
+            let sign = if !is_nonnegative {
+                &b"-"[..]
+            } else if f.sign_plus() {
+                &b"+"[..]
+            } else {
+                &b""[..]
+            };
+            let formatted = flt2dec::Formatted{sign, parts};
+            f.pad_formatted_parts(&formatted)
+        }
+
+        $(
+            #[stable(feature = "integer_exp_format", since = "1.42.0")]
+            impl fmt::LowerExp for $t {
+                #[allow(unused_comparisons)]
+                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    let is_nonnegative = *self >= 0;
+                    let n = if is_nonnegative {
+                        self.$conv_fn()
+                    } else {
+                        // convert the negative num to positive by summing 1 to it's 2 complement
+                        (!self.$conv_fn()).wrapping_add(1)
+                    };
+                    $name(n, is_nonnegative, false, f)
+                }
+            })*
+        $(
+            #[stable(feature = "integer_exp_format", since = "1.42.0")]
+            impl fmt::UpperExp for $t {
+                #[allow(unused_comparisons)]
+                fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+                    let is_nonnegative = *self >= 0;
+                    let n = if is_nonnegative {
+                        self.$conv_fn()
+                    } else {
+                        // convert the negative num to positive by summing 1 to it's 2 complement
+                        (!self.$conv_fn()).wrapping_add(1)
+                    };
+                    $name(n, is_nonnegative, true, f)
+                }
+            })*
+    };
+}
+
 // Include wasm32 in here since it doesn't reflect the native pointer size, and
 // often cares strongly about getting a smaller code size.
 #[cfg(any(target_pointer_width = "64", target_arch = "wasm32"))]
@@ -265,6 +421,10 @@ mod imp {
         i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
             as u64 via to_u64 named fmt_u64
     );
+    impl_Exp!(
+        i8, u8, i16, u16, i32, u32, i64, u64, usize, isize
+            as u64 via to_u64 named exp_u64
+    );
 }
 
 #[cfg(not(any(target_pointer_width = "64", target_arch = "wasm32")))]
@@ -272,6 +432,9 @@ mod imp {
     use super::*;
     impl_Display!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named fmt_u32);
     impl_Display!(i64, u64 as u64 via to_u64 named fmt_u64);
+    impl_Exp!(i8, u8, i16, u16, i32, u32, isize, usize as u32 via to_u32 named exp_u32);
+    impl_Exp!(i64, u64 as u64 via to_u64 named exp_u64);
 }
 
 impl_Display!(i128, u128 as u128 via to_u128 named fmt_u128);
+impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
diff --git a/src/libcore/tests/fmt/num.rs b/src/libcore/tests/fmt/num.rs
index 10fcf8b76cc..a50c2b46a91 100644
--- a/src/libcore/tests/fmt/num.rs
+++ b/src/libcore/tests/fmt/num.rs
@@ -38,6 +38,16 @@ fn test_format_int() {
     assert_eq!(format!("{:o}", 1i16), "1");
     assert_eq!(format!("{:o}", 1i32), "1");
     assert_eq!(format!("{:o}", 1i64), "1");
+    assert_eq!(format!("{:e}", 1isize), "1e0");
+    assert_eq!(format!("{:e}", 1i8), "1e0");
+    assert_eq!(format!("{:e}", 1i16), "1e0");
+    assert_eq!(format!("{:e}", 1i32), "1e0");
+    assert_eq!(format!("{:e}", 1i64), "1e0");
+    assert_eq!(format!("{:E}", 1isize), "1E0");
+    assert_eq!(format!("{:E}", 1i8), "1E0");
+    assert_eq!(format!("{:E}", 1i16), "1E0");
+    assert_eq!(format!("{:E}", 1i32), "1E0");
+    assert_eq!(format!("{:E}", 1i64), "1E0");
 
     assert_eq!(format!("{}", 1usize), "1");
     assert_eq!(format!("{}", 1u8), "1");
@@ -69,6 +79,14 @@ fn test_format_int() {
     assert_eq!(format!("{:o}", 1u16), "1");
     assert_eq!(format!("{:o}", 1u32), "1");
     assert_eq!(format!("{:o}", 1u64), "1");
+    assert_eq!(format!("{:e}", 1u8), "1e0");
+    assert_eq!(format!("{:e}", 1u16), "1e0");
+    assert_eq!(format!("{:e}", 1u32), "1e0");
+    assert_eq!(format!("{:e}", 1u64), "1e0");
+    assert_eq!(format!("{:E}", 1u8), "1E0");
+    assert_eq!(format!("{:E}", 1u16), "1E0");
+    assert_eq!(format!("{:E}", 1u32), "1E0");
+    assert_eq!(format!("{:E}", 1u64), "1E0");
 
     // Test a larger number
     assert_eq!(format!("{:b}", 55), "110111");
@@ -76,6 +94,64 @@ fn test_format_int() {
     assert_eq!(format!("{}", 55), "55");
     assert_eq!(format!("{:x}", 55), "37");
     assert_eq!(format!("{:X}", 55), "37");
+    assert_eq!(format!("{:e}", 55), "5.5e1");
+    assert_eq!(format!("{:E}", 55), "5.5E1");
+    assert_eq!(format!("{:e}", 10000000000u64), "1e10");
+    assert_eq!(format!("{:E}", 10000000000u64), "1E10");
+    assert_eq!(format!("{:e}", 10000000001u64), "1.0000000001e10");
+    assert_eq!(format!("{:E}", 10000000001u64), "1.0000000001E10");
+}
+
+#[test]
+fn test_format_int_exp_limits() {
+    use core::{i128, i16, i32, i64, i8, u128, u16, u32, u64, u8};
+    assert_eq!(format!("{:e}", i8::MIN), "-1.28e2");
+    assert_eq!(format!("{:e}", i8::MAX), "1.27e2");
+    assert_eq!(format!("{:e}", i16::MIN), "-3.2768e4");
+    assert_eq!(format!("{:e}", i16::MAX), "3.2767e4");
+    assert_eq!(format!("{:e}", i32::MIN), "-2.147483648e9");
+    assert_eq!(format!("{:e}", i32::MAX), "2.147483647e9");
+    assert_eq!(format!("{:e}", i64::MIN), "-9.223372036854775808e18");
+    assert_eq!(format!("{:e}", i64::MAX), "9.223372036854775807e18");
+    assert_eq!(format!("{:e}", i128::MIN), "-1.70141183460469231731687303715884105728e38");
+    assert_eq!(format!("{:e}", i128::MAX), "1.70141183460469231731687303715884105727e38");
+
+    assert_eq!(format!("{:e}", u8::MAX), "2.55e2");
+    assert_eq!(format!("{:e}", u16::MAX), "6.5535e4");
+    assert_eq!(format!("{:e}", u32::MAX), "4.294967295e9");
+    assert_eq!(format!("{:e}", u64::MAX), "1.8446744073709551615e19");
+    assert_eq!(format!("{:e}", u128::MAX), "3.40282366920938463463374607431768211455e38");
+}
+
+#[test]
+fn test_format_int_exp_precision() {
+    use core::{i128, i16, i32, i64, i8};
+
+    //test that float and integer match
+    let big_int: u32 = 314_159_265;
+    assert_eq!(format!("{:.1e}", big_int), format!("{:.1e}", f64::from(big_int)));
+
+    //test adding precision
+    assert_eq!(format!("{:.10e}", i8::MIN), "-1.2800000000e2");
+    assert_eq!(format!("{:.10e}", i16::MIN), "-3.2768000000e4");
+    assert_eq!(format!("{:.10e}", i32::MIN), "-2.1474836480e9");
+    assert_eq!(format!("{:.20e}", i64::MIN), "-9.22337203685477580800e18");
+    assert_eq!(format!("{:.40e}", i128::MIN), "-1.7014118346046923173168730371588410572800e38");
+
+    //test rounding
+    assert_eq!(format!("{:.1e}", i8::MIN), "-1.3e2");
+    assert_eq!(format!("{:.1e}", i16::MIN), "-3.3e4");
+    assert_eq!(format!("{:.1e}", i32::MIN), "-2.1e9");
+    assert_eq!(format!("{:.1e}", i64::MIN), "-9.2e18");
+    assert_eq!(format!("{:.1e}", i128::MIN), "-1.7e38");
+
+    //test huge precision
+    assert_eq!(format!("{:.1000e}", 1), format!("1.{}e0", "0".repeat(1000)));
+    //test zero precision
+    assert_eq!(format!("{:.0e}", 1), format!("1e0",));
+
+    //test padding with precision (and sign)
+    assert_eq!(format!("{:+10.3e}", 1), "  +1.000e0");
 }
 
 #[test]
@@ -86,6 +162,8 @@ fn test_format_int_zero() {
     assert_eq!(format!("{:o}", 0), "0");
     assert_eq!(format!("{:x}", 0), "0");
     assert_eq!(format!("{:X}", 0), "0");
+    assert_eq!(format!("{:e}", 0), "0e0");
+    assert_eq!(format!("{:E}", 0), "0E0");
 
     assert_eq!(format!("{}", 0u32), "0");
     assert_eq!(format!("{:?}", 0u32), "0");
@@ -93,6 +171,8 @@ fn test_format_int_zero() {
     assert_eq!(format!("{:o}", 0u32), "0");
     assert_eq!(format!("{:x}", 0u32), "0");
     assert_eq!(format!("{:X}", 0u32), "0");
+    assert_eq!(format!("{:e}", 0u32), "0e0");
+    assert_eq!(format!("{:E}", 0u32), "0E0");
 }
 
 #[test]
diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs
index 2e8e4dac237..4fdabed54b8 100644
--- a/src/librustc_mir/const_eval/eval_queries.rs
+++ b/src/librustc_mir/const_eval/eval_queries.rs
@@ -288,7 +288,10 @@ pub fn const_eval_raw_provider<'tcx>(
     let cid = key.value;
     let def_id = cid.instance.def.def_id();
 
-    if def_id.is_local() && tcx.typeck_tables_of(def_id).tainted_by_errors {
+    if def_id.is_local()
+        && tcx.has_typeck_tables(def_id)
+        && tcx.typeck_tables_of(def_id).tainted_by_errors
+    {
         return Err(ErrorHandled::Reported);
     }
 
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index 5a4225ece65..20b9df0a2d9 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -1832,10 +1832,16 @@ impl<'a> Parser<'a> {
                     }
                 }
                 Err(mut e) => {
+                    e.span_label(struct_sp, "while parsing this struct");
                     if let Some(f) = recovery_field {
                         fields.push(f);
+                        e.span_suggestion(
+                            self.prev_span.shrink_to_hi(),
+                            "try adding a comma",
+                            ",".into(),
+                            Applicability::MachineApplicable,
+                        );
                     }
-                    e.span_label(struct_sp, "while parsing this struct");
                     e.emit();
                     self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore);
                     self.eat(&token::Comma);
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 1a505a39e46..80d914d8d0a 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -2280,7 +2280,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         } else if attr.check_name(sym::thread_local) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
         } else if attr.check_name(sym::track_caller) {
-            if tcx.fn_sig(id).abi() != abi::Abi::Rust {
+            if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust {
                 struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
                     .emit();
             }
@@ -2301,7 +2301,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 codegen_fn_attrs.export_name = Some(s);
             }
         } else if attr.check_name(sym::target_feature) {
-            if tcx.fn_sig(id).unsafety() == Unsafety::Normal {
+            if tcx.is_closure(id) || tcx.fn_sig(id).unsafety() == Unsafety::Normal {
                 let msg = "`#[target_feature(..)]` can only be applied to `unsafe` functions";
                 tcx.sess
                     .struct_span_err(attr.span, msg)
diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs
index 700e015b08e..04eaba515ff 100644
--- a/src/libstd/ffi/c_str.rs
+++ b/src/libstd/ffi/c_str.rs
@@ -6,6 +6,7 @@ use crate::fmt::{self, Write};
 use crate::io;
 use crate::mem;
 use crate::memchr;
+use crate::num::NonZeroU8;
 use crate::ops;
 use crate::os::raw::c_char;
 use crate::ptr;
@@ -741,6 +742,32 @@ impl From<Box<CStr>> for CString {
     }
 }
 
+#[stable(feature = "cstring_from_vec_of_nonzerou8", since = "1.43.0")]
+impl From<Vec<NonZeroU8>> for CString {
+    /// Converts a [`Vec`]`<`[`NonZeroU8`]`>` into a [`CString`] without
+    /// copying nor checking for inner null bytes.
+    ///
+    /// [`CString`]: ../ffi/struct.CString.html
+    /// [`NonZeroU8`]: ../num/struct.NonZeroU8.html
+    /// [`Vec`]: ../vec/struct.Vec.html
+    #[inline]
+    fn from(v: Vec<NonZeroU8>) -> CString {
+        unsafe {
+            // Transmute `Vec<NonZeroU8>` to `Vec<u8>`.
+            let v: Vec<u8> = {
+                // Safety:
+                //   - transmuting between `NonZeroU8` and `u8` is sound;
+                //   - `alloc::Layout<NonZeroU8> == alloc::Layout<u8>`.
+                let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v);
+                Vec::from_raw_parts(ptr.cast::<u8>(), len, cap)
+            };
+            // Safety: `v` cannot contain null bytes, given the type-level
+            // invariant of `NonZeroU8`.
+            CString::from_vec_unchecked(v)
+        }
+    }
+}
+
 #[stable(feature = "more_box_slice_clone", since = "1.29.0")]
 impl Clone for Box<CStr> {
     #[inline]
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index f9c9f224730..799585ffc0a 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -309,6 +309,7 @@
 #![feature(unboxed_closures)]
 #![feature(untagged_unions)]
 #![feature(unwind_attributes)]
+#![feature(vec_into_raw_parts)]
 // NB: the above list is sorted to minimize merge conflicts.
 #![default_lib_allocator]
 
diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs
index 04bfdf67e12..a11f61fdd69 100644
--- a/src/libstd/sys/wasi/fs.rs
+++ b/src/libstd/sys/wasi/fs.rs
@@ -12,7 +12,6 @@ use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
 use crate::sys_common::FromInner;
 
-pub use crate::sys_common::fs::copy;
 pub use crate::sys_common::fs::remove_dir_all;
 
 pub struct File {
@@ -647,3 +646,12 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
 pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
     f.to_str().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "input must be utf-8"))
 }
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    use crate::fs::File;
+
+    let mut reader = File::open(from)?;
+    let mut writer = File::create(to)?;
+
+    io::copy(&mut reader, &mut writer)
+}
diff --git a/src/test/ui/consts/issue-68684.rs b/src/test/ui/consts/issue-68684.rs
new file mode 100644
index 00000000000..c98f199b60e
--- /dev/null
+++ b/src/test/ui/consts/issue-68684.rs
@@ -0,0 +1,15 @@
+// check-pass
+
+enum _Enum {
+    A(),
+}
+
+type _E = _Enum;
+
+const fn _a() -> _Enum {
+    _E::A()
+}
+
+const _A: _Enum = _a();
+
+fn main() {}
diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs
new file mode 100644
index 00000000000..85ebd66b66c
--- /dev/null
+++ b/src/test/ui/macros/issue-68060.rs
@@ -0,0 +1,16 @@
+// build-fail
+
+#![feature(track_caller)]
+
+fn main() {
+    (0..)
+        .map(
+            #[target_feature(enable = "")]
+            //~^ ERROR: the feature named `` is not valid for this target
+            //~| ERROR: `#[target_feature(..)]` can only be applied to `unsafe` functions
+            #[track_caller]
+            //~^ ERROR: `#[track_caller]` requires Rust ABI
+            |_| (),
+        )
+        .next();
+}
diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr
new file mode 100644
index 00000000000..230867410d9
--- /dev/null
+++ b/src/test/ui/macros/issue-68060.stderr
@@ -0,0 +1,24 @@
+error: `#[target_feature(..)]` can only be applied to `unsafe` functions
+  --> $DIR/issue-68060.rs:8:13
+   |
+LL |             #[target_feature(enable = "")]
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can only be applied to `unsafe` functions
+...
+LL |             |_| (),
+   |             ------ not an `unsafe` function
+
+error: the feature named `` is not valid for this target
+  --> $DIR/issue-68060.rs:8:30
+   |
+LL |             #[target_feature(enable = "")]
+   |                              ^^^^^^^^^^^ `` is not valid for this target
+
+error[E0737]: `#[track_caller]` requires Rust ABI
+  --> $DIR/issue-68060.rs:11:13
+   |
+LL |             #[track_caller]
+   |             ^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0737`.
diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr
index 193138d7460..c3f747b61b9 100644
--- a/src/test/ui/parser/removed-syntax-with-1.stderr
+++ b/src/test/ui/parser/removed-syntax-with-1.stderr
@@ -2,8 +2,9 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with`
   --> $DIR/removed-syntax-with-1.rs:8:25
    |
 LL |     let b = S { foo: () with a, bar: () };
-   |             -           ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator
-   |             |
+   |             -          -^^^^ expected one of `,`, `.`, `?`, `}`, or an operator
+   |             |          |
+   |             |          help: try adding a comma: `,`
    |             while parsing this struct
 
 error: aborting due to previous error
diff --git a/src/test/ui/suggestions/struct-initializer-comma.rs b/src/test/ui/suggestions/struct-initializer-comma.rs
new file mode 100644
index 00000000000..613b976848f
--- /dev/null
+++ b/src/test/ui/suggestions/struct-initializer-comma.rs
@@ -0,0 +1,13 @@
+struct Foo {
+    first: bool,
+    second: u8,
+}
+
+fn main() {
+    let a = Foo {
+        //~^ ERROR missing field
+        first: true
+        second: 25
+        //~^ ERROR expected one of
+    };
+}
diff --git a/src/test/ui/suggestions/struct-initializer-comma.stderr b/src/test/ui/suggestions/struct-initializer-comma.stderr
new file mode 100644
index 00000000000..731e8e10ab3
--- /dev/null
+++ b/src/test/ui/suggestions/struct-initializer-comma.stderr
@@ -0,0 +1,23 @@
+error: expected one of `,`, `.`, `?`, `}`, or an operator, found `second`
+  --> $DIR/struct-initializer-comma.rs:10:9
+   |
+LL |     let a = Foo {
+   |             --- while parsing this struct
+LL |
+LL |         first: true
+   |                    -
+   |                    |
+   |                    expected one of `,`, `.`, `?`, `}`, or an operator
+   |                    help: try adding a comma: `,`
+LL |         second: 25
+   |         ^^^^^^ unexpected token
+
+error[E0063]: missing field `second` in initializer of `Foo`
+  --> $DIR/struct-initializer-comma.rs:7:13
+   |
+LL |     let a = Foo {
+   |             ^^^ missing `second`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0063`.