about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/util/parser.rs7
-rw-r--r--compiler/rustc_mir_build/src/builder/scope.rs20
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs12
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs55
-rw-r--r--library/alloc/src/collections/binary_heap/mod.rs3
-rw-r--r--library/core/src/cell/once.rs12
-rw-r--r--library/core/src/fmt/mod.rs11
-rw-r--r--library/core/src/iter/sources/once.rs3
-rw-r--r--library/core/src/iter/traits/iterator.rs6
-rw-r--r--library/core/src/option.rs8
-rw-r--r--library/core/src/ptr/const_ptr.rs9
-rw-r--r--library/core/src/result.rs11
-rw-r--r--library/core/src/slice/iter.rs67
-rw-r--r--library/core/src/str/converts.rs5
-rw-r--r--library/core/src/sync/atomic.rs4
-rw-r--r--library/std/src/io/mod.rs25
-rw-r--r--library/std/src/process.rs29
-rw-r--r--tests/ui-fulldeps/pprust-parenthesis-insertion.rs14
-rw-r--r--tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.rs29
-rw-r--r--tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr52
-rw-r--r--tests/ui/parser/recover/missing-dot-on-statement-expression.fixed28
-rw-r--r--tests/ui/parser/recover/missing-dot-on-statement-expression.rs28
-rw-r--r--tests/ui/parser/recover/missing-dot-on-statement-expression.stderr46
-rw-r--r--tests/ui/suggestions/type-ascription-and-other-error.stderr5
24 files changed, 379 insertions, 110 deletions
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
index 1d4b01aa94c..8f2b7a23c01 100644
--- a/compiler/rustc_ast/src/util/parser.rs
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -153,9 +153,10 @@ impl AssocOp {
         match *self {
             Assign | AssignOp(_) => Fixity::Right,
             As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
-            | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
-            | LAnd | LOr => Fixity::Left,
-            DotDot | DotDotEq => Fixity::None,
+            | BitXor | BitOr | LAnd | LOr => Fixity::Left,
+            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
+                Fixity::None
+            }
         }
     }
 
diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs
index fd9f9da6e77..35c98037827 100644
--- a/compiler/rustc_mir_build/src/builder/scope.rs
+++ b/compiler/rustc_mir_build/src/builder/scope.rs
@@ -1481,14 +1481,6 @@ fn build_scope_drops<'tcx>(
                 block = next;
             }
             DropKind::ForLint => {
-                // If the operand has been moved, and we are not on an unwind
-                // path, then don't generate the drop. (We only take this into
-                // account for non-unwind paths so as not to disturb the
-                // caching mechanism.)
-                if scope.moved_locals.iter().any(|&o| o == local) {
-                    continue;
-                }
-
                 // As in the `DropKind::Storage` case below:
                 // normally lint-related drops are not emitted for unwind,
                 // so we can just leave `unwind_to` unmodified, but in some
@@ -1500,6 +1492,14 @@ fn build_scope_drops<'tcx>(
                     unwind_to = unwind_drops.drops[unwind_to].next;
                 }
 
+                // If the operand has been moved, and we are not on an unwind
+                // path, then don't generate the drop. (We only take this into
+                // account for non-unwind paths so as not to disturb the
+                // caching mechanism.)
+                if scope.moved_locals.iter().any(|&o| o == local) {
+                    continue;
+                }
+
                 cfg.push(block, Statement {
                     source_info,
                     kind: StatementKind::BackwardIncompatibleDropHint {
@@ -1552,7 +1552,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
             let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1);
             for (drop_idx, drop_node) in drops.drops.iter_enumerated().skip(1) {
                 match drop_node.data.kind {
-                    DropKind::Storage => {
+                    DropKind::Storage | DropKind::ForLint => {
                         if is_coroutine {
                             let unwind_drop = self
                                 .scopes
@@ -1563,7 +1563,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
                             unwind_indices.push(unwind_indices[drop_node.next]);
                         }
                     }
-                    DropKind::Value | DropKind::ForLint => {
+                    DropKind::Value => {
                         let unwind_drop = self
                             .scopes
                             .unwind_drops
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 2f4adf2af9e..7533e75ffe2 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -279,13 +279,9 @@ impl<'a> Parser<'a> {
                 break;
             }
 
-            let fixity = op.fixity();
-            let min_prec = match fixity {
+            let min_prec = match op.fixity() {
                 Fixity::Right => Bound::Included(prec),
-                Fixity::Left => Bound::Excluded(prec),
-                // We currently have no non-associative operators that are not handled above by
-                // the special cases. The code is here only for future convenience.
-                Fixity::None => Bound::Excluded(prec),
+                Fixity::Left | Fixity::None => Bound::Excluded(prec),
             };
             let (rhs, _) = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
                 let attrs = this.parse_outer_attributes()?;
@@ -337,10 +333,6 @@ impl<'a> Parser<'a> {
                     self.dcx().span_bug(span, "AssocOp should have been handled by special case")
                 }
             };
-
-            if let Fixity::None = fixity {
-                break;
-            }
         }
 
         Ok((lhs, parsed_something))
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 151abf0be95..1ddb5fc0a11 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -745,6 +745,51 @@ impl<'a> Parser<'a> {
         Ok(self.mk_block(stmts, s, lo.to(self.prev_token.span)))
     }
 
+    fn recover_missing_dot(&mut self, err: &mut Diag<'_>) {
+        let Some((ident, _)) = self.token.ident() else {
+            return;
+        };
+        if let Some(c) = ident.name.as_str().chars().next()
+            && c.is_uppercase()
+        {
+            return;
+        }
+        if self.token.is_reserved_ident() && !self.token.is_ident_named(kw::Await) {
+            return;
+        }
+        if self.prev_token.is_reserved_ident() && self.prev_token.is_ident_named(kw::Await) {
+            // Likely `foo.await bar`
+        } else if !self.prev_token.is_reserved_ident() && self.prev_token.is_ident() {
+            // Likely `foo bar`
+        } else if self.prev_token.kind == token::Question {
+            // `foo? bar`
+        } else if self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis) {
+            // `foo() bar`
+        } else {
+            return;
+        }
+        if self.token.span == self.prev_token.span {
+            // Account for syntax errors in proc-macros.
+            return;
+        }
+        if self.look_ahead(1, |t| [token::Semi, token::Question, token::Dot].contains(&t.kind)) {
+            err.span_suggestion_verbose(
+                self.prev_token.span.between(self.token.span),
+                "you might have meant to write a field access",
+                ".".to_string(),
+                Applicability::MaybeIncorrect,
+            );
+        }
+        if self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis)) {
+            err.span_suggestion_verbose(
+                self.prev_token.span.between(self.token.span),
+                "you might have meant to write a method call",
+                ".".to_string(),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+
     /// Parses a statement, including the trailing semicolon.
     pub fn parse_full_stmt(
         &mut self,
@@ -851,7 +896,8 @@ impl<'a> Parser<'a> {
                             Some(if recover.no() {
                                 res?
                             } else {
-                                res.unwrap_or_else(|e| {
+                                res.unwrap_or_else(|mut e| {
+                                    self.recover_missing_dot(&mut e);
                                     let guar = e.emit();
                                     self.recover_stmt();
                                     guar
@@ -872,7 +918,12 @@ impl<'a> Parser<'a> {
                 // We might be at the `,` in `let x = foo<bar, baz>;`. Try to recover.
                 match &mut local.kind {
                     LocalKind::Init(expr) | LocalKind::InitElse(expr, _) => {
-                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr)?;
+                        self.check_mistyped_turbofish_with_multiple_type_params(e, expr).map_err(
+                            |mut e| {
+                                self.recover_missing_dot(&mut e);
+                                e
+                            },
+                        )?;
                         // We found `foo<bar, baz>`, have we fully recovered?
                         self.expect_semi()?;
                     }
diff --git a/library/alloc/src/collections/binary_heap/mod.rs b/library/alloc/src/collections/binary_heap/mod.rs
index 116e0e73e96..965fd63a529 100644
--- a/library/alloc/src/collections/binary_heap/mod.rs
+++ b/library/alloc/src/collections/binary_heap/mod.rs
@@ -531,8 +531,7 @@ impl<T: Ord, A: Allocator> BinaryHeap<T, A> {
     /// heap.push(1);
     /// heap.push(5);
     /// heap.push(2);
-    /// {
-    ///     let mut val = heap.peek_mut().unwrap();
+    /// if let Some(mut val) = heap.peek_mut() {
     ///     *val = 0;
     /// }
     /// assert_eq!(heap.peek(), Some(&2));
diff --git a/library/core/src/cell/once.rs b/library/core/src/cell/once.rs
index c14afe0f476..6a85791916a 100644
--- a/library/core/src/cell/once.rs
+++ b/library/core/src/cell/once.rs
@@ -262,7 +262,9 @@ impl<T> OnceCell<T> {
     ///
     /// let value = cell.get_mut_or_try_init(|| "1234".parse());
     /// assert_eq!(value, Ok(&mut 1234));
-    /// *value.unwrap() += 2;
+    ///
+    /// let Ok(value) = value else { return; };
+    /// *value += 2;
     /// assert_eq!(cell.get(), Some(&1236))
     /// ```
     #[unstable(feature = "once_cell_get_mut", issue = "121641")]
@@ -304,8 +306,8 @@ impl<T> OnceCell<T> {
     /// assert_eq!(cell.into_inner(), None);
     ///
     /// let cell = OnceCell::new();
-    /// cell.set("hello".to_string()).unwrap();
-    /// assert_eq!(cell.into_inner(), Some("hello".to_string()));
+    /// let _ = cell.set("hello".to_owned());
+    /// assert_eq!(cell.into_inner(), Some("hello".to_owned()));
     /// ```
     #[inline]
     #[stable(feature = "once_cell", since = "1.70.0")]
@@ -332,8 +334,8 @@ impl<T> OnceCell<T> {
     /// assert_eq!(cell.take(), None);
     ///
     /// let mut cell = OnceCell::new();
-    /// cell.set("hello".to_string()).unwrap();
-    /// assert_eq!(cell.take(), Some("hello".to_string()));
+    /// let _ = cell.set("hello".to_owned());
+    /// assert_eq!(cell.take(), Some("hello".to_owned()));
     /// assert_eq!(cell.get(), None);
     /// ```
     #[inline]
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 14c70065101..c2c78dd9c67 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -152,8 +152,9 @@ pub trait Write {
     /// }
     ///
     /// let mut buf = String::new();
-    /// writer(&mut buf, "hola").unwrap();
+    /// writer(&mut buf, "hola")?;
     /// assert_eq!(&buf, "hola");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn write_str(&mut self, s: &str) -> Result;
@@ -179,9 +180,10 @@ pub trait Write {
     /// }
     ///
     /// let mut buf = String::new();
-    /// writer(&mut buf, 'a').unwrap();
-    /// writer(&mut buf, 'b').unwrap();
+    /// writer(&mut buf, 'a')?;
+    /// writer(&mut buf, 'b')?;
     /// assert_eq!(&buf, "ab");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "fmt_write_char", since = "1.1.0")]
     fn write_char(&mut self, c: char) -> Result {
@@ -208,8 +210,9 @@ pub trait Write {
     /// }
     ///
     /// let mut buf = String::new();
-    /// writer(&mut buf, "world").unwrap();
+    /// writer(&mut buf, "world")?;
     /// assert_eq!(&buf, "world");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn write_fmt(&mut self, args: Arguments<'_>) -> Result {
diff --git a/library/core/src/iter/sources/once.rs b/library/core/src/iter/sources/once.rs
index 21be4377da1..c4a9860bdd7 100644
--- a/library/core/src/iter/sources/once.rs
+++ b/library/core/src/iter/sources/once.rs
@@ -34,7 +34,7 @@ use crate::iter::{FusedIterator, TrustedLen};
 /// use std::fs;
 /// use std::path::PathBuf;
 ///
-/// let dirs = fs::read_dir(".foo").unwrap();
+/// let dirs = fs::read_dir(".foo")?;
 ///
 /// // we need to convert from an iterator of DirEntry-s to an iterator of
 /// // PathBufs, so we use map
@@ -50,6 +50,7 @@ use crate::iter::{FusedIterator, TrustedLen};
 /// for f in files {
 ///     println!("{f:?}");
 /// }
+/// # std::io::Result::Ok(())
 /// ```
 #[stable(feature = "iter_once", since = "1.2.0")]
 pub fn once<T>(value: T) -> Once<T> {
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 38dfbbef393..ff39e8ac25f 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -2564,7 +2564,7 @@ pub trait Iterator {
     /// # Example
     ///
     /// ```
-    /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap();
+    /// let reduced: i32 = (1..10).reduce(|acc, e| acc + e).unwrap_or(0);
     /// assert_eq!(reduced, 45);
     ///
     /// // Which is equivalent to doing it with `fold`:
@@ -3087,7 +3087,7 @@ pub trait Iterator {
     ///     [2.4, f32::NAN, 1.3]
     ///         .into_iter()
     ///         .reduce(f32::max)
-    ///         .unwrap(),
+    ///         .unwrap_or(0.),
     ///     2.4
     /// );
     /// ```
@@ -3123,7 +3123,7 @@ pub trait Iterator {
     ///     [2.4, f32::NAN, 1.3]
     ///         .into_iter()
     ///         .reduce(f32::min)
-    ///         .unwrap(),
+    ///         .unwrap_or(0.),
     ///     1.3
     /// );
     /// ```
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index f4ac7af6396..a9f06b92ad5 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -937,10 +937,16 @@ impl<T> Option<T> {
     /// Returns the contained [`Some`] value, consuming the `self` value.
     ///
     /// Because this function may panic, its use is generally discouraged.
+    /// Panics are meant for unrecoverable errors, and
+    /// [may abort the entire program][panic-abort].
+    ///
     /// Instead, prefer to use pattern matching and handle the [`None`]
     /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or
-    /// [`unwrap_or_default`].
+    /// [`unwrap_or_default`]. In functions returning `Option`, you can use
+    /// [the `?` (try) operator][try-option].
     ///
+    /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html
+    /// [try-option]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#where-the--operator-can-be-used
     /// [`unwrap_or`]: Option::unwrap_or
     /// [`unwrap_or_else`]: Option::unwrap_or_else
     /// [`unwrap_or_default`]: Option::unwrap_or_default
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index f100adecbbb..ac905eceaa8 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -502,11 +502,12 @@ impl<T: ?Sized> *const T {
     /// let mut out = String::new();
     /// while ptr != end_rounded_up {
     ///     unsafe {
-    ///         write!(&mut out, "{}, ", *ptr).unwrap();
+    ///         write!(&mut out, "{}, ", *ptr)?;
     ///     }
     ///     ptr = ptr.wrapping_offset(step);
     /// }
     /// assert_eq!(out.as_str(), "1, 3, 5, ");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "ptr_wrapping_offset", since = "1.16.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
@@ -1125,11 +1126,12 @@ impl<T: ?Sized> *const T {
     /// let mut out = String::new();
     /// while ptr != end_rounded_up {
     ///     unsafe {
-    ///         write!(&mut out, "{}, ", *ptr).unwrap();
+    ///         write!(&mut out, "{}, ", *ptr)?;
     ///     }
     ///     ptr = ptr.wrapping_add(step);
     /// }
     /// assert_eq!(out, "1, 3, 5, ");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
@@ -1203,11 +1205,12 @@ impl<T: ?Sized> *const T {
     /// let mut out = String::new();
     /// while ptr != start_rounded_down {
     ///     unsafe {
-    ///         write!(&mut out, "{}, ", *ptr).unwrap();
+    ///         write!(&mut out, "{}, ", *ptr)?;
     ///     }
     ///     ptr = ptr.wrapping_sub(step);
     /// }
     /// assert_eq!(out, "5, 3, 1, ");
+    /// # std::fmt::Result::Ok(())
     /// ```
     #[stable(feature = "pointer_methods", since = "1.26.0")]
     #[must_use = "returns a new pointer rather than modifying its argument"]
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 9c7be618bc7..92b5cba1531 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -1065,10 +1065,15 @@ impl<T, E> Result<T, E> {
     /// Returns the contained [`Ok`] value, consuming the `self` value.
     ///
     /// Because this function may panic, its use is generally discouraged.
-    /// Instead, prefer to use pattern matching and handle the [`Err`]
-    /// case explicitly, or call [`unwrap_or`], [`unwrap_or_else`], or
-    /// [`unwrap_or_default`].
+    /// Panics are meant for unrecoverable errors, and
+    /// [may abort the entire program][panic-abort].
+    ///
+    /// Instead, prefer to use [the `?` (try) operator][try-operator], or pattern matching
+    /// to handle the [`Err`] case explicitly, or call [`unwrap_or`],
+    /// [`unwrap_or_else`], or [`unwrap_or_default`].
     ///
+    /// [panic-abort]: https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html
+    /// [try-operator]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
     /// [`unwrap_or`]: Result::unwrap_or
     /// [`unwrap_or_else`]: Result::unwrap_or_else
     /// [`unwrap_or_default`]: Result::unwrap_or_default
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index a27baf9db22..d2842f69008 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -46,13 +46,19 @@ impl<'a, T> IntoIterator for &'a mut [T] {
 /// Basic usage:
 ///
 /// ```
-/// // First, we declare a type which has `iter` method to get the `Iter` struct (`&[usize]` here):
+/// // First, we need a slice to call the `iter` method on:
 /// let slice = &[1, 2, 3];
 ///
-/// // Then, we iterate over it:
+/// // Then we call `iter` on the slice to get the `Iter` struct,
+/// // and iterate over it:
 /// for element in slice.iter() {
 ///     println!("{element}");
 /// }
+///
+/// // This for loop actually already works without calling `iter`:
+/// for element in slice {
+///     println!("{element}");
+/// }
 /// ```
 ///
 /// [`iter`]: slice::iter
@@ -109,19 +115,25 @@ impl<'a, T> Iter<'a, T> {
     /// Basic usage:
     ///
     /// ```
-    /// // First, we declare a type which has the `iter` method to get the `Iter`
+    /// // First, we need a slice to call the `iter` method on:
     /// // struct (`&[usize]` here):
     /// let slice = &[1, 2, 3];
     ///
-    /// // Then, we get the iterator:
+    /// // Then we call `iter` on the slice to get the `Iter` struct:
     /// let mut iter = slice.iter();
-    /// // So if we print what `as_slice` method returns here, we have "[1, 2, 3]":
+    /// // Here `as_slice` still returns the whole slice, so this prints "[1, 2, 3]":
     /// println!("{:?}", iter.as_slice());
     ///
-    /// // Next, we move to the second element of the slice:
+    /// // Now, we call the `next` method to remove the first element of the iterator:
     /// iter.next();
-    /// // Now `as_slice` returns "[2, 3]":
+    /// // Here the iterator does not contain the first element of the slice any more,
+    /// // so `as_slice` only returns the last two elements of the slice,
+    /// // and so this prints "[2, 3]":
     /// println!("{:?}", iter.as_slice());
+    ///
+    /// // The underlying slice has not been modified and still contains three elements,
+    /// // so this prints "[1, 2, 3]":
+    /// println!("{:?}", slice);
     /// ```
     #[must_use]
     #[stable(feature = "iter_to_slice", since = "1.4.0")]
@@ -166,11 +178,11 @@ impl<T> AsRef<[T]> for Iter<'_, T> {
 /// Basic usage:
 ///
 /// ```
-/// // First, we declare a type which has `iter_mut` method to get the `IterMut`
-/// // struct (`&[usize]` here):
-/// let mut slice = &mut [1, 2, 3];
+/// // First, we need a slice to call the `iter_mut` method on:
+/// let slice = &mut [1, 2, 3];
 ///
-/// // Then, we iterate over it and increment each element value:
+/// // Then we call `iter_mut` on the slice to get the `IterMut` struct,
+/// // iterate over it and increment each element value:
 /// for element in slice.iter_mut() {
 ///     *element += 1;
 /// }
@@ -247,28 +259,21 @@ impl<'a, T> IterMut<'a, T> {
     /// Basic usage:
     ///
     /// ```
-    /// // First, we declare a type which has `iter_mut` method to get the `IterMut`
-    /// // struct (`&[usize]` here):
+    /// // First, we need a slice to call the `iter_mut` method on:
     /// let mut slice = &mut [1, 2, 3];
     ///
-    /// {
-    ///     // Then, we get the iterator:
-    ///     let mut iter = slice.iter_mut();
-    ///     // We move to next element:
-    ///     iter.next();
-    ///     // So if we print what `into_slice` method returns here, we have "[2, 3]":
-    ///     println!("{:?}", iter.into_slice());
-    /// }
-    ///
-    /// // Now let's modify a value of the slice:
-    /// {
-    ///     // First we get back the iterator:
-    ///     let mut iter = slice.iter_mut();
-    ///     // We change the value of the first element of the slice returned by the `next` method:
-    ///     *iter.next().unwrap() += 1;
-    /// }
-    /// // Now slice is "[2, 2, 3]":
-    /// println!("{slice:?}");
+    /// // Then we call `iter_mut` on the slice to get the `IterMut` struct:
+    /// let mut iter = slice.iter_mut();
+    /// // Now, we call the `next` method to remove the first element of the iterator,
+    /// // unwrap and dereference what we get from `next` and increase its value by 1:
+    /// *iter.next().unwrap() += 1;
+    /// // Here the iterator does not contain the first element of the slice any more,
+    /// // so `into_slice` only returns the last two elements of the slice,
+    /// // and so this prints "[2, 3]":
+    /// println!("{:?}", iter.into_slice());
+    /// // The underlying slice still contains three elements, but its first element
+    /// // was increased by 1, so this prints "[2, 2, 3]":
+    /// println!("{:?}", slice);
     /// ```
     #[must_use = "`self` will be dropped if the result is not used"]
     #[stable(feature = "iter_to_slice", since = "1.4.0")]
diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs
index c7bae42765f..de68f80aa0c 100644
--- a/library/core/src/str/converts.rs
+++ b/library/core/src/str/converts.rs
@@ -47,10 +47,11 @@ use crate::{mem, ptr};
 /// // some bytes, in a vector
 /// let sparkle_heart = vec![240, 159, 146, 150];
 ///
-/// // We know these bytes are valid, so just use `unwrap()`.
-/// let sparkle_heart = str::from_utf8(&sparkle_heart).unwrap();
+/// // We can use the ? (try) operator to check if the bytes are valid
+/// let sparkle_heart = str::from_utf8(&sparkle_heart)?;
 ///
 /// assert_eq!("💖", sparkle_heart);
+/// # Ok::<_, str::Utf8Error>(())
 /// ```
 ///
 /// Incorrect bytes:
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index 487fffba881..fda26a67299 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -86,7 +86,7 @@
 //!     // This is fine: `join` synchronizes the code in a way such that the atomic
 //!     // store happens-before the non-atomic write.
 //!     let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed)); // atomic store
-//!     handle.join().unwrap(); // synchronize
+//!     handle.join().expect("thread won't panic"); // synchronize
 //!     s.spawn(|| unsafe { atomic.as_ptr().write(2) }); // non-atomic write
 //! });
 //!
@@ -103,7 +103,7 @@
 //!     // This is fine: `join` synchronizes the code in a way such that
 //!     // the 1-byte store happens-before the 2-byte store.
 //!     let handle = s.spawn(|| atomic.store(1, Ordering::Relaxed));
-//!     handle.join().unwrap();
+//!     handle.join().expect("thread won't panic");
 //!     s.spawn(|| unsafe {
 //!         let differently_sized = transmute::<&AtomicU16, &AtomicU8>(&atomic);
 //!         differently_sized.store(2, Ordering::Relaxed);
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 4ffb0463006..7912f969bbd 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -1083,7 +1083,7 @@ pub trait Read {
     ///     let f = BufReader::new(File::open("foo.txt")?);
     ///
     ///     for byte in f.bytes() {
-    ///         println!("{}", byte.unwrap());
+    ///         println!("{}", byte?);
     ///     }
     ///     Ok(())
     /// }
@@ -1995,15 +1995,16 @@ pub trait Seek {
     ///     .write(true)
     ///     .read(true)
     ///     .create(true)
-    ///     .open("foo.txt").unwrap();
+    ///     .open("foo.txt")?;
     ///
     /// let hello = "Hello!\n";
-    /// write!(f, "{hello}").unwrap();
-    /// f.rewind().unwrap();
+    /// write!(f, "{hello}")?;
+    /// f.rewind()?;
     ///
     /// let mut buf = String::new();
-    /// f.read_to_string(&mut buf).unwrap();
+    /// f.read_to_string(&mut buf)?;
     /// assert_eq!(&buf, hello);
+    /// # std::io::Result::Ok(())
     /// ```
     #[stable(feature = "seek_rewind", since = "1.55.0")]
     fn rewind(&mut self) -> Result<()> {
@@ -2212,8 +2213,9 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
 ///
 /// let stdin = io::stdin();
 /// for line in stdin.lock().lines() {
-///     println!("{}", line.unwrap());
+///     println!("{}", line?);
 /// }
+/// # std::io::Result::Ok(())
 /// ```
 ///
 /// If you have something that implements [`Read`], you can use the [`BufReader`
@@ -2236,7 +2238,8 @@ fn skip_until<R: BufRead + ?Sized>(r: &mut R, delim: u8) -> Result<usize> {
 ///     let f = BufReader::new(f);
 ///
 ///     for line in f.lines() {
-///         println!("{}", line.unwrap());
+///         let line = line?;
+///         println!("{line}");
 ///     }
 ///
 ///     Ok(())
@@ -2274,7 +2277,7 @@ pub trait BufRead: Read {
     /// let stdin = io::stdin();
     /// let mut stdin = stdin.lock();
     ///
-    /// let buffer = stdin.fill_buf().unwrap();
+    /// let buffer = stdin.fill_buf()?;
     ///
     /// // work with buffer
     /// println!("{buffer:?}");
@@ -2282,6 +2285,7 @@ pub trait BufRead: Read {
     /// // ensure the bytes we worked with aren't returned again later
     /// let length = buffer.len();
     /// stdin.consume(length);
+    /// # std::io::Result::Ok(())
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fill_buf(&mut self) -> Result<&[u8]>;
@@ -2327,12 +2331,13 @@ pub trait BufRead: Read {
     /// let stdin = io::stdin();
     /// let mut stdin = stdin.lock();
     ///
-    /// while stdin.has_data_left().unwrap() {
+    /// while stdin.has_data_left()? {
     ///     let mut line = String::new();
-    ///     stdin.read_line(&mut line).unwrap();
+    ///     stdin.read_line(&mut line)?;
     ///     // work with line
     ///     println!("{line:?}");
     /// }
+    /// # std::io::Result::Ok(())
     /// ```
     #[unstable(feature = "buf_read_has_data_left", reason = "recently added", issue = "86423")]
     fn has_data_left(&mut self) -> Result<bool> {
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 6933528cdbd..929d2b57afe 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -224,7 +224,7 @@ pub struct Child {
     /// has been captured. You might find it helpful to do
     ///
     /// ```ignore (incomplete)
-    /// let stdin = child.stdin.take().unwrap();
+    /// let stdin = child.stdin.take().expect("handle present");
     /// ```
     ///
     /// to avoid partially moving the `child` and thus blocking yourself from calling
@@ -236,7 +236,7 @@ pub struct Child {
     /// has been captured. You might find it helpful to do
     ///
     /// ```ignore (incomplete)
-    /// let stdout = child.stdout.take().unwrap();
+    /// let stdout = child.stdout.take().expect("handle present");
     /// ```
     ///
     /// to avoid partially moving the `child` and thus blocking yourself from calling
@@ -248,7 +248,7 @@ pub struct Child {
     /// has been captured. You might find it helpful to do
     ///
     /// ```ignore (incomplete)
-    /// let stderr = child.stderr.take().unwrap();
+    /// let stderr = child.stderr.take().expect("handle present");
     /// ```
     ///
     /// to avoid partially moving the `child` and thus blocking yourself from calling
@@ -1052,14 +1052,14 @@ impl Command {
     /// use std::io::{self, Write};
     /// let output = Command::new("/bin/cat")
     ///     .arg("file.txt")
-    ///     .output()
-    ///     .expect("failed to execute process");
+    ///     .output()?;
     ///
     /// println!("status: {}", output.status);
-    /// io::stdout().write_all(&output.stdout).unwrap();
-    /// io::stderr().write_all(&output.stderr).unwrap();
+    /// io::stdout().write_all(&output.stdout)?;
+    /// io::stderr().write_all(&output.stderr)?;
     ///
     /// assert!(output.status.success());
+    /// # io::Result::Ok(())
     /// ```
     #[stable(feature = "process", since = "1.0.0")]
     pub fn output(&mut self) -> io::Result<Output> {
@@ -1391,11 +1391,11 @@ impl Stdio {
     /// let output = Command::new("rev")
     ///     .stdin(Stdio::inherit())
     ///     .stdout(Stdio::piped())
-    ///     .output()
-    ///     .expect("Failed to execute command");
+    ///     .output()?;
     ///
     /// print!("You piped in the reverse of: ");
-    /// io::stdout().write_all(&output.stdout).unwrap();
+    /// io::stdout().write_all(&output.stdout)?;
+    /// # io::Result::Ok(())
     /// ```
     #[must_use]
     #[stable(feature = "process", since = "1.0.0")]
@@ -1575,14 +1575,14 @@ impl From<fs::File> for Stdio {
     /// use std::process::Command;
     ///
     /// // With the `foo.txt` file containing "Hello, world!"
-    /// let file = File::open("foo.txt").unwrap();
+    /// let file = File::open("foo.txt")?;
     ///
     /// let reverse = Command::new("rev")
     ///     .stdin(file)  // Implicit File conversion into a Stdio
-    ///     .output()
-    ///     .expect("failed reverse command");
+    ///     .output()?;
     ///
     /// assert_eq!(reverse.stdout, b"!dlrow ,olleH");
+    /// # std::io::Result::Ok(())
     /// ```
     fn from(file: fs::File) -> Stdio {
         Stdio::from_inner(file.into_inner().into())
@@ -2179,7 +2179,7 @@ impl Child {
     /// ```no_run
     /// use std::process::Command;
     ///
-    /// let mut child = Command::new("ls").spawn().unwrap();
+    /// let mut child = Command::new("ls").spawn()?;
     ///
     /// match child.try_wait() {
     ///     Ok(Some(status)) => println!("exited with: {status}"),
@@ -2190,6 +2190,7 @@ impl Child {
     ///     }
     ///     Err(e) => println!("error attempting to wait: {e}"),
     /// }
+    /// # std::io::Result::Ok(())
     /// ```
     #[stable(feature = "process_try_wait", since = "1.18.0")]
     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
diff --git a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
index 184458bad55..258a1fdb1d3 100644
--- a/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
+++ b/tests/ui-fulldeps/pprust-parenthesis-insertion.rs
@@ -61,6 +61,9 @@ static EXPRS: &[&str] = &[
     "(2 + 2) * 2",
     "2 * (2 + 2)",
     "2 + 2 + 2",
+    // Right-associative operator.
+    "2 += 2 += 2",
+    "(2 += 2) += 2",
     // Return has lower precedence than a binary operator.
     "(return 2) + 2",
     "2 + (return 2)", // FIXME: no parenthesis needed.
@@ -89,6 +92,13 @@ static EXPRS: &[&str] = &[
     // allowed, except if the break is also labeled.
     "break 'outer 'inner: loop {} + 2",
     "break ('inner: loop {} + 2)",
+    // Grammar restriction: ranges cannot be the endpoint of another range.
+    "(2..2)..2",
+    "2..(2..2)",
+    "(2..2)..",
+    "..(2..2)",
+    // Grammar restriction: comparison operators cannot be chained (1 < 2 == false).
+    "((1 < 2) == false) as usize",
     // Grammar restriction: the value in let-else is not allowed to end in a
     // curly brace.
     "{ let _ = 1 + 1 else {}; }",
@@ -113,10 +123,6 @@ static EXPRS: &[&str] = &[
     "if let _ = () && (Struct {}).x {}",
     */
     /*
-    // FIXME: pretty-printer produces invalid syntax. `(1 < 2 == false) as usize`
-    "((1 < 2) == false) as usize",
-    */
-    /*
     // FIXME: pretty-printer produces invalid syntax. `for _ in 1..{ 2 } {}`
     "for _ in (1..{ 2 }) {}",
     */
diff --git a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.rs b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.rs
new file mode 100644
index 00000000000..8d85cee19fd
--- /dev/null
+++ b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.rs
@@ -0,0 +1,29 @@
+//@ edition: 2021
+//@ build-fail
+
+// Make sure we don't ICE when emitting the "lint" drop statement
+// used for tail_expr_drop_order.
+
+#![deny(tail_expr_drop_order)]
+
+struct Drop;
+impl std::ops::Drop for Drop {
+    fn drop(&mut self) {}
+}
+
+async fn func() -> Result<(), Drop> {
+    todo!()
+}
+
+async fn retry_db() -> Result<(), Drop> {
+    loop {
+        match func().await {
+            //~^ ERROR relative drop order changing in Rust 2024
+            //~| WARNING this changes meaning in Rust 2024
+            Ok(()) => return Ok(()),
+            Err(e) => {}
+        }
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr
new file mode 100644
index 00000000000..d04abebe144
--- /dev/null
+++ b/tests/ui/drop/tail_expr_drop_order-on-coroutine-unwind.stderr
@@ -0,0 +1,52 @@
+error: relative drop order changing in Rust 2024
+  --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:20:15
+   |
+LL |         match func().await {
+   |               ^^^^^^^-----
+   |               |      |
+   |               |      this value will be stored in a temporary; let us call it `#1`
+   |               |      `#1` will be dropped later as of Edition 2024
+   |               this value will be stored in a temporary; let us call it `#2`
+   |               up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024
+...
+LL |             Err(e) => {}
+   |                 -
+   |                 |
+   |                 `e` calls a custom destructor
+   |                 `e` will be dropped later as of Edition 2024
+LL |         }
+LL |     }
+   |     - now the temporary value is dropped here, before the local variables in the block or statement
+   |
+   = warning: this changes meaning in Rust 2024
+   = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-tail-expr-scope.html>
+note: `#2` invokes this custom destructor
+  --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:10:1
+   |
+LL | / impl std::ops::Drop for Drop {
+LL | |     fn drop(&mut self) {}
+LL | | }
+   | |_^
+note: `#1` invokes this custom destructor
+  --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:10:1
+   |
+LL | / impl std::ops::Drop for Drop {
+LL | |     fn drop(&mut self) {}
+LL | | }
+   | |_^
+note: `e` invokes this custom destructor
+  --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:10:1
+   |
+LL | / impl std::ops::Drop for Drop {
+LL | |     fn drop(&mut self) {}
+LL | | }
+   | |_^
+   = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages
+note: the lint level is defined here
+  --> $DIR/tail_expr_drop_order-on-coroutine-unwind.rs:7:9
+   |
+LL | #![deny(tail_expr_drop_order)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/recover/missing-dot-on-statement-expression.fixed b/tests/ui/parser/recover/missing-dot-on-statement-expression.fixed
new file mode 100644
index 00000000000..1be4485b474
--- /dev/null
+++ b/tests/ui/parser/recover/missing-dot-on-statement-expression.fixed
@@ -0,0 +1,28 @@
+//@ run-rustfix
+#![allow(unused_must_use, dead_code)]
+struct S {
+    field: (),
+}
+fn main() {
+    let _ = [1, 2, 3].iter().map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
+    //~^ HELP you might have meant to write a method call
+}
+fn foo() {
+    let baz = S {
+        field: ()
+    };
+    let _ = baz.field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
+    //~^ HELP you might have meant to write a field
+}
+
+fn bar() {
+    [1, 2, 3].iter().map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
+    //~^ HELP you might have meant to write a method call
+}
+fn baz() {
+    let baz = S {
+        field: ()
+    };
+    baz.field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
+    //~^ HELP you might have meant to write a field
+}
diff --git a/tests/ui/parser/recover/missing-dot-on-statement-expression.rs b/tests/ui/parser/recover/missing-dot-on-statement-expression.rs
new file mode 100644
index 00000000000..5e2b545f414
--- /dev/null
+++ b/tests/ui/parser/recover/missing-dot-on-statement-expression.rs
@@ -0,0 +1,28 @@
+//@ run-rustfix
+#![allow(unused_must_use, dead_code)]
+struct S {
+    field: (),
+}
+fn main() {
+    let _ = [1, 2, 3].iter()map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
+    //~^ HELP you might have meant to write a method call
+}
+fn foo() {
+    let baz = S {
+        field: ()
+    };
+    let _ = baz field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
+    //~^ HELP you might have meant to write a field
+}
+
+fn bar() {
+    [1, 2, 3].iter()map(|x| x); //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
+    //~^ HELP you might have meant to write a method call
+}
+fn baz() {
+    let baz = S {
+        field: ()
+    };
+    baz field; //~ ERROR expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
+    //~^ HELP you might have meant to write a field
+}
diff --git a/tests/ui/parser/recover/missing-dot-on-statement-expression.stderr b/tests/ui/parser/recover/missing-dot-on-statement-expression.stderr
new file mode 100644
index 00000000000..a04d8bd34e2
--- /dev/null
+++ b/tests/ui/parser/recover/missing-dot-on-statement-expression.stderr
@@ -0,0 +1,46 @@
+error: expected one of `.`, `;`, `?`, `else`, or an operator, found `map`
+  --> $DIR/missing-dot-on-statement-expression.rs:7:29
+   |
+LL |     let _ = [1, 2, 3].iter()map(|x| x);
+   |                             ^^^ expected one of `.`, `;`, `?`, `else`, or an operator
+   |
+help: you might have meant to write a method call
+   |
+LL |     let _ = [1, 2, 3].iter().map(|x| x);
+   |                             +
+
+error: expected one of `!`, `.`, `::`, `;`, `?`, `else`, `{`, or an operator, found `field`
+  --> $DIR/missing-dot-on-statement-expression.rs:14:17
+   |
+LL |     let _ = baz field;
+   |                 ^^^^^ expected one of 8 possible tokens
+   |
+help: you might have meant to write a field access
+   |
+LL |     let _ = baz.field;
+   |                +
+
+error: expected one of `.`, `;`, `?`, `}`, or an operator, found `map`
+  --> $DIR/missing-dot-on-statement-expression.rs:19:21
+   |
+LL |     [1, 2, 3].iter()map(|x| x);
+   |                     ^^^ expected one of `.`, `;`, `?`, `}`, or an operator
+   |
+help: you might have meant to write a method call
+   |
+LL |     [1, 2, 3].iter().map(|x| x);
+   |                     +
+
+error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found `field`
+  --> $DIR/missing-dot-on-statement-expression.rs:26:9
+   |
+LL |     baz field;
+   |         ^^^^^ expected one of 8 possible tokens
+   |
+help: you might have meant to write a field access
+   |
+LL |     baz.field;
+   |        +
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/suggestions/type-ascription-and-other-error.stderr b/tests/ui/suggestions/type-ascription-and-other-error.stderr
index 4efddca4b47..7f8b8b7470e 100644
--- a/tests/ui/suggestions/type-ascription-and-other-error.stderr
+++ b/tests/ui/suggestions/type-ascription-and-other-error.stderr
@@ -3,6 +3,11 @@ error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, `}`, or an operator, found
    |
 LL |     not rust;
    |         ^^^^ expected one of 8 possible tokens
+   |
+help: you might have meant to write a field access
+   |
+LL |     not.rust;
+   |        +
 
 error: aborting due to 1 previous error