From a8ff1aead87a702d78609dd873579106f6bc00c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Fri, 1 Apr 2022 18:32:02 +0200 Subject: [PATCH 1/9] Avoid duplication of doc comments in `std::char` constants and functions. For those consts and functions, only the summary is kept and a reference to the `char` associated const/method is included. Additionaly, re-exported functions have been converted to function definitions that call the previously re-exported function. This makes it easier to add a deprecated attribute to these functions in the future. --- library/core/src/char/convert.rs | 139 ++----------------------------- library/core/src/char/decode.rs | 49 +---------- library/core/src/char/mod.rs | 69 +++++++++------ 3 files changed, 54 insertions(+), 203 deletions(-) diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 4ee0310b361..778f06aeb63 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -6,52 +6,10 @@ use crate::mem::transmute; use crate::str::FromStr; -/// Converts a `u32` to a `char`. -/// -/// Note that all [`char`]s are valid [`u32`]s, and can be cast to one with -/// `as`: -/// -/// ``` -/// let c = '💯'; -/// let i = c as u32; -/// -/// assert_eq!(128175, i); -/// ``` -/// -/// However, the reverse is not true: not all valid [`u32`]s are valid -/// [`char`]s. `from_u32()` will return `None` if the input is not a valid value -/// for a [`char`]. -/// -/// For an unsafe version of this function which ignores these checks, see -/// [`from_u32_unchecked`]. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::char; -/// -/// let c = char::from_u32(0x2764); -/// -/// assert_eq!(Some('❤'), c); -/// ``` -/// -/// Returning `None` when the input is not a valid [`char`]: -/// -/// ``` -/// use std::char; -/// -/// let c = char::from_u32(0x110000); -/// -/// assert_eq!(None, c); -/// ``` -#[doc(alias = "chr")] +/// Converts a `u32` to a `char`. See [`char::from_u32`]. #[must_use] #[inline] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] -pub const fn from_u32(i: u32) -> Option { +pub(super) const fn from_u32(i: u32) -> Option { // FIXME: once Result::ok is const fn, use it here match char_try_from_u32(i) { Ok(c) => Some(c), @@ -59,44 +17,11 @@ pub const fn from_u32(i: u32) -> Option { } } -/// Converts a `u32` to a `char`, ignoring validity. -/// -/// Note that all [`char`]s are valid [`u32`]s, and can be cast to one with -/// `as`: -/// -/// ``` -/// let c = '💯'; -/// let i = c as u32; -/// -/// assert_eq!(128175, i); -/// ``` -/// -/// However, the reverse is not true: not all valid [`u32`]s are valid -/// [`char`]s. `from_u32_unchecked()` will ignore this, and blindly cast to -/// [`char`], possibly creating an invalid one. -/// -/// # Safety -/// -/// This function is unsafe, as it may construct invalid `char` values. -/// -/// For a safe version of this function, see the [`from_u32`] function. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::char; -/// -/// let c = unsafe { char::from_u32_unchecked(0x2764) }; -/// -/// assert_eq!('❤', c); -/// ``` +/// Converts a `u32` to a `char`, ignoring validity. See [`char::from_u32_unchecked`]. +#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] #[inline] #[must_use] -#[stable(feature = "char_from_unchecked", since = "1.5.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] -pub const unsafe fn from_u32_unchecked(i: u32) -> char { +pub(super) const unsafe fn from_u32_unchecked(i: u32) -> char { // SAFETY: the caller must guarantee that `i` is a valid char value. if cfg!(debug_assertions) { char::from_u32(i).unwrap() } else { unsafe { transmute(i) } } } @@ -317,60 +242,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } -/// Converts a digit in the given radix to a `char`. -/// -/// A 'radix' here is sometimes also called a 'base'. A radix of two -/// indicates a binary number, a radix of ten, decimal, and a radix of -/// sixteen, hexadecimal, to give some common values. Arbitrary -/// radices are supported. -/// -/// `from_digit()` will return `None` if the input is not a digit in -/// the given radix. -/// -/// # Panics -/// -/// Panics if given a radix larger than 36. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::char; -/// -/// let c = char::from_digit(4, 10); -/// -/// assert_eq!(Some('4'), c); -/// -/// // Decimal 11 is a single digit in base 16 -/// let c = char::from_digit(11, 16); -/// -/// assert_eq!(Some('b'), c); -/// ``` -/// -/// Returning `None` when the input is not a digit: -/// -/// ``` -/// use std::char; -/// -/// let c = char::from_digit(20, 10); -/// -/// assert_eq!(None, c); -/// ``` -/// -/// Passing a large radix, causing a panic: -/// -/// ```should_panic -/// use std::char; -/// -/// // this panics -/// let c = char::from_digit(1, 37); -/// ``` +/// Converts a digit in the given radix to a `char`. See [`char::from_digit`]. #[inline] #[must_use] -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] -pub const fn from_digit(num: u32, radix: u32) -> Option { +pub(super) const fn from_digit(num: u32, radix: u32) -> Option { if radix > 36 { panic!("from_digit: radix is too high (maximum 36)"); } diff --git a/library/core/src/char/decode.rs b/library/core/src/char/decode.rs index 794c9c13cc3..71297acd171 100644 --- a/library/core/src/char/decode.rs +++ b/library/core/src/char/decode.rs @@ -30,54 +30,9 @@ pub struct DecodeUtf16Error { } /// Creates an iterator over the UTF-16 encoded code points in `iter`, -/// returning unpaired surrogates as `Err`s. -/// -/// # Examples -/// -/// Basic usage: -/// -/// ``` -/// use std::char::decode_utf16; -/// -/// // 𝄞music -/// let v = [ -/// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, -/// ]; -/// -/// assert_eq!( -/// decode_utf16(v.iter().cloned()) -/// .map(|r| r.map_err(|e| e.unpaired_surrogate())) -/// .collect::>(), -/// vec![ -/// Ok('𝄞'), -/// Ok('m'), Ok('u'), Ok('s'), -/// Err(0xDD1E), -/// Ok('i'), Ok('c'), -/// Err(0xD834) -/// ] -/// ); -/// ``` -/// -/// A lossy decoder can be obtained by replacing `Err` results with the replacement character: -/// -/// ``` -/// use std::char::{decode_utf16, REPLACEMENT_CHARACTER}; -/// -/// // 𝄞music -/// let v = [ -/// 0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0xDD1E, 0x0069, 0x0063, 0xD834, -/// ]; -/// -/// assert_eq!( -/// decode_utf16(v.iter().cloned()) -/// .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) -/// .collect::(), -/// "𝄞mus�ic�" -/// ); -/// ``` -#[stable(feature = "decode_utf16", since = "1.9.0")] +/// returning unpaired surrogates as `Err`s. See [`char::decode_utf16`]. #[inline] -pub fn decode_utf16>(iter: I) -> DecodeUtf16 { +pub(super) fn decode_utf16>(iter: I) -> DecodeUtf16 { DecodeUtf16 { iter: iter.into_iter(), buf: None } } diff --git a/library/core/src/char/mod.rs b/library/core/src/char/mod.rs index 9364ac4f3ec..0df23e7bbe6 100644 --- a/library/core/src/char/mod.rs +++ b/library/core/src/char/mod.rs @@ -23,18 +23,12 @@ mod methods; // stable re-exports -#[stable(feature = "char_from_unchecked", since = "1.5.0")] -pub use self::convert::from_u32_unchecked; #[stable(feature = "try_from", since = "1.34.0")] pub use self::convert::CharTryFromError; #[stable(feature = "char_from_str", since = "1.20.0")] pub use self::convert::ParseCharError; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::convert::{from_digit, from_u32}; #[stable(feature = "decode_utf16", since = "1.9.0")] -pub use self::decode::{decode_utf16, DecodeUtf16, DecodeUtf16Error}; -#[stable(feature = "unicode_version", since = "1.45.0")] -pub use crate::unicode::UNICODE_VERSION; +pub use self::decode::{DecodeUtf16, DecodeUtf16Error}; // perma-unstable re-exports #[unstable(feature = "char_internals", reason = "exposed only for libstd", issue = "none")] @@ -89,30 +83,57 @@ Cn Unassigned a reserved unassigned code point or a noncharacter */ -/// The highest valid code point a `char` can have, `'\u{10FFFF}'`. -/// -/// # Examples -/// -/// ``` -/// # fn something_which_returns_char() -> char { 'a' } -/// let c: char = something_which_returns_char(); -/// assert!(c <= char::MAX); -/// -/// let value_at_max = char::MAX as u32; -/// assert_eq!(char::from_u32(value_at_max), Some('\u{10FFFF}')); -/// assert_eq!(char::from_u32(value_at_max + 1), None); -/// ``` +/// The highest valid code point a `char` can have, `'\u{10FFFF}'`. Use [`char::MAX`] instead. #[stable(feature = "rust1", since = "1.0.0")] pub const MAX: char = char::MAX; /// `U+FFFD REPLACEMENT CHARACTER` (�) is used in Unicode to represent a -/// decoding error. -/// -/// It can occur, for example, when giving ill-formed UTF-8 bytes to -/// [`String::from_utf8_lossy`](../../std/string/struct.String.html#method.from_utf8_lossy). +/// decoding error. Use [`char::REPLACEMENT_CHARACTER`] instead. #[stable(feature = "decode_utf16", since = "1.9.0")] pub const REPLACEMENT_CHARACTER: char = char::REPLACEMENT_CHARACTER; +/// The version of [Unicode](https://www.unicode.org/) that the Unicode parts of +/// `char` and `str` methods are based on. Use [`char::UNICODE_VERSION`] instead. +#[stable(feature = "unicode_version", since = "1.45.0")] +pub const UNICODE_VERSION: (u8, u8, u8) = char::UNICODE_VERSION; + +/// Creates an iterator over the UTF-16 encoded code points in `iter`, returning +/// unpaired surrogates as `Err`s. Use [`char::decode_utf16`] instead. +#[stable(feature = "decode_utf16", since = "1.9.0")] +#[inline] +pub fn decode_utf16>(iter: I) -> DecodeUtf16 { + self::decode::decode_utf16(iter) +} + +/// Converts a `u32` to a `char`. Use [`char::from_u32`] instead. +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[must_use] +#[inline] +pub const fn from_u32(i: u32) -> Option { + self::convert::from_u32(i) +} + +/// Converts a `u32` to a `char`, ignoring validity. Use [`char::from_u32_unchecked`]. +/// instead. +#[stable(feature = "char_from_unchecked", since = "1.5.0")] +#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[must_use] +#[inline] +pub const unsafe fn from_u32_unchecked(i: u32) -> char { + // SAFETY: the safety contract must be upheld by the caller. + unsafe { self::convert::from_u32_unchecked(i) } +} + +/// Converts a digit in the given radix to a `char`. Use [`char::from_digit`] instead. +#[stable(feature = "rust1", since = "1.0.0")] +#[rustc_const_unstable(feature = "const_char_convert", issue = "89259")] +#[must_use] +#[inline] +pub const fn from_digit(num: u32, radix: u32) -> Option { + self::convert::from_digit(num, radix) +} + /// Returns an iterator that yields the hexadecimal Unicode escape of a /// character, as `char`s. /// From d5440926e2832f810cadeccff6e2ca5332e11466 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Thu, 7 Apr 2022 22:46:53 +0400 Subject: [PATCH 2/9] Suggest replacing typeof(...) with an actual type This commit suggests replacing typeof(...) with an actual type of "...", for example in case of `typeof(1)` we suggest replacing it with `i32`. If the expression - Is not const (`{ let a = 1; let _: typeof(a); }`) - Can't be found (`let _: typeof(this_variable_does_not_exist)`) - Or has non-suggestable type (closure, generator, error, etc) we don't suggest anything. --- .../locales/en-US/diagnostics.ftl | 1 + compiler/rustc_typeck/src/astconv/mod.rs | 12 ++++++++++-- compiler/rustc_typeck/src/errors.rs | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl index 336e7a66857..2b1deb34304 100644 --- a/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl +++ b/compiler/rustc_error_messages/locales/en-US/diagnostics.ftl @@ -62,6 +62,7 @@ typeck-functional-record-update-on-non-struct = typeck-typeof-reserved-keyword-used = `typeof` is a reserved keyword but unimplemented + .suggestion = consider replacing `typeof(...)` with an actual type .label = reserved keyword typeck-return-stmt-outside-of-fn-body = diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index b6287031665..42379055fff 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2462,8 +2462,16 @@ fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool self.normalize_ty(ast_ty.span, array_ty) } hir::TyKind::Typeof(ref e) => { - tcx.sess.emit_err(TypeofReservedKeywordUsed { span: ast_ty.span }); - tcx.type_of(tcx.hir().local_def_id(e.hir_id)) + let ty = tcx.type_of(tcx.hir().local_def_id(e.hir_id)); + let span = ast_ty.span; + tcx.sess.emit_err(TypeofReservedKeywordUsed { + span, + ty, + opt_sugg: Some((span, Applicability::MachineApplicable)) + .filter(|_| ty.is_suggestable()), + }); + + ty } hir::TyKind::Infer => { // Infer also appears as the type of arguments or return diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs index 0b78aea9f05..1088be5f566 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_typeck/src/errors.rs @@ -1,5 +1,7 @@ //! Errors emitted by typeck. +use rustc_errors::Applicability; use rustc_macros::SessionDiagnostic; +use rustc_middle::ty::Ty; use rustc_span::{symbol::Ident, Span, Symbol}; #[derive(SessionDiagnostic)] @@ -127,10 +129,13 @@ pub struct FunctionalRecordUpdateOnNonStruct { #[derive(SessionDiagnostic)] #[error(code = "E0516", slug = "typeck-typeof-reserved-keyword-used")] -pub struct TypeofReservedKeywordUsed { +pub struct TypeofReservedKeywordUsed<'tcx> { + pub ty: Ty<'tcx>, #[primary_span] #[label] pub span: Span, + #[suggestion_verbose(message = "suggestion", code = "{ty}")] + pub opt_sugg: Option<(Span, Applicability)>, } #[derive(SessionDiagnostic)] From 71fea61bc9f387d99dd99b1f99d35e1a92f578a7 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda <41065217+TaKO8Ki@users.noreply.github.com> Date: Fri, 8 Apr 2022 23:17:57 +0900 Subject: [PATCH 3/9] suggest adding a local for vector to fix borrowck errors --- .../src/diagnostics/conflict_errors.rs | 40 ++++++++++++++----- .../borrowck/suggest-local-var-for-vector.rs | 4 ++ .../suggest-local-var-for-vector.stderr | 24 +++++++++++ .../suggest-storing-local-var-for-vector.rs | 4 ++ ...uggest-storing-local-var-for-vector.stderr | 24 +++++++++++ .../two-phase-nonrecv-autoref.nll.stderr | 22 ++++++++++ 6 files changed, 109 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/borrowck/suggest-local-var-for-vector.rs create mode 100644 src/test/ui/borrowck/suggest-local-var-for-vector.stderr create mode 100644 src/test/ui/borrowck/suggest-storing-local-var-for-vector.rs create mode 100644 src/test/ui/borrowck/suggest-storing-local-var-for-vector.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b945d687043..9e5fb674772 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -785,13 +785,22 @@ fn suggest_using_local_if_applicable( issued_borrow: &BorrowData<'tcx>, explanation: BorrowExplanation, ) { - let used_in_call = - matches!(explanation, BorrowExplanation::UsedLater(LaterUseKind::Call, _call_span, _)); + let used_in_call = matches!( + explanation, + BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _) + ); if !used_in_call { debug!("not later used in call"); return; } + let use_span = + if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation { + Some(use_span) + } else { + None + }; + let outer_call_loc = if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location { loc @@ -835,7 +844,10 @@ fn suggest_using_local_if_applicable( debug!("===> outer_call_loc = {:?}, inner_call_loc = {:?}", outer_call_loc, inner_call_loc); let inner_call_span = inner_call_term.source_info.span; - let outer_call_span = outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span; + let outer_call_span = match use_span { + Some(span) => span, + None => outer_call_stmt.either(|s| s.source_info, |t| t.source_info).span, + }; if outer_call_span == inner_call_span || !outer_call_span.contains(inner_call_span) { // FIXME: This stops the suggestion in some cases where it should be emitted. // Fix the spans for those cases so it's emitted correctly. @@ -845,8 +857,20 @@ fn suggest_using_local_if_applicable( ); return; } - err.span_help(inner_call_span, "try adding a local storing this argument..."); - err.span_help(outer_call_span, "...and then using that local as the argument to this call"); + err.span_help( + inner_call_span, + &format!( + "try adding a local storing this{}...", + if use_span.is_some() { "" } else { " argument" } + ), + ); + err.span_help( + outer_call_span, + &format!( + "...and then using that local {}", + if use_span.is_some() { "here" } else { "as the argument to this call" } + ), + ); } fn suggest_split_at_mut_if_applicable( @@ -1912,10 +1936,8 @@ pub(crate) fn report_illegal_reassignment( } else { "cannot assign twice to immutable variable" }; - if span != assigned_span { - if !from_arg { - err.span_label(assigned_span, format!("first assignment to {}", place_description)); - } + if span != assigned_span && !from_arg { + err.span_label(assigned_span, format!("first assignment to {}", place_description)); } if let Some(decl) = local_decl && let Some(name) = local_name diff --git a/src/test/ui/borrowck/suggest-local-var-for-vector.rs b/src/test/ui/borrowck/suggest-local-var-for-vector.rs new file mode 100644 index 00000000000..40f013f6a78 --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-for-vector.rs @@ -0,0 +1,4 @@ +fn main() { + let mut vec = vec![0u32; 420]; + vec[vec.len() - 1] = 123; //~ ERROR cannot borrow `vec` as immutable because it is also borrowed as mutable +} diff --git a/src/test/ui/borrowck/suggest-local-var-for-vector.stderr b/src/test/ui/borrowck/suggest-local-var-for-vector.stderr new file mode 100644 index 00000000000..615fffcd578 --- /dev/null +++ b/src/test/ui/borrowck/suggest-local-var-for-vector.stderr @@ -0,0 +1,24 @@ +error[E0502]: cannot borrow `vec` as immutable because it is also borrowed as mutable + --> $DIR/suggest-local-var-for-vector.rs:3:9 + | +LL | vec[vec.len() - 1] = 123; + | ----^^^^^^^^^----- + | | | + | | immutable borrow occurs here + | mutable borrow occurs here + | mutable borrow later used here + | +help: try adding a local storing this... + --> $DIR/suggest-local-var-for-vector.rs:3:9 + | +LL | vec[vec.len() - 1] = 123; + | ^^^^^^^^^ +help: ...and then using that local here + --> $DIR/suggest-local-var-for-vector.rs:3:5 + | +LL | vec[vec.len() - 1] = 123; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/suggest-storing-local-var-for-vector.rs b/src/test/ui/borrowck/suggest-storing-local-var-for-vector.rs new file mode 100644 index 00000000000..40f013f6a78 --- /dev/null +++ b/src/test/ui/borrowck/suggest-storing-local-var-for-vector.rs @@ -0,0 +1,4 @@ +fn main() { + let mut vec = vec![0u32; 420]; + vec[vec.len() - 1] = 123; //~ ERROR cannot borrow `vec` as immutable because it is also borrowed as mutable +} diff --git a/src/test/ui/borrowck/suggest-storing-local-var-for-vector.stderr b/src/test/ui/borrowck/suggest-storing-local-var-for-vector.stderr new file mode 100644 index 00000000000..e3a16eddfd5 --- /dev/null +++ b/src/test/ui/borrowck/suggest-storing-local-var-for-vector.stderr @@ -0,0 +1,24 @@ +error[E0502]: cannot borrow `vec` as immutable because it is also borrowed as mutable + --> $DIR/suggest-storing-local-var-for-vector.rs:3:9 + | +LL | vec[vec.len() - 1] = 123; + | ----^^^^^^^^^----- + | | | + | | immutable borrow occurs here + | mutable borrow occurs here + | mutable borrow later used here + | +help: try adding a local storing this... + --> $DIR/suggest-storing-local-var-for-vector.rs:3:9 + | +LL | vec[vec.len() - 1] = 123; + | ^^^^^^^^^ +help: ...and then using that local here + --> $DIR/suggest-storing-local-var-for-vector.rs:3:5 + | +LL | vec[vec.len() - 1] = 123; + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr index 50d277a12f7..0f2daaf99d9 100644 --- a/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr +++ b/src/test/ui/borrowck/two-phase-nonrecv-autoref.nll.stderr @@ -54,6 +54,17 @@ LL | i[i[3]] = 4; | | immutable borrow occurs here | mutable borrow occurs here | mutable borrow later used here + | +help: try adding a local storing this... + --> $DIR/two-phase-nonrecv-autoref.rs:138:7 + | +LL | i[i[3]] = 4; + | ^^^^ +help: ...and then using that local here + --> $DIR/two-phase-nonrecv-autoref.rs:138:5 + | +LL | i[i[3]] = 4; + | ^^^^^^^ error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable --> $DIR/two-phase-nonrecv-autoref.rs:143:7 @@ -64,6 +75,17 @@ LL | i[i[3]] = i[4]; | | immutable borrow occurs here | mutable borrow occurs here | mutable borrow later used here + | +help: try adding a local storing this... + --> $DIR/two-phase-nonrecv-autoref.rs:143:7 + | +LL | i[i[3]] = i[4]; + | ^^^^ +help: ...and then using that local here + --> $DIR/two-phase-nonrecv-autoref.rs:143:5 + | +LL | i[i[3]] = i[4]; + | ^^^^^^^ error: aborting due to 7 previous errors From ca9ef27ed156d599f6feac0c772cc29d960526ee Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 9 Apr 2022 08:10:34 -0700 Subject: [PATCH 4/9] Check for git submodules in non-git source tree. --- src/bootstrap/bootstrap.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 0b6bdf47419..00dc7da275a 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1097,8 +1097,19 @@ class RustBuild(object): def update_submodules(self): """Update submodules""" - if (not os.path.exists(os.path.join(self.rust_root, ".git"))) or \ - self.get_toml('submodules') == "false": + has_git = os.path.exists(os.path.join(self.rust_root, ".git")) + # This just arbitrarily checks for cargo, but any workspace member in + # a submodule would work. + has_submodules = os.path.exists(os.path.join(self.rust_root, "src/tools/cargo/Cargo.toml")) + if not has_git and not has_submodules: + print("This is not a git repository, and the requisite git submodules were not found.") + print("If you downloaded the source from https://github.com/rust-lang/rust/releases,") + print("those sources will not work. Instead, consider downloading from the source") + print("releases linked at") + print("https://forge.rust-lang.org/infra/other-installation-methods.html#source-code") + print("or clone the repository at https://github.com/rust-lang/rust/.") + raise SystemExit(1) + if not has_git or self.get_toml('submodules') == "false": return default_encoding = sys.getdefaultencoding() From 1834c21f88cac45c3b4b8c1c50855b06668516a5 Mon Sep 17 00:00:00 2001 From: niluxv Date: Sat, 9 Apr 2022 17:39:07 +0200 Subject: [PATCH 5/9] Fix missing space in lossy provenance cast lint --- compiler/rustc_typeck/src/check/cast.rs | 2 +- src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index 6091b8fee00..e73b9c979eb 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -1012,7 +1012,7 @@ fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::I err.help(msg); } err.help( - "if you can't comply with strict provenance and need to expose the pointer\ + "if you can't comply with strict provenance and need to expose the pointer \ provenance you can use `.expose_addr()` instead" ); diff --git a/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr b/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr index 489cb03ddd3..e7a6c1837bd 100644 --- a/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr +++ b/src/test/ui/lint/lint-strict-provenance-lossy-casts.stderr @@ -9,7 +9,7 @@ note: the lint level is defined here | LL | #![deny(lossy_provenance_casts)] | ^^^^^^^^^^^^^^^^^^^^^^ - = help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `u32` --> $DIR/lint-strict-provenance-lossy-casts.rs:9:22 @@ -17,7 +17,7 @@ error: under strict provenance it is considered bad style to cast pointer `*cons LL | let addr_32bit = &x as *const u8 as u32; | ^^^^^^^^^^^^^^^^^^^^^^ help: use `.addr()` to obtain the address of a pointer: `(&x as *const u8).addr() as u32` | - = help: if you can't comply with strict provenance and need to expose the pointerprovenance you can use `.expose_addr()` instead + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_addr()` instead error: aborting due to 2 previous errors From cc57656969554bd0dcbf95641bebadc076fac61f Mon Sep 17 00:00:00 2001 From: ouz-a Date: Sat, 9 Apr 2022 20:38:06 +0300 Subject: [PATCH 6/9] support multiple derefs --- .../src/deref_separator.rs | 39 ++++--- .../derefer_test_multiple.main.Derefer.diff | 100 ++++++++++++++++++ src/test/mir-opt/derefer_test_multiple.rs | 9 ++ 3 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 src/test/mir-opt/derefer_test_multiple.main.Derefer.diff create mode 100644 src/test/mir-opt/derefer_test_multiple.rs diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index 79aac163550..d8660d4f2fd 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -11,6 +11,8 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { for (i, stmt) in data.statements.iter_mut().enumerate() { match stmt.kind { StatementKind::Assign(box (og_place, Rvalue::Ref(region, borrow_knd, place))) => { + let mut place_local = place.local; + let mut last_len = 0; for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() { if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() { // The type that we are derefing. @@ -23,14 +25,30 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { patch.add_statement(loc, StatementKind::StorageLive(temp)); // We are adding current p_ref's projections to our - // temp value. - let deref_place = - Place::from(p_ref.local).project_deeper(p_ref.projection, tcx); - patch.add_assign( - loc, - Place::from(temp), - Rvalue::Use(Operand::Move(deref_place)), - ); + // temp value, excluding projections we already covered. + if idx == 1 { + let deref_place = Place::from(place_local) + .project_deeper(&p_ref.projection[last_len..], tcx); + patch.add_assign( + loc, + Place::from(temp), + Rvalue::Use(Operand::Move(deref_place)), + ); + + place_local = temp; + last_len = p_ref.projection.len(); + } else { + let deref_place = Place::from(place_local) + .project_deeper(&p_ref.projection[last_len..], tcx); + patch.add_assign( + loc, + Place::from(temp), + Rvalue::Use(Operand::Move(deref_place)), + ); + + place_local = temp; + last_len = p_ref.projection.len(); + } // We are creating a place by using our temp value's location // and copying derefed values which we need to create new statement. @@ -50,11 +68,6 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // Since our job with the temp is done it should be gone let loc = Location { block: block, statement_index: i + 1 }; patch.add_statement(loc, StatementKind::StorageDead(temp)); - - // As all projections are off the base projection, if there are - // multiple derefs in the middle of projection, it might cause - // unsoundness, to not let that happen we break the loop. - break; } } } diff --git a/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff b/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff new file mode 100644 index 00000000000..d465724326e --- /dev/null +++ b/src/test/mir-opt/derefer_test_multiple.main.Derefer.diff @@ -0,0 +1,100 @@ +- // MIR for `main` before Derefer ++ // MIR for `main` after Derefer + + fn main() -> () { + let mut _0: (); // return place in scope 0 at $DIR/derefer_test_multiple.rs:2:12: 2:12 + let mut _1: (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:3:9: 3:14 + let mut _3: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:4:22: 4:28 + let mut _5: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:5:22: 5:28 + let mut _7: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:22: 6:28 ++ let mut _10: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ let mut _11: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ let mut _12: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ let mut _13: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ let mut _14: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ let mut _15: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30 + scope 1 { + debug a => _1; // in scope 1 at $DIR/derefer_test_multiple.rs:3:9: 3:14 + let mut _2: (i32, &mut (i32, i32)); // in scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14 + scope 2 { + debug b => _2; // in scope 2 at $DIR/derefer_test_multiple.rs:4:9: 4:14 + let mut _4: (i32, &mut (i32, &mut (i32, i32))); // in scope 2 at $DIR/derefer_test_multiple.rs:5:9: 5:14 + scope 3 { + debug c => _4; // in scope 3 at $DIR/derefer_test_multiple.rs:5:9: 5:14 + let mut _6: (i32, &mut (i32, &mut (i32, &mut (i32, i32)))); // in scope 3 at $DIR/derefer_test_multiple.rs:6:9: 6:14 + scope 4 { + debug d => _6; // in scope 4 at $DIR/derefer_test_multiple.rs:6:9: 6:14 + let _8: &mut i32; // in scope 4 at $DIR/derefer_test_multiple.rs:7:9: 7:10 + scope 5 { + debug x => _8; // in scope 5 at $DIR/derefer_test_multiple.rs:7:9: 7:10 + let _9: &mut i32; // in scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10 + scope 6 { + debug y => _9; // in scope 6 at $DIR/derefer_test_multiple.rs:8:9: 8:10 + } + } + } + } + } + } + + bb0: { + StorageLive(_1); // scope 0 at $DIR/derefer_test_multiple.rs:3:9: 3:14 + (_1.0: i32) = const 42_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25 + (_1.1: i32) = const 43_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25 + StorageLive(_2); // scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14 + StorageLive(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28 + _3 = &mut _1; // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28 + (_2.0: i32) = const 99_i32; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29 + (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29 + StorageDead(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:28: 4:29 + StorageLive(_4); // scope 2 at $DIR/derefer_test_multiple.rs:5:9: 5:14 + StorageLive(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28 + _5 = &mut _2; // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28 + (_4.0: i32) = const 11_i32; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29 + (_4.1: &mut (i32, &mut (i32, i32))) = move _5; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29 + StorageDead(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:28: 5:29 + StorageLive(_6); // scope 3 at $DIR/derefer_test_multiple.rs:6:9: 6:14 + StorageLive(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28 + _7 = &mut _4; // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28 + (_6.0: i32) = const 13_i32; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29 + (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))) = move _7; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29 + StorageDead(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:28: 6:29 + StorageLive(_8); // scope 4 at $DIR/derefer_test_multiple.rs:7:9: 7:10 +- _8 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ StorageLive(_10); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ _10 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ StorageLive(_11); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ _11 = move ((*_10).1: &mut (i32, &mut (i32, i32))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ StorageLive(_12); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ _12 = move ((*_11).1: &mut (i32, i32)); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ _8 = &mut ((*_12).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30 ++ StorageDead(_10); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10 ++ StorageDead(_11); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10 ++ StorageDead(_12); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10 + StorageLive(_9); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10 +- _9 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ StorageLive(_13); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ _13 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ StorageLive(_14); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ _14 = move ((*_13).1: &mut (i32, &mut (i32, i32))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ StorageLive(_15); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ _15 = move ((*_14).1: &mut (i32, i32)); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ _9 = &mut ((*_15).1: i32); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30 ++ StorageDead(_13); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2 ++ StorageDead(_14); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2 ++ StorageDead(_15); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2 + _0 = const (); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2 + StorageDead(_9); // scope 5 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + StorageDead(_8); // scope 4 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + StorageDead(_6); // scope 3 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + StorageDead(_4); // scope 2 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + StorageDead(_2); // scope 1 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + StorageDead(_1); // scope 0 at $DIR/derefer_test_multiple.rs:9:1: 9:2 + return; // scope 0 at $DIR/derefer_test_multiple.rs:9:2: 9:2 ++ } ++ ++ bb1 (cleanup): { ++ resume; // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2 + } + } + diff --git a/src/test/mir-opt/derefer_test_multiple.rs b/src/test/mir-opt/derefer_test_multiple.rs new file mode 100644 index 00000000000..a27363447fe --- /dev/null +++ b/src/test/mir-opt/derefer_test_multiple.rs @@ -0,0 +1,9 @@ +// EMIT_MIR derefer_test_multiple.main.Derefer.diff +fn main () { + let mut a = (42, 43); + let mut b = (99, &mut a); + let mut c = (11, &mut b); + let mut d = (13, &mut c); + let x = &mut (*d.1).1.1.1; + let y = &mut (*d.1).1.1.1; +} From 80afd9db2e3871387ecab9c2bdeffc0f970d32d3 Mon Sep 17 00:00:00 2001 From: ouz-a Date: Sat, 9 Apr 2022 22:23:49 +0300 Subject: [PATCH 7/9] remove the if block --- .../src/deref_separator.rs | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs index d8660d4f2fd..24b626ad966 100644 --- a/compiler/rustc_mir_transform/src/deref_separator.rs +++ b/compiler/rustc_mir_transform/src/deref_separator.rs @@ -26,29 +26,16 @@ pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { // We are adding current p_ref's projections to our // temp value, excluding projections we already covered. - if idx == 1 { - let deref_place = Place::from(place_local) - .project_deeper(&p_ref.projection[last_len..], tcx); - patch.add_assign( - loc, - Place::from(temp), - Rvalue::Use(Operand::Move(deref_place)), - ); + let deref_place = Place::from(place_local) + .project_deeper(&p_ref.projection[last_len..], tcx); + patch.add_assign( + loc, + Place::from(temp), + Rvalue::Use(Operand::Move(deref_place)), + ); - place_local = temp; - last_len = p_ref.projection.len(); - } else { - let deref_place = Place::from(place_local) - .project_deeper(&p_ref.projection[last_len..], tcx); - patch.add_assign( - loc, - Place::from(temp), - Rvalue::Use(Operand::Move(deref_place)), - ); - - place_local = temp; - last_len = p_ref.projection.len(); - } + place_local = temp; + last_len = p_ref.projection.len(); // We are creating a place by using our temp value's location // and copying derefed values which we need to create new statement. From 8e15b6cfdeeb36aae27518ce6446452392ebc021 Mon Sep 17 00:00:00 2001 From: Roc Yu Date: Sat, 9 Apr 2022 19:14:29 -0400 Subject: [PATCH 8/9] rustdoc: Reduce allocations in a `html::markdown` function --- src/librustdoc/html/markdown.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 3a3d61b1e67..943c521485b 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -251,7 +251,7 @@ fn next(&mut self) -> Option { } } let lines = origtext.lines().filter_map(|l| map_line(l).for_html()); - let text = lines.collect::>>().join("\n"); + let text = lines.intersperse("\n".into()).collect::(); let parse_result = match kind { CodeBlockKind::Fenced(ref lang) => { @@ -291,15 +291,13 @@ fn next(&mut self) -> Option { let test = origtext .lines() .map(|l| map_line(l).for_code()) - .collect::>>() - .join("\n"); + .intersperse("\n".into()) + .collect::(); let krate = krate.as_ref().map(|s| &**s); let (test, _, _) = doctest::make_test(&test, krate, false, &Default::default(), edition, None); let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" }; - let edition_string = format!("&edition={}", edition); - // These characters don't need to be escaped in a URI. // FIXME: use a library function for percent encoding. fn dont_escape(c: u8) -> bool { @@ -325,8 +323,8 @@ fn dont_escape(c: u8) -> bool { } } Some(format!( - r#"Run"#, - url, test_escaped, channel, edition_string + r#"Run"#, + url, test_escaped, channel, edition, )) }); From 8412d5dc5c4f284390f89d0352adae56dbc650e9 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 10 Apr 2022 16:45:09 +0400 Subject: [PATCH 9/9] --bless tests --- src/test/ui/error-codes/E0516.stderr | 5 +++++ src/test/ui/issues/issue-29184.stderr | 5 +++++ src/test/ui/typeof/type_mismatch.stderr | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/test/ui/error-codes/E0516.stderr b/src/test/ui/error-codes/E0516.stderr index 2e6de5053d5..5243b7caf22 100644 --- a/src/test/ui/error-codes/E0516.stderr +++ b/src/test/ui/error-codes/E0516.stderr @@ -3,6 +3,11 @@ error[E0516]: `typeof` is a reserved keyword but unimplemented | LL | let x: typeof(92) = 92; | ^^^^^^^^^^ reserved keyword + | +help: consider replacing `typeof(...)` with an actual type + | +LL | let x: i32 = 92; + | ~~~ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-29184.stderr b/src/test/ui/issues/issue-29184.stderr index 87d3632ee42..75b6c64f2ce 100644 --- a/src/test/ui/issues/issue-29184.stderr +++ b/src/test/ui/issues/issue-29184.stderr @@ -3,6 +3,11 @@ error[E0516]: `typeof` is a reserved keyword but unimplemented | LL | let x: typeof(92) = 92; | ^^^^^^^^^^ reserved keyword + | +help: consider replacing `typeof(...)` with an actual type + | +LL | let x: i32 = 92; + | ~~~ error: aborting due to previous error diff --git a/src/test/ui/typeof/type_mismatch.stderr b/src/test/ui/typeof/type_mismatch.stderr index e82b5e44973..e75214cd31a 100644 --- a/src/test/ui/typeof/type_mismatch.stderr +++ b/src/test/ui/typeof/type_mismatch.stderr @@ -3,6 +3,11 @@ error[E0516]: `typeof` is a reserved keyword but unimplemented | LL | let b: typeof(a) = 1i8; | ^^^^^^^^^ reserved keyword + | +help: consider replacing `typeof(...)` with an actual type + | +LL | let b: u8 = 1i8; + | ~~ error[E0308]: mismatched types --> $DIR/type_mismatch.rs:5:24