diff --git a/src/doc/unstable-book/src/library-features/char-error-internals.md b/src/doc/unstable-book/src/library-features/char-error-internals.md new file mode 100644 index 00000000000..8013b4988e1 --- /dev/null +++ b/src/doc/unstable-book/src/library-features/char-error-internals.md @@ -0,0 +1,5 @@ +# `char_error_internals` + +This feature is internal to the Rust compiler and is not intended for general use. + +------------------------ diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 8bb16febb04..c9c7a27c614 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2124,10 +2124,12 @@ impl From> for Vec { } } -#[stable(feature = "box_from_vec", since = "1.18.0")] -impl Into> for Vec { - fn into(self) -> Box<[T]> { - self.into_boxed_slice() +// note: test pulls in libstd, which causes errors here +#[cfg(not(test))] +#[stable(feature = "box_from_vec", since = "1.20.0")] +impl From> for Box<[T]> { + fn from(v: Vec) -> Box<[T]> { + v.into_boxed_slice() } } diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 98268e3813f..44f5fdbf431 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -19,7 +19,7 @@ use char_private::is_printable; use convert::TryFrom; use fmt::{self, Write}; use slice; -use str::from_utf8_unchecked_mut; +use str::{from_utf8_unchecked_mut, FromStr}; use iter::FusedIterator; use mem::transmute; @@ -208,6 +208,63 @@ impl From for char { } } + +/// An error which can be returned when parsing a char. +#[stable(feature = "char_from_str", since = "1.19.0")] +#[derive(Clone, Debug)] +pub struct ParseCharError { + kind: CharErrorKind, +} + +impl ParseCharError { + #[unstable(feature = "char_error_internals", + reason = "this method should not be available publicly", + issue = "0")] + #[doc(hidden)] + pub fn __description(&self) -> &str { + match self.kind { + CharErrorKind::EmptyString => { + "cannot parse char from empty string" + }, + CharErrorKind::TooManyChars => "too many characters in string" + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum CharErrorKind { + EmptyString, + TooManyChars, +} + +#[stable(feature = "char_from_str", since = "1.19.0")] +impl fmt::Display for ParseCharError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.__description().fmt(f) + } +} + + +#[stable(feature = "char_from_str", since = "1.19.0")] +impl FromStr for char { + type Err = ParseCharError; + + #[inline] + fn from_str(s: &str) -> Result { + let mut chars = s.chars(); + match (chars.next(), chars.next()) { + (None, _) => { + Err(ParseCharError { kind: CharErrorKind::EmptyString }) + }, + (Some(c), None) => Ok(c), + _ => { + Err(ParseCharError { kind: CharErrorKind::TooManyChars }) + } + } + } +} + + #[unstable(feature = "try_from", issue = "33417")] impl TryFrom for char { type Error = CharTryFromError; diff --git a/src/libcore/tests/char.rs b/src/libcore/tests/char.rs index e4012ec91e2..7c3b90c8153 100644 --- a/src/libcore/tests/char.rs +++ b/src/libcore/tests/char.rs @@ -10,6 +10,7 @@ use std::{char,str}; use std::convert::TryFrom; +use std::str::FromStr; #[test] fn test_convert() { @@ -28,6 +29,16 @@ fn test_convert() { assert!(char::try_from(0xFFFF_FFFF_u32).is_err()); } +#[test] +fn test_from_str() { + assert_eq!(char::from_str("a").unwrap(), 'a'); + assert_eq!(char::try_from("a").unwrap(), 'a'); + assert_eq!(char::from_str("\0").unwrap(), '\0'); + assert_eq!(char::from_str("\u{D7FF}").unwrap(), '\u{d7FF}'); + assert!(char::from_str("").is_err()); + assert!(char::from_str("abc").is_err()); +} + #[test] fn test_is_lowercase() { assert!('a'.is_lowercase()); diff --git a/src/liblibc b/src/liblibc index 03562b0cb26..2015cf17a6a 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit 03562b0cb26a00f49d4eaf18ca3e49608110b0c8 +Subproject commit 2015cf17a6a2a2280e93d9c57214ba92dbbaf42f diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 0c82679c307..473c0f3ffda 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use rustc::hir::def_id::DefId; use rustc::ty; use rustc::ty::adjustment; use util::nodemap::FxHashMap; @@ -144,20 +145,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { ty::TyTuple(ref tys, _) if tys.is_empty() => return, ty::TyNever => return, ty::TyBool => return, - ty::TyAdt(def, _) => { - let attrs = cx.tcx.get_attrs(def.did); - check_must_use(cx, &attrs, s.span) - } + ty::TyAdt(def, _) => check_must_use(cx, def.did, s.span), _ => false, }; if !warned { cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); } - fn check_must_use(cx: &LateContext, attrs: &[ast::Attribute], sp: Span) -> bool { - for attr in attrs { + fn check_must_use(cx: &LateContext, def_id: DefId, sp: Span) -> bool { + for attr in cx.tcx.get_attrs(def_id).iter() { if attr.check_name("must_use") { - let mut msg = "unused result which must be used".to_string(); + let mut msg = format!("unused `{}` which must be used", + cx.tcx.item_path_str(def_id)); // check for #[must_use="..."] if let Some(s) = attr.value_str() { msg.push_str(": "); diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 49fb44f9d37..b97c08b5bde 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -523,7 +523,10 @@ impl<'a> Resolver<'a> { }; let kind = ModuleKind::Def(Def::Mod(def_id), name); - self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)) + let module = + self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)); + self.extern_module_map.insert((def_id, macros_only), module); + module } pub fn macro_def_scope(&mut self, expansion: Mark) -> Module<'a> { diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 889ba81e778..770bca7524c 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -438,6 +438,35 @@ pub struct JoinPathsError { /// /// # Examples /// +/// Joining paths on a Unix-like platform: +/// +/// ``` +/// # if cfg!(unix) { +/// use std::env; +/// use std::ffi::OsString; +/// use std::path::Path; +/// +/// let paths = [Path::new("/bin"), Path::new("/usr/bin")]; +/// let path_os_string = env::join_paths(paths.iter()).unwrap(); +/// assert_eq!(path_os_string, OsString::from("/bin:/usr/bin")); +/// # } +/// ``` +/// +/// Joining a path containing a colon on a Unix-like platform results in an error: +/// +/// ``` +/// # if cfg!(unix) { +/// use std::env; +/// use std::path::Path; +/// +/// let paths = [Path::new("/bin"), Path::new("/usr/bi:n")]; +/// assert!(env::join_paths(paths.iter()).is_err()); +/// # } +/// ``` +/// +/// Using `env::join_paths` with `env::spit_paths` to append an item to the `PATH` environment +/// variable: +/// /// ``` /// use std::env; /// use std::path::PathBuf; diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 3d203429e7b..4b340f70fbc 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -340,6 +340,14 @@ impl Error for char::CharTryFromError { } } +#[stable(feature = "char_from_str", since = "1.19.0")] +impl Error for char::ParseCharError { + fn description(&self) -> &str { + self.__description() + } +} + + // copied from any.rs impl Error + 'static { /// Returns true if the boxed type is the same as `T` diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 74ac9d528bb..1a91417ca0e 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -152,6 +152,14 @@ pub struct CStr { /// in the vector provided. /// /// [`CString::new`]: struct.CString.html#method.new +/// +/// # Examples +/// +/// ``` +/// use std::ffi::{CString, NulError}; +/// +/// let _: NulError = CString::new(b"f\0oo".to_vec()).unwrap_err(); +/// ``` #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct NulError(usize, Vec); @@ -160,6 +168,14 @@ pub struct NulError(usize, Vec); /// byte was found too early in the slice provided or one wasn't found at all. /// /// [`CStr::from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul +/// +/// # Examples +/// +/// ``` +/// use std::ffi::{CStr, FromBytesWithNulError}; +/// +/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err(); +/// ``` #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub struct FromBytesWithNulError { @@ -271,6 +287,27 @@ impl CString { /// to undefined behavior or allocator corruption. /// /// [`into_raw`]: #method.into_raw + /// + /// # Examples + /// + /// Create a `CString`, pass ownership to an `extern` function (via raw pointer), then retake + /// ownership with `from_raw`: + /// + /// ```no_run + /// use std::ffi::CString; + /// use std::os::raw::c_char; + /// + /// extern { + /// fn some_extern_function(s: *mut c_char); + /// } + /// + /// let c_string = CString::new("Hello!").unwrap(); + /// let raw = c_string.into_raw(); + /// unsafe { + /// some_extern_function(raw); + /// let c_string = CString::from_raw(raw); + /// } + /// ``` #[stable(feature = "cstr_memory", since = "1.4.0")] pub unsafe fn from_raw(ptr: *mut c_char) -> CString { let len = libc::strlen(ptr) + 1; // Including the NUL byte @@ -412,6 +449,18 @@ impl CString { /// Extracts a [`CStr`] slice containing the entire string. /// /// [`CStr`]: struct.CStr.html + /// + /// # Examples + /// + /// ``` + /// #![feature(as_c_str)] + /// + /// use std::ffi::{CString, CStr}; + /// + /// let c_string = CString::new(b"foo".to_vec()).unwrap(); + /// let c_str = c_string.as_c_str(); + /// assert_eq!(c_str, CStr::from_bytes_with_nul(b"foo\0").unwrap()); + /// ``` #[inline] #[unstable(feature = "as_c_str", issue = "40380")] pub fn as_c_str(&self) -> &CStr { @@ -421,6 +470,18 @@ impl CString { /// Converts this `CString` into a boxed [`CStr`]. /// /// [`CStr`]: struct.CStr.html + /// + /// # Examples + /// + /// ``` + /// #![feature(into_boxed_c_str)] + /// + /// use std::ffi::{CString, CStr}; + /// + /// let c_string = CString::new(b"foo".to_vec()).unwrap(); + /// let boxed = c_string.into_boxed_c_str(); + /// assert_eq!(&*boxed, CStr::from_bytes_with_nul(b"foo\0").unwrap()); + /// ``` #[unstable(feature = "into_boxed_c_str", issue = "40380")] pub fn into_boxed_c_str(self) -> Box { unsafe { mem::transmute(self.into_inner()) } @@ -708,6 +769,24 @@ impl CStr { /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); /// assert!(cstr.is_ok()); /// ``` + /// + /// Creating a `CStr` without a trailing nul byte is an error: + /// + /// ``` + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"hello"); + /// assert!(c_str.is_err()); + /// ``` + /// + /// Creating a `CStr` with an interior nul byte is an error: + /// + /// ``` + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"he\0llo\0"); + /// assert!(c_str.is_err()); + /// ``` #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { @@ -800,6 +879,15 @@ impl CStr { /// > **Note**: This method is currently implemented as a 0-cost cast, but /// > it is planned to alter its definition in the future to perform the /// > length calculation whenever this method is called. + /// + /// # Examples + /// + /// ``` + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); + /// assert_eq!(c_str.to_bytes(), b"foo"); + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn to_bytes(&self) -> &[u8] { @@ -817,6 +905,15 @@ impl CStr { /// > length calculation whenever this method is called. /// /// [`to_bytes`]: #method.to_bytes + /// + /// # Examples + /// + /// ``` + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); + /// assert_eq!(c_str.to_bytes_with_nul(), b"foo\0"); + /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn to_bytes_with_nul(&self) -> &[u8] { @@ -834,6 +931,15 @@ impl CStr { /// > check whenever this method is called. /// /// [`&str`]: ../primitive.str.html + /// + /// # Examples + /// + /// ``` + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); + /// assert_eq!(c_str.to_str(), Ok("foo")); + /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] pub fn to_str(&self) -> Result<&str, str::Utf8Error> { // NB: When CStr is changed to perform the length check in .to_bytes() @@ -857,6 +963,31 @@ impl CStr { /// /// [`Cow`]: ../borrow/enum.Cow.html /// [`str`]: ../primitive.str.html + /// + /// # Examples + /// + /// Calling `to_string_lossy` on a `CStr` containing valid UTF-8: + /// + /// ``` + /// use std::borrow::Cow; + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"Hello World\0").unwrap(); + /// assert_eq!(c_str.to_string_lossy(), Cow::Borrowed("Hello World")); + /// ``` + /// + /// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8: + /// + /// ``` + /// use std::borrow::Cow; + /// use std::ffi::CStr; + /// + /// let c_str = CStr::from_bytes_with_nul(b"Hello \xF0\x90\x80World\0").unwrap(); + /// assert_eq!( + /// c_str.to_string_lossy(), + /// Cow::Owned(String::from("Hello �World")) as Cow + /// ); + /// ``` #[stable(feature = "cstr_to_str", since = "1.4.0")] pub fn to_string_lossy(&self) -> Cow { String::from_utf8_lossy(self.to_bytes()) @@ -866,6 +997,18 @@ impl CStr { /// /// [`Box`]: ../boxed/struct.Box.html /// [`CString`]: struct.CString.html + /// + /// # Examples + /// + /// ``` + /// #![feature(into_boxed_c_str)] + /// + /// use std::ffi::CString; + /// + /// let c_string = CString::new(b"foo".to_vec()).unwrap(); + /// let boxed = c_string.into_boxed_c_str(); + /// assert_eq!(boxed.into_c_string(), CString::new("foo").unwrap()); + /// ``` #[unstable(feature = "into_boxed_c_str", issue = "40380")] pub fn into_c_string(self: Box) -> CString { unsafe { mem::transmute(self) } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7c843711dbe..bafe23e80a0 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -254,6 +254,7 @@ #![feature(cfg_target_thread_local)] #![feature(cfg_target_vendor)] #![feature(char_escape_debug)] +#![feature(char_error_internals)] #![feature(char_internals)] #![feature(collections_range)] #![feature(compiler_builtins_lib)] diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index eb36cbe3b1f..d4d8993efb3 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -38,6 +38,8 @@ use tables::{conversions, derived_property, general_category, property}; pub use core::char::{MAX, from_digit, from_u32, from_u32_unchecked}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::char::{EscapeDebug, EscapeDefault, EscapeUnicode}; +#[stable(feature = "char_from_str", since = "1.19.0")] +pub use core::char::ParseCharError; // unstable reexports #[unstable(feature = "try_from", issue = "33417")] diff --git a/src/test/compile-fail/unused-result.rs b/src/test/compile-fail/unused-result.rs index 6ed3b081c97..0c6c7fc5a0d 100644 --- a/src/test/compile-fail/unused-result.rs +++ b/src/test/compile-fail/unused-result.rs @@ -26,8 +26,8 @@ fn qux() -> MustUseMsg { return foo::(); } #[allow(unused_results)] fn test() { foo::(); - foo::(); //~ ERROR: unused result which must be used - foo::(); //~ ERROR: unused result which must be used: some message + foo::(); //~ ERROR: unused `MustUse` which must be used + foo::(); //~ ERROR: unused `MustUseMsg` which must be used: some message } #[allow(unused_results, unused_must_use)] @@ -39,8 +39,8 @@ fn test2() { fn main() { foo::(); //~ ERROR: unused result - foo::(); //~ ERROR: unused result which must be used - foo::(); //~ ERROR: unused result which must be used: some message + foo::(); //~ ERROR: unused `MustUse` which must be used + foo::(); //~ ERROR: unused `MustUseMsg` which must be used: some message let _ = foo::(); let _ = foo::();