From 60274a95fef57a18113f7c48be68be31ece860eb Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Sun, 1 Sep 2019 17:23:20 +0200 Subject: [PATCH 1/6] Added From> for CString Updated tracking issue number Added safeguards for transmute_vec potentially being factored out elsewhere Clarified comment about avoiding mem::forget Removed unneeded unstable guard Added back a stability annotation for CI Minor documentation improvements Thanks to @Centril's code review Co-Authored-By: Mazdak Farrokhzad Improved layout checks, type annotations and removed unaccurate comment Removed unnecessary check on array layout Adapt the stability annotation to the new 1.41 milestone Co-Authored-By: Mazdak Farrokhzad Simplify the implementation. Use `Vec::into_raw_parts` instead of a manual implementation of `Vec::transmute`. If `Vec::into_raw_parts` uses `NonNull` instead, then the code here will need to be adjusted to take it into account (issue #65816) Reduce the whitespace of safety comments --- src/libstd/ffi/c_str.rs | 27 +++++++++++++++++++++++++++ src/libstd/lib.rs | 1 + 2 files changed, 28 insertions(+) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index d2ee65f0a74eb..217672ea292fc 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> for CString { } } +#[stable(feature = "cstring_from_vec_of_nonzerou8", since = "1.43.0")] +impl From> 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) -> CString { + unsafe { + // Transmute `Vec` to `Vec`. + let v: Vec = { + // Safety: + // - transmuting between `NonZeroU8` and `u8` is sound; + // - `alloc::Layout == alloc::Layout`. + let (ptr, len, cap): (*mut NonZeroU8, _, _) = Vec::into_raw_parts(v); + Vec::from_raw_parts(ptr.cast::(), 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 { #[inline] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9e9df5ab9b6ce..c05ebc7349d71 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -310,6 +310,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] From 8fb8bb4b3ff25570a7a9b105c1a569bb2307f25f Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Wed, 12 Feb 2020 19:51:30 +0000 Subject: [PATCH 2/6] Fix std::fs::copy on WASI target Previously `std::fs::copy` on wasm32-wasi would reuse code from the `sys_common` module and would successfully copy contents of the file just to fail right before closing it. This was happening because `sys_common::copy` tries to copy permissions of the file, but permissions are not a thing in WASI (at least yet) and `set_permissions` is implemented as an unconditional runtime error. This change instead adds a custom working implementation of `std::fs::copy` (like Rust already has on some other targets) that doesn't try to call `set_permissions` and is essentially a thin wrapper around `std::io::copy`. Fixes #68560. --- src/libstd/sys/wasi/fs.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libstd/sys/wasi/fs.rs b/src/libstd/sys/wasi/fs.rs index 04bfdf67e12d2..a11f61fdd69fd 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, 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 { + use crate::fs::File; + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + + io::copy(&mut reader, &mut writer) +} From be92200af7ac732063500a96890ed0354dcc490b Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 14 Feb 2020 21:49:57 +0900 Subject: [PATCH 3/6] Check `has_typeck_tables` before calling `typeck_tables_of` --- src/librustc_mir/const_eval/eval_queries.rs | 5 ++++- src/test/ui/consts/issue-68684.rs | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/consts/issue-68684.rs diff --git a/src/librustc_mir/const_eval/eval_queries.rs b/src/librustc_mir/const_eval/eval_queries.rs index 2e8e4dac237bc..4fdabed54b852 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/test/ui/consts/issue-68684.rs b/src/test/ui/consts/issue-68684.rs new file mode 100644 index 0000000000000..c98f199b60e49 --- /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() {} From 47aa2b5bfd35d3e094b55a1a2841698f2765e419 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Fri, 14 Feb 2020 12:46:06 +0900 Subject: [PATCH 4/6] Avoid calling `fn_sig` on closures --- src/librustc_typeck/collect.rs | 4 ++-- src/test/ui/macros/issue-68060.rs | 16 ++++++++++++++++ src/test/ui/macros/issue-68060.stderr | 24 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/macros/issue-68060.rs create mode 100644 src/test/ui/macros/issue-68060.stderr diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5349c324ad8a7..5779218a93797 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2877,7 +2877,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(); } @@ -2898,7 +2898,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/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs new file mode 100644 index 0000000000000..85ebd66b66cb6 --- /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 0000000000000..230867410d966 --- /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`. From a8fe47d1756df0fb68f6ed2edd2cedfb3cc66d7c Mon Sep 17 00:00:00 2001 From: Max Blachman Date: Sun, 24 Nov 2019 14:30:56 -0800 Subject: [PATCH 5/6] implement LowerExp and UpperExp for integers --- src/libcore/fmt/num.rs | 163 +++++++++++++++++++++++++++++++++++ src/libcore/tests/fmt/num.rs | 80 +++++++++++++++++ 2 files changed, 243 insertions(+) diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index d562639a6588d..c187471fb5faa 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::::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::::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 10fcf8b76ccb4..a50c2b46a919b 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] From 98757f14d0242e6dcae258df8aeb7e17580702ef Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Fri, 14 Feb 2020 22:28:13 -0500 Subject: [PATCH 6/6] Suggest a comma if a struct initializer field fails to parse Currently, we emit a "try adding a comma" suggestion if a comma is missing in a struct definition. However, we emit no such suggestion if a comma is missing in a struct initializer. This commit adds a "try adding a comma" suggestion when we don't find a comma during the parsing of a struct initializer field. The change to `src/test/ui/parser/removed-syntax-with-1.stderr` isn't great, but I don't see a good way of avoiding it. --- src/librustc_parse/parser/expr.rs | 8 ++++++- .../ui/parser/removed-syntax-with-1.stderr | 5 ++-- .../suggestions/struct-initializer-comma.rs | 13 +++++++++++ .../struct-initializer-comma.stderr | 23 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/suggestions/struct-initializer-comma.rs create mode 100644 src/test/ui/suggestions/struct-initializer-comma.stderr diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 5a4225ece65aa..20b9df0a2d9b6 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/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index 193138d74604d..c3f747b61b99f 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 0000000000000..613b976848f70 --- /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 0000000000000..731e8e10ab3ca --- /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`.