about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs1
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs5
-rw-r--r--compiler/rustc_errors/src/emitter.rs10
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_expand/locales/en-US.ftl3
-rw-r--r--compiler/rustc_expand/src/errors.rs7
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs3
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs7
-rw-r--r--compiler/rustc_middle/src/ty/normalize_erasing_regions.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs6
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs116
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs22
-rw-r--r--compiler/rustc_parse/src/parser/item.rs24
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs17
-rw-r--r--compiler/rustc_session/src/session.rs10
-rw-r--r--config.toml.example1
-rw-r--r--library/alloc/src/string.rs4
-rw-r--r--library/core/src/str/traits.rs4
-rw-r--r--library/std/src/os/unix/fs.rs30
-rw-r--r--library/std/src/os/unix/fs/tests.rs57
-rw-r--r--library/std/src/sys/unix/fd.rs184
-rw-r--r--library/std/src/sys/unix/fs.rs8
-rwxr-xr-xsrc/bootstrap/configure.py22
-rw-r--r--src/librustdoc/html/render/search_index.rs34
-rw-r--r--src/librustdoc/html/static/js/search.js14
-rw-r--r--tests/rustdoc-js-std/option-type-signatures.js7
-rw-r--r--tests/rustdoc-js-std/parser-errors.js10
-rw-r--r--tests/rustdoc-js/where-clause.js19
-rw-r--r--tests/rustdoc-js/where-clause.rs16
-rw-r--r--tests/ui/parser/issue-107705.rs3
-rw-r--r--tests/ui/parser/issue-107705.stderr10
-rw-r--r--tests/ui/suggestions/multiline-multipart-suggestion.rs19
-rw-r--r--tests/ui/suggestions/multiline-multipart-suggestion.stderr46
34 files changed, 508 insertions, 218 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 9ad945359b6..a29cdade023 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -444,6 +444,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                             "aborted execution: attempted to leave type `{}` uninitialized, which is invalid",
                             ty
                         ),
+                        ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
                     };
 
                     M::abort(self, msg)?;
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index dcd15b919f4..23fcd22c52b 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -30,7 +30,7 @@ pub fn check_validity_requirement<'tcx>(
         return Ok(!layout.abi.is_uninhabited());
     }
 
-    if tcx.sess.opts.unstable_opts.strict_init_checks {
+    if kind == ValidityRequirement::Uninit || tcx.sess.opts.unstable_opts.strict_init_checks {
         might_permit_raw_init_strict(layout, tcx, kind)
     } else {
         let layout_cx = LayoutCx { tcx, param_env: param_env_and_ty.param_env };
@@ -99,6 +99,9 @@ fn might_permit_raw_init_lax<'tcx>(
                 }
                 s.valid_range(cx).contains(val)
             }
+            ValidityRequirement::Uninit => {
+                bug!("ValidityRequirement::Uninit should have been handled above")
+            }
         }
     };
 
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 211bbf4f50e..1b2e7b7e083 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1895,7 +1895,7 @@ impl EmitterWriter {
                         self.draw_code_line(
                             &mut buffer,
                             &mut row_num,
-                            &Vec::new(),
+                            &[],
                             p + line_start,
                             l,
                             show_code_change,
@@ -1919,7 +1919,7 @@ impl EmitterWriter {
                             self.draw_code_line(
                                 &mut buffer,
                                 &mut row_num,
-                                &Vec::new(),
+                                &[],
                                 p + line_start,
                                 l,
                                 show_code_change,
@@ -1936,7 +1936,7 @@ impl EmitterWriter {
                             self.draw_code_line(
                                 &mut buffer,
                                 &mut row_num,
-                                &Vec::new(),
+                                &[],
                                 p + line_start,
                                 l,
                                 show_code_change,
@@ -1951,7 +1951,7 @@ impl EmitterWriter {
                 self.draw_code_line(
                     &mut buffer,
                     &mut row_num,
-                    highlight_parts,
+                    &highlight_parts,
                     line_pos + line_start,
                     line,
                     show_code_change,
@@ -2176,7 +2176,7 @@ impl EmitterWriter {
         &self,
         buffer: &mut StyledBuffer,
         row_num: &mut usize,
-        highlight_parts: &Vec<SubstitutionHighlight>,
+        highlight_parts: &[SubstitutionHighlight],
         line_num: usize,
         line_to_add: &str,
         show_code_change: DisplaySuggestion,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index cbf595089cc..99af872f07f 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -331,7 +331,7 @@ impl CodeSuggestion {
                     });
                     buf.push_str(&part.snippet);
                     let cur_hi = sm.lookup_char_pos(part.span.hi());
-                    if prev_hi.line == cur_lo.line && cur_hi.line == cur_lo.line {
+                    if cur_hi.line == cur_lo.line {
                         // Account for the difference between the width of the current code and the
                         // snippet being suggested, so that the *later* suggestions are correctly
                         // aligned on the screen.
diff --git a/compiler/rustc_expand/locales/en-US.ftl b/compiler/rustc_expand/locales/en-US.ftl
index b475d285f6b..cfae781bdee 100644
--- a/compiler/rustc_expand/locales/en-US.ftl
+++ b/compiler/rustc_expand/locales/en-US.ftl
@@ -133,3 +133,6 @@ expand_trace_macro = trace_macro
 expand_proc_macro_panicked =
     proc macro panicked
     .help = message: {$message}
+
+expand_proc_macro_derive_tokens =
+    proc-macro derive produced unparseable tokens
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index 70ab222b484..e5102a952e7 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -390,3 +390,10 @@ pub(crate) struct ProcMacroPanicked {
 pub(crate) struct ProcMacroPanickedHelp {
     pub message: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(expand_proc_macro_derive_tokens)]
+pub struct ProcMacroDeriveTokens {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index cef64a10479..ddba1441719 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -176,7 +176,7 @@ impl MultiItemModifier for DeriveProcMacro {
 
         // fail if there have been errors emitted
         if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
-            ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
+            ecx.sess.emit_err(errors::ProcMacroDeriveTokens { span });
         }
 
         ExpandResult::Ready(items)
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index e9e121f9c9b..d9af2fd74ce 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -794,8 +794,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
-        let cname = self.crate_name(LOCAL_CRATE);
-        self.sess.consider_optimizing(cname.as_str(), msg)
+        self.sess.consider_optimizing(|| self.crate_name(LOCAL_CRATE), msg)
     }
 
     /// Obtain all lang items of this crate and all dependencies (recursively)
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 9a517d2d2b4..254ffc33c96 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -170,13 +170,17 @@ pub const FAT_PTR_EXTRA: usize = 1;
 /// * Cranelift stores the base-2 log of the lane count in a 4 bit integer.
 pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
 
-/// Used in `might_permit_raw_init` to indicate the kind of initialisation
+/// Used in `check_validity_requirement` to indicate the kind of initialization
 /// that is checked to be valid
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
 pub enum ValidityRequirement {
     Inhabited,
     Zero,
+    /// The return value of mem::uninitialized, 0x01
+    /// (unless -Zstrict-init-checks is on, in which case it's the same as Uninit).
     UninitMitigated0x01Fill,
+    /// True uninitialized memory.
+    Uninit,
 }
 
 impl ValidityRequirement {
@@ -196,6 +200,7 @@ impl fmt::Display for ValidityRequirement {
             Self::Inhabited => f.write_str("is inhabited"),
             Self::Zero => f.write_str("allows being left zeroed"),
             Self::UninitMitigated0x01Fill => f.write_str("allows being filled with 0x01"),
+            Self::Uninit => f.write_str("allows being left uninitialized"),
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
index 57c8f3075b0..578cd82aa4c 100644
--- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
+++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
@@ -7,7 +7,6 @@
 //! `normalize_generic_arg_after_erasing_regions` query for each type
 //! or constant found within. (This underlying query is what is cached.)
 
-use crate::mir;
 use crate::traits::query::NoSolution;
 use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder};
 use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeVisitableExt};
@@ -16,7 +15,6 @@ use crate::ty::{self, EarlyBinder, SubstsRef, Ty, TyCtxt, TypeVisitableExt};
 pub enum NormalizationError<'tcx> {
     Type(Ty<'tcx>),
     Const(ty::Const<'tcx>),
-    ConstantKind(mir::ConstantKind<'tcx>),
 }
 
 impl<'tcx> NormalizationError<'tcx> {
@@ -24,7 +22,6 @@ impl<'tcx> NormalizationError<'tcx> {
         match self {
             NormalizationError::Type(t) => format!("{}", t),
             NormalizationError::Const(c) => format!("{}", c),
-            NormalizationError::ConstantKind(ck) => format!("{}", ck),
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index a937ec24741..9086412c09a 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -34,8 +34,6 @@ impl<'tcx> Cx<'tcx> {
 
     #[instrument(level = "trace", skip(self, hir_expr))]
     pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
-        let temp_lifetime =
-            self.rvalue_scopes.temporary_scope(self.region_scope_tree, hir_expr.hir_id.local_id);
         let expr_scope =
             region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
 
@@ -68,7 +66,7 @@ impl<'tcx> Cx<'tcx> {
 
         // Next, wrap this up in the expr's scope.
         expr = Expr {
-            temp_lifetime,
+            temp_lifetime: expr.temp_lifetime,
             ty: expr.ty,
             span: hir_expr.span,
             kind: ExprKind::Scope {
@@ -83,7 +81,7 @@ impl<'tcx> Cx<'tcx> {
             self.region_scope_tree.opt_destruction_scope(hir_expr.hir_id.local_id)
         {
             expr = Expr {
-                temp_lifetime,
+                temp_lifetime: expr.temp_lifetime,
                 ty: expr.ty,
                 span: hir_expr.span,
                 kind: ExprKind::Scope {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index a051dbe9ff5..0a65c37ea7b 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -19,7 +19,6 @@ use crate::errors::{
 };
 
 use crate::fluent_generated as fluent;
-use crate::lexer::UnmatchedDelim;
 use crate::parser;
 use rustc_ast as ast;
 use rustc_ast::ptr::P;
@@ -220,7 +219,6 @@ impl MultiSugg {
 /// is dropped.
 pub struct SnapshotParser<'a> {
     parser: Parser<'a>,
-    unclosed_delims: Vec<UnmatchedDelim>,
 }
 
 impl<'a> Deref for SnapshotParser<'a> {
@@ -255,27 +253,15 @@ impl<'a> Parser<'a> {
         &self.sess.span_diagnostic
     }
 
-    /// Replace `self` with `snapshot.parser` and extend `unclosed_delims` with `snapshot.unclosed_delims`.
-    /// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears.
+    /// Replace `self` with `snapshot.parser`.
     pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) {
         *self = snapshot.parser;
-        self.unclosed_delims.extend(snapshot.unclosed_delims);
-    }
-
-    pub fn unclosed_delims(&self) -> &[UnmatchedDelim] {
-        &self.unclosed_delims
     }
 
     /// Create a snapshot of the `Parser`.
     pub fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> {
-        let mut snapshot = self.clone();
-        let unclosed_delims = self.unclosed_delims.clone();
-        // Clear `unclosed_delims` in snapshot to avoid
-        // duplicate errors being emitted when the `Parser`
-        // is dropped (which may or may not happen, depending
-        // if the parsing the snapshot is created for is successful)
-        snapshot.unclosed_delims.clear();
-        SnapshotParser { parser: snapshot, unclosed_delims }
+        let snapshot = self.clone();
+        SnapshotParser { parser: snapshot }
     }
 
     pub(super) fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
@@ -579,21 +565,6 @@ impl<'a> Parser<'a> {
         } else {
             label_sp
         };
-        match self.recover_closing_delimiter(
-            &expected
-                .iter()
-                .filter_map(|tt| match tt {
-                    TokenType::Token(t) => Some(t.clone()),
-                    _ => None,
-                })
-                .collect::<Vec<_>>(),
-            err,
-        ) {
-            Err(e) => err = e,
-            Ok(recovered) => {
-                return Ok(recovered);
-            }
-        }
 
         if self.check_too_many_raw_str_terminators(&mut err) {
             if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
@@ -1573,12 +1544,6 @@ impl<'a> Parser<'a> {
         );
         let mut err = self.struct_span_err(sp, &msg);
         let label_exp = format!("expected `{token_str}`");
-        match self.recover_closing_delimiter(&[t.clone()], err) {
-            Err(e) => err = e,
-            Ok(recovered) => {
-                return Ok(recovered);
-            }
-        }
         let sm = self.sess.source_map();
         if !sm.is_multiline(prev_sp.until(sp)) {
             // When the spans are in the same line, it means that the only content
@@ -1795,81 +1760,6 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub(super) fn recover_closing_delimiter(
-        &mut self,
-        tokens: &[TokenKind],
-        mut err: DiagnosticBuilder<'a, ErrorGuaranteed>,
-    ) -> PResult<'a, bool> {
-        let mut pos = None;
-        // We want to use the last closing delim that would apply.
-        for (i, unmatched) in self.unclosed_delims.iter().enumerate().rev() {
-            if tokens.contains(&token::CloseDelim(unmatched.expected_delim))
-                && Some(self.token.span) > unmatched.unclosed_span
-            {
-                pos = Some(i);
-            }
-        }
-        match pos {
-            Some(pos) => {
-                // Recover and assume that the detected unclosed delimiter was meant for
-                // this location. Emit the diagnostic and act as if the delimiter was
-                // present for the parser's sake.
-
-                // Don't attempt to recover from this unclosed delimiter more than once.
-                let unmatched = self.unclosed_delims.remove(pos);
-                let delim = TokenType::Token(token::CloseDelim(unmatched.expected_delim));
-                if unmatched.found_delim.is_none() {
-                    // We encountered `Eof`, set this fact here to avoid complaining about missing
-                    // `fn main()` when we found place to suggest the closing brace.
-                    *self.sess.reached_eof.borrow_mut() = true;
-                }
-
-                // We want to suggest the inclusion of the closing delimiter where it makes
-                // the most sense, which is immediately after the last token:
-                //
-                //  {foo(bar {}}
-                //      ^      ^
-                //      |      |
-                //      |      help: `)` may belong here
-                //      |
-                //      unclosed delimiter
-                if let Some(sp) = unmatched.unclosed_span {
-                    let mut primary_span: Vec<Span> =
-                        err.span.primary_spans().iter().cloned().collect();
-                    primary_span.push(sp);
-                    let mut primary_span: MultiSpan = primary_span.into();
-                    for span_label in err.span.span_labels() {
-                        if let Some(label) = span_label.label {
-                            primary_span.push_span_label(span_label.span, label);
-                        }
-                    }
-                    err.set_span(primary_span);
-                    err.span_label(sp, "unclosed delimiter");
-                }
-                // Backticks should be removed to apply suggestions.
-                let mut delim = delim.to_string();
-                delim.retain(|c| c != '`');
-                err.span_suggestion_short(
-                    self.prev_token.span.shrink_to_hi(),
-                    &format!("`{delim}` may belong here"),
-                    delim,
-                    Applicability::MaybeIncorrect,
-                );
-                if unmatched.found_delim.is_none() {
-                    // Encountered `Eof` when lexing blocks. Do not recover here to avoid knockdown
-                    // errors which would be emitted elsewhere in the parser and let other error
-                    // recovery consume the rest of the file.
-                    Err(err)
-                } else {
-                    err.emit();
-                    self.expected_tokens.clear(); // Reduce the number of errors.
-                    Ok(true)
-                }
-            }
-            _ => Err(err),
-        }
-    }
-
     /// Eats tokens until we can be relatively sure we reached the end of the
     /// statement. This is something of a best-effort heuristic.
     ///
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index b2951e7a184..e00eda47c66 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1210,8 +1210,13 @@ impl<'a> Parser<'a> {
                         // `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.
                         self.restore_snapshot(snapshot);
                         let close_paren = self.prev_token.span;
-                        let span = lo.to(self.prev_token.span);
-                        if !fields.is_empty() {
+                        let span = lo.to(close_paren);
+                        if !fields.is_empty() &&
+                            // `token.kind` should not be compared here.
+                            // This is because the `snapshot.token.kind` is treated as the same as
+                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different.
+                            self.span_to_snippet(close_paren).map_or(false, |snippet| snippet == ")")
+                        {
                             let mut replacement_err = errors::ParenthesesWithStructFields {
                                 span,
                                 r#type: path,
@@ -1389,19 +1394,6 @@ impl<'a> Parser<'a> {
             self.parse_expr_let()
         } else if self.eat_keyword(kw::Underscore) {
             Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore))
-        } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
-            // Don't complain about bare semicolons after unclosed braces
-            // recovery in order to keep the error count down. Fixing the
-            // delimiters will possibly also fix the bare semicolon found in
-            // expression context. For example, silence the following error:
-            //
-            //     error: expected expression, found `;`
-            //      --> file.rs:2:13
-            //       |
-            //     2 |     foo(bar(;
-            //       |             ^ expected expression
-            self.bump();
-            Ok(self.mk_expr_err(self.token.span))
         } else if self.token.uninterpolated_span().rust_2018() {
             // `Span::rust_2018()` is somewhat expensive; don't get it repeatedly.
             if self.check_keyword(kw::Async) {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 9d9ae154ad4..85cc8ca02a9 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -125,16 +125,13 @@ impl<'a> Parser<'a> {
             return Ok(Some(item.into_inner()));
         };
 
-        let mut unclosed_delims = vec![];
         let item =
             self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| {
                 let item =
                     this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode);
-                unclosed_delims.append(&mut this.unclosed_delims);
                 Ok((item?, TrailingToken::None))
             })?;
 
-        self.unclosed_delims.append(&mut unclosed_delims);
         Ok(item)
     }
 
@@ -1960,21 +1957,12 @@ impl<'a> Parser<'a> {
         // FIXME: This will make us not emit the help even for declarative
         // macros within the same crate (that we can fix), which is sad.
         if !span.from_expansion() {
-            if self.unclosed_delims.is_empty() {
-                let DelimSpan { open, close } = args.dspan;
-                err.multipart_suggestion(
-                    "change the delimiters to curly braces",
-                    vec![(open, "{".to_string()), (close, '}'.to_string())],
-                    Applicability::MaybeIncorrect,
-                );
-            } else {
-                err.span_suggestion(
-                    span,
-                    "change the delimiters to curly braces",
-                    " { /* items */ }",
-                    Applicability::HasPlaceholders,
-                );
-            }
+            let DelimSpan { open, close } = args.dspan;
+            err.multipart_suggestion(
+                "change the delimiters to curly braces",
+                vec![(open, "{".to_string()), (close, '}'.to_string())],
+                Applicability::MaybeIncorrect,
+            );
             err.span_suggestion(
                 span.shrink_to_hi(),
                 "add a semicolon",
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index da82e4724d1..6e9b447fa61 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -146,10 +146,7 @@ pub struct Parser<'a> {
     /// See the comments in the `parse_path_segment` function for more details.
     unmatched_angle_bracket_count: u32,
     max_angle_bracket_count: u32,
-    /// A list of all unclosed delimiters found by the lexer. If an entry is used for error recovery
-    /// it gets removed from here. Every entry left at the end gets emitted as an independent
-    /// error.
-    pub(super) unclosed_delims: Vec<UnmatchedDelim>,
+
     last_unexpected_token_span: Option<Span>,
     /// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
     /// looked like it could have been a mistyped path or literal `Option:Some(42)`).
@@ -168,7 +165,7 @@ pub struct Parser<'a> {
 // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
 // it doesn't unintentionally get bigger.
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Parser<'_>, 312);
+rustc_data_structures::static_assert_size!(Parser<'_>, 288);
 
 /// Stores span information about a closure.
 #[derive(Clone)]
@@ -215,12 +212,6 @@ struct CaptureState {
     inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>,
 }
 
-impl<'a> Drop for Parser<'a> {
-    fn drop(&mut self) {
-        emit_unclosed_delims(&mut self.unclosed_delims, &self.sess);
-    }
-}
-
 /// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
 /// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
 /// use this type to emit them as a linear sequence. But a linear sequence is
@@ -478,7 +469,6 @@ impl<'a> Parser<'a> {
             desugar_doc_comments,
             unmatched_angle_bracket_count: 0,
             max_angle_bracket_count: 0,
-            unclosed_delims: Vec::new(),
             last_unexpected_token_span: None,
             last_type_ascription: None,
             subparser_name,
@@ -859,7 +849,6 @@ impl<'a> Parser<'a> {
         let mut recovered = false;
         let mut trailing = false;
         let mut v = ThinVec::new();
-        let unclosed_delims = !self.unclosed_delims.is_empty();
 
         while !self.expect_any_with_type(kets, expect) {
             if let token::CloseDelim(..) | token::Eof = self.token.kind {
@@ -901,7 +890,7 @@ impl<'a> Parser<'a> {
                                 _ => {
                                     // Attempt to keep parsing if it was a similar separator.
                                     if let Some(tokens) = t.similar_tokens() {
-                                        if tokens.contains(&self.token.kind) && !unclosed_delims {
+                                        if tokens.contains(&self.token.kind) {
                                             self.bump();
                                         }
                                     }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 446ba63ed1c..12634f67185 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -882,10 +882,14 @@ impl Session {
 
     /// We want to know if we're allowed to do an optimization for crate foo from -z fuel=foo=n.
     /// This expends fuel if applicable, and records fuel if applicable.
-    pub fn consider_optimizing<T: Fn() -> String>(&self, crate_name: &str, msg: T) -> bool {
+    pub fn consider_optimizing(
+        &self,
+        get_crate_name: impl Fn() -> Symbol,
+        msg: impl Fn() -> String,
+    ) -> bool {
         let mut ret = true;
         if let Some((ref c, _)) = self.opts.unstable_opts.fuel {
-            if c == crate_name {
+            if c == get_crate_name().as_str() {
                 assert_eq!(self.threads(), 1);
                 let mut fuel = self.optimization_fuel.lock();
                 ret = fuel.remaining != 0;
@@ -903,7 +907,7 @@ impl Session {
             }
         }
         if let Some(ref c) = self.opts.unstable_opts.print_fuel {
-            if c == crate_name {
+            if c == get_crate_name().as_str() {
                 assert_eq!(self.threads(), 1);
                 self.print_fuel.fetch_add(1, SeqCst);
             }
diff --git a/config.toml.example b/config.toml.example
index 69eb228a2d5..3aaeb09d4c0 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -164,6 +164,7 @@ changelog-seen = 2
 # General build configuration options
 # =============================================================================
 [build]
+
 # The default stage to use for the `check` subcommand
 #check-stage = 0
 
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 2b843647dd5..c7e7ed3e95e 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -2213,10 +2213,6 @@ impl PartialEq for String {
     fn eq(&self, other: &String) -> bool {
         PartialEq::eq(&self[..], &other[..])
     }
-    #[inline]
-    fn ne(&self, other: &String) -> bool {
-        PartialEq::ne(&self[..], &other[..])
-    }
 }
 
 macro_rules! impl_eq {
diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs
index d3ed811b157..68f62ce8be5 100644
--- a/library/core/src/str/traits.rs
+++ b/library/core/src/str/traits.rs
@@ -28,10 +28,6 @@ impl PartialEq for str {
     fn eq(&self, other: &str) -> bool {
         self.as_bytes() == other.as_bytes()
     }
-    #[inline]
-    fn ne(&self, other: &str) -> bool {
-        !(*self).eq(other)
-    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs
index 3fc6cc44ce4..a0e664acd13 100644
--- a/library/std/src/os/unix/fs.rs
+++ b/library/std/src/os/unix/fs.rs
@@ -17,6 +17,10 @@ use crate::sealed::Sealed;
 #[allow(unused_imports)]
 use io::{Read, Write};
 
+// Tests for this module
+#[cfg(test)]
+mod tests;
+
 /// Unix-specific extensions to [`fs::File`].
 #[stable(feature = "file_offset", since = "1.15.0")]
 pub trait FileExt {
@@ -54,6 +58,16 @@ pub trait FileExt {
     #[stable(feature = "file_offset", since = "1.15.0")]
     fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
 
+    /// Like `read_at`, except that it reads into a slice of buffers.
+    ///
+    /// Data is copied to fill each buffer in order, with the final buffer
+    /// written to possibly being only partially filled. This method must behave
+    /// equivalently to a single call to read with concatenated buffers.
+    #[unstable(feature = "unix_file_vectored_at", issue = "89517")]
+    fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        io::default_read_vectored(|b| self.read_at(b, offset), bufs)
+    }
+
     /// Reads the exact number of byte required to fill `buf` from the given offset.
     ///
     /// The offset is relative to the start of the file and thus independent
@@ -155,6 +169,16 @@ pub trait FileExt {
     #[stable(feature = "file_offset", since = "1.15.0")]
     fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
 
+    /// Like `write_at`, except that it writes from a slice of buffers.
+    ///
+    /// Data is copied from each buffer in order, with the final buffer read
+    /// from possibly being only partially consumed. This method must behave as
+    /// a call to `write_at` with the buffers concatenated would.
+    #[unstable(feature = "unix_file_vectored_at", issue = "89517")]
+    fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        io::default_write_vectored(|b| self.write_at(b, offset), bufs)
+    }
+
     /// Attempts to write an entire buffer starting from a given offset.
     ///
     /// The offset is relative to the start of the file and thus independent
@@ -218,9 +242,15 @@ impl FileExt for fs::File {
     fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
         self.as_inner().read_at(buf, offset)
     }
+    fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        self.as_inner().read_vectored_at(bufs, offset)
+    }
     fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
         self.as_inner().write_at(buf, offset)
     }
+    fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        self.as_inner().write_vectored_at(bufs, offset)
+    }
 }
 
 /// Unix-specific extensions to [`fs::Permissions`].
diff --git a/library/std/src/os/unix/fs/tests.rs b/library/std/src/os/unix/fs/tests.rs
new file mode 100644
index 00000000000..67f607bd468
--- /dev/null
+++ b/library/std/src/os/unix/fs/tests.rs
@@ -0,0 +1,57 @@
+use super::*;
+
+#[test]
+fn read_vectored_at() {
+    let msg = b"preadv is working!";
+    let dir = crate::sys_common::io::test::tmpdir();
+
+    let filename = dir.join("preadv.txt");
+    {
+        let mut file = fs::File::create(&filename).unwrap();
+        file.write_all(msg).unwrap();
+    }
+    {
+        let file = fs::File::open(&filename).unwrap();
+        let mut buf0 = [0; 4];
+        let mut buf1 = [0; 3];
+
+        let mut iovec = [io::IoSliceMut::new(&mut buf0), io::IoSliceMut::new(&mut buf1)];
+
+        let n = file.read_vectored_at(&mut iovec, 4).unwrap();
+
+        assert!(n == 4 || n == 7);
+        assert_eq!(&buf0, b"dv i");
+
+        if n == 7 {
+            assert_eq!(&buf1, b"s w");
+        }
+    }
+}
+
+#[test]
+fn write_vectored_at() {
+    let msg = b"pwritev is not working!";
+    let dir = crate::sys_common::io::test::tmpdir();
+
+    let filename = dir.join("preadv.txt");
+    {
+        let mut file = fs::File::create(&filename).unwrap();
+        file.write_all(msg).unwrap();
+    }
+    let expected = {
+        let file = fs::File::options().write(true).open(&filename).unwrap();
+        let buf0 = b"    ";
+        let buf1 = b"great  ";
+
+        let iovec = [io::IoSlice::new(buf0), io::IoSlice::new(buf1)];
+
+        let n = file.write_vectored_at(&iovec, 11).unwrap();
+
+        assert!(n == 4 || n == 11);
+
+        if n == 4 { b"pwritev is     working!" } else { b"pwritev is     great  !" }
+    };
+
+    let content = fs::read(&filename).unwrap();
+    assert_eq!(&content, expected);
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 53523ca8c1d..9874af4d3e2 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -98,7 +98,7 @@ impl FileDesc {
         let ret = cvt(unsafe {
             libc::readv(
                 self.as_raw_fd(),
-                bufs.as_ptr() as *const libc::iovec,
+                bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
                 cmp::min(bufs.len(), max_iov()) as libc::c_int,
             )
         })?;
@@ -107,7 +107,7 @@ impl FileDesc {
 
     #[cfg(any(target_os = "espidf", target_os = "horizon"))]
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        return crate::io::default_read_vectored(|b| self.read(b), bufs);
+        io::default_read_vectored(|b| self.read(b), bufs)
     }
 
     #[inline]
@@ -153,6 +153,95 @@ impl FileDesc {
         Ok(())
     }
 
+    #[cfg(any(
+        target_os = "emscripten",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "linux",
+        target_os = "netbsd",
+    ))]
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            libc::preadv(
+                self.as_raw_fd(),
+                bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
+                cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                offset as _,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    #[cfg(not(any(
+        target_os = "android",
+        target_os = "emscripten",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "ios",
+        target_os = "linux",
+        target_os = "macos",
+        target_os = "netbsd",
+    )))]
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        io::default_read_vectored(|b| self.read_at(b, offset), bufs)
+    }
+
+    // We support some old Android versions that do not have `preadv` in libc,
+    // so we use weak linkage and fallback to a direct syscall if not available.
+    //
+    // On 32-bit targets, we don't want to deal with weird ABI issues around
+    // passing 64-bits parameters to syscalls, so we fallback to the default
+    // implementation if `preadv` is not available.
+    #[cfg(all(target_os = "android", target_pointer_width = "64"))]
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::syscall! {
+            fn preadv(
+                fd: libc::c_int,
+                iovec: *const libc::iovec,
+                n_iovec: libc::c_int,
+                offset: off64_t
+            ) -> isize
+        }
+
+        let ret = cvt(unsafe {
+            preadv(
+                self.as_raw_fd(),
+                bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
+                cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                offset as _,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    // We support old MacOS and iOS versions that do not have `preadv`. There is
+    // no `syscall` possible in these platform.
+    #[cfg(any(
+        all(target_os = "android", target_pointer_width = "32"),
+        target_os = "ios",
+        target_os = "macos",
+    ))]
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
+
+        match preadv64.get() {
+            Some(preadv) => {
+                let ret = cvt(unsafe {
+                    preadv(
+                        self.as_raw_fd(),
+                        bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
+                        cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                        offset as _,
+                    )
+                })?;
+                Ok(ret as usize)
+            }
+            None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
+        }
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::write(
@@ -178,7 +267,7 @@ impl FileDesc {
 
     #[cfg(any(target_os = "espidf", target_os = "horizon"))]
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        return crate::io::default_write_vectored(|b| self.write(b), bufs);
+        io::default_write_vectored(|b| self.write(b), bufs)
     }
 
     #[inline]
@@ -203,6 +292,95 @@ impl FileDesc {
         }
     }
 
+    #[cfg(any(
+        target_os = "emscripten",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "linux",
+        target_os = "netbsd",
+    ))]
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        let ret = cvt(unsafe {
+            libc::pwritev(
+                self.as_raw_fd(),
+                bufs.as_ptr() as *const libc::iovec,
+                cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                offset as _,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    #[cfg(not(any(
+        target_os = "android",
+        target_os = "emscripten",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "illumos",
+        target_os = "ios",
+        target_os = "linux",
+        target_os = "macos",
+        target_os = "netbsd",
+    )))]
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        io::default_write_vectored(|b| self.write_at(b, offset), bufs)
+    }
+
+    // We support some old Android versions that do not have `pwritev` in libc,
+    // so we use weak linkage and fallback to a direct syscall if not available.
+    //
+    // On 32-bit targets, we don't want to deal with weird ABI issues around
+    // passing 64-bits parameters to syscalls, so we fallback to the default
+    // implementation if `pwritev` is not available.
+    #[cfg(all(target_os = "android", target_pointer_width = "64"))]
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::syscall! {
+            fn pwritev(
+                fd: libc::c_int,
+                iovec: *const libc::iovec,
+                n_iovec: libc::c_int,
+                offset: off64_t
+            ) -> isize
+        }
+
+        let ret = cvt(unsafe {
+            pwritev(
+                self.as_raw_fd(),
+                bufs.as_ptr() as *const libc::iovec,
+                cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                offset as _,
+            )
+        })?;
+        Ok(ret as usize)
+    }
+
+    // We support old MacOS and iOS versions that do not have `pwritev`. There is
+    // no `syscall` possible in these platform.
+    #[cfg(any(
+        all(target_os = "android", target_pointer_width = "32"),
+        target_os = "ios",
+        target_os = "macos",
+    ))]
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
+
+        match pwritev64.get() {
+            Some(pwritev) => {
+                let ret = cvt(unsafe {
+                    pwritev(
+                        self.as_raw_fd(),
+                        bufs.as_ptr() as *const libc::iovec,
+                        cmp::min(bufs.len(), max_iov()) as libc::c_int,
+                        offset as _,
+                    )
+                })?;
+                Ok(ret as usize)
+            }
+            None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
+        }
+    }
+
     #[cfg(not(any(
         target_env = "newlib",
         target_os = "solaris",
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index bdccb784674..7566fafda24 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -1132,6 +1132,10 @@ impl File {
         self.0.read_buf(cursor)
     }
 
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        self.0.read_vectored_at(bufs, offset)
+    }
+
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         self.0.write(buf)
     }
@@ -1149,6 +1153,10 @@ impl File {
         self.0.write_at(buf, offset)
     }
 
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        self.0.write_vectored_at(bufs, offset)
+    }
+
     pub fn flush(&self) -> io::Result<()> {
         Ok(())
     }
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index ab3d0829296..c0d382d8a50 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -488,6 +488,22 @@ for section_key, section_config in config.items():
     else:
         configure_section(sections[section_key], section_config)
 
+def write_uncommented(target, f):
+    block = []
+    is_comment = True
+
+    for line in target:
+        block.append(line)
+        if len(line) == 0:
+            if not is_comment:
+                for l in block:
+                    f.write(l + "\n")
+            block = []
+            is_comment = True
+            continue
+        is_comment = is_comment and line.startswith('#')
+    return f
+
 # Now that we've built up our `config.toml`, write it all out in the same
 # order that we read it in.
 p("")
@@ -496,11 +512,9 @@ with bootstrap.output('config.toml') as f:
     for section in section_order:
         if section == 'target':
             for target in targets:
-                for line in targets[target]:
-                    f.write(line + "\n")
+                f = write_uncommented(targets[target], f)
         else:
-            for line in sections[section]:
-                f.write(line + "\n")
+            f = write_uncommented(sections[section], f)
 
 with bootstrap.output('Makefile') as f:
     contents = os.path.join(rust_dir, 'src', 'bootstrap', 'mk', 'Makefile.in')
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 090ea2cb157..e22ac6ec19b 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -7,9 +7,7 @@ use rustc_span::symbol::Symbol;
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 
 use crate::clean;
-use crate::clean::types::{
-    FnRetTy, Function, GenericBound, Generics, ItemId, Type, WherePredicate,
-};
+use crate::clean::types::{FnRetTy, Function, Generics, ItemId, Type, WherePredicate};
 use crate::formats::cache::{Cache, OrphanImplItem};
 use crate::formats::item_type::ItemType;
 use crate::html::format::join_with_double_colon;
@@ -482,29 +480,23 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
     if let Type::Generic(arg_s) = *arg {
         // First we check if the bounds are in a `where` predicate...
         if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
-            WherePredicate::BoundPredicate { ty, .. } => ty.def_id(cache) == arg.def_id(cache),
+            WherePredicate::BoundPredicate { ty: Type::Generic(ty_s), .. } => *ty_s == arg_s,
             _ => false,
         }) {
             let mut ty_generics = Vec::new();
             let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
             for bound in bounds.iter() {
-                if let GenericBound::TraitBound(poly_trait, _) = bound {
-                    for param_def in poly_trait.generic_params.iter() {
-                        match &param_def.kind {
-                            clean::GenericParamDefKind::Type { default: Some(ty), .. } => {
-                                add_generics_and_bounds_as_types(
-                                    self_,
-                                    generics,
-                                    ty,
-                                    tcx,
-                                    recurse + 1,
-                                    &mut ty_generics,
-                                    cache,
-                                )
-                            }
-                            _ => {}
-                        }
-                    }
+                if let Some(path) = bound.get_trait_path() {
+                    let ty = Type::Path { path };
+                    add_generics_and_bounds_as_types(
+                        self_,
+                        generics,
+                        &ty,
+                        tcx,
+                        recurse + 1,
+                        &mut ty_generics,
+                        cache,
+                    );
                 }
             }
             insert_ty(res, arg.clone(), ty_generics);
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 5a46729156d..b98bced4126 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -469,6 +469,15 @@ function initSearch(rawSearchIndex) {
             }
             const posBefore = parserState.pos;
             getNextElem(query, parserState, elems, endChar === ">");
+            if (endChar !== "") {
+                if (parserState.pos >= parserState.length) {
+                    throw ["Unclosed ", "<"];
+                }
+                const c2 = parserState.userQuery[parserState.pos];
+                if (!isSeparatorCharacter(c2) && c2 !== endChar) {
+                    throw ["Expected ", endChar, ", found ", c2];
+                }
+            }
             // This case can be encountered if `getNextElem` encountered a "stop character" right
             // from the start. For example if you have `,,` or `<>`. In this case, we simply move up
             // the current position to continue the parsing.
@@ -477,7 +486,10 @@ function initSearch(rawSearchIndex) {
             }
             foundStopChar = false;
         }
-        // We are either at the end of the string or on the `endChar`` character, let's move forward
+        if (parserState.pos >= parserState.length && endChar !== "") {
+            throw ["Unclosed ", "<"];
+        }
+        // We are either at the end of the string or on the `endChar` character, let's move forward
         // in any case.
         parserState.pos += 1;
     }
diff --git a/tests/rustdoc-js-std/option-type-signatures.js b/tests/rustdoc-js-std/option-type-signatures.js
new file mode 100644
index 00000000000..dee4819e81a
--- /dev/null
+++ b/tests/rustdoc-js-std/option-type-signatures.js
@@ -0,0 +1,7 @@
+const QUERY = 'option, fnonce -> option';
+
+const EXPECTED = {
+    'others': [
+        { 'path': 'std::option::Option', 'name': 'map' },
+    ],
+};
diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js
index 6c5a7770283..98c6f27ca61 100644
--- a/tests/rustdoc-js-std/parser-errors.js
+++ b/tests/rustdoc-js-std/parser-errors.js
@@ -39,6 +39,7 @@ const QUERY = [
     "a!!",
     "mod:a!",
     "a!::a",
+    "a<",
 ];
 
 const PARSED = [
@@ -402,4 +403,13 @@ const PARSED = [
         userQuery: "a!::a",
         error: 'Cannot have associated items in macros',
     },
+    {
+        elems: [],
+        foundElems: 0,
+        original: "a<",
+        returned: [],
+        typeFilter: -1,
+        userQuery: "a<",
+        error: "Unclosed `<`",
+    },
 ];
diff --git a/tests/rustdoc-js/where-clause.js b/tests/rustdoc-js/where-clause.js
new file mode 100644
index 00000000000..6cb42a455a3
--- /dev/null
+++ b/tests/rustdoc-js/where-clause.js
@@ -0,0 +1,19 @@
+const QUERY = ['trait<nested>', '-> trait<nested>', 't1, t2'];
+
+const EXPECTED = [
+    {
+        'in_args': [
+           { 'path': 'where_clause', 'name': 'abracadabra' },
+        ],
+    },
+    {
+        'others': [
+            { 'path': 'where_clause', 'name': 'alacazam' },
+        ],
+    },
+    {
+        'others': [
+            { 'path': 'where_clause', 'name': 'presto' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/where-clause.rs b/tests/rustdoc-js/where-clause.rs
new file mode 100644
index 00000000000..808561feee2
--- /dev/null
+++ b/tests/rustdoc-js/where-clause.rs
@@ -0,0 +1,16 @@
+pub struct Nested;
+
+pub trait Trait<T> {
+    fn thank_you(x: T);
+}
+
+pub fn abracadabra<X>(_: X) where X: Trait<Nested> {}
+
+pub fn alacazam<X>() -> X where X: Trait<Nested> {}
+
+pub trait T1 {}
+pub trait T2<'a, T> {
+    fn please(_: &'a T);
+}
+
+pub fn presto<A, B>(_: A, _: B) where A: T1, B: for <'b> T2<'b, Nested> {}
diff --git a/tests/ui/parser/issue-107705.rs b/tests/ui/parser/issue-107705.rs
new file mode 100644
index 00000000000..b80984fcdb0
--- /dev/null
+++ b/tests/ui/parser/issue-107705.rs
@@ -0,0 +1,3 @@
+// compile-flags: -C debug-assertions
+
+fn f() {a(b:&, //~ ERROR this file contains an unclosed delimiter
diff --git a/tests/ui/parser/issue-107705.stderr b/tests/ui/parser/issue-107705.stderr
new file mode 100644
index 00000000000..d2d61346118
--- /dev/null
+++ b/tests/ui/parser/issue-107705.stderr
@@ -0,0 +1,10 @@
+error: this file contains an unclosed delimiter
+  --> $DIR/issue-107705.rs:3:67
+   |
+LL | fn f() {a(b:&,
+   |        - - unclosed delimiter                                     ^
+   |        |
+   |        unclosed delimiter
+
+error: aborting due to previous error
+
diff --git a/tests/ui/suggestions/multiline-multipart-suggestion.rs b/tests/ui/suggestions/multiline-multipart-suggestion.rs
new file mode 100644
index 00000000000..77d0322d05f
--- /dev/null
+++ b/tests/ui/suggestions/multiline-multipart-suggestion.rs
@@ -0,0 +1,19 @@
+// compile-flags: --error-format=human --color=always
+// ignore-windows
+
+fn short(foo_bar: &Vec<&i32>) -> &i32 { //~ ERROR missing lifetime specifier
+    &12
+}
+
+fn long( //~ ERROR missing lifetime specifier
+    foo_bar: &Vec<&i32>,
+    something_very_long_so_that_the_line_will_wrap_around__________: i32,
+) -> &i32 {
+    &12
+}
+
+fn long2( //~ ERROR missing lifetime specifier
+    foo_bar: &Vec<&i32>) -> &i32 {
+    &12
+}
+fn main() {}
diff --git a/tests/ui/suggestions/multiline-multipart-suggestion.stderr b/tests/ui/suggestions/multiline-multipart-suggestion.stderr
new file mode 100644
index 00000000000..045a86b4f54
--- /dev/null
+++ b/tests/ui/suggestions/multiline-multipart-suggestion.stderr
@@ -0,0 +1,46 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/multiline-multipart-suggestion.rs:4:34
+   |
+LL | fn short(foo_bar: &Vec<&i32>) -> &i32 {
+   |                   ----------     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL | fn short<'a>(foo_bar: &'a Vec<&'a i32>) -> &'a i32 {
+   |         ++++           ++      ++           ++
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/multiline-multipart-suggestion.rs:11:6
+   |
+LL |     foo_bar: &Vec<&i32>,
+   |              ----------
+LL |     something_very_long_so_that_the_line_will_wrap_around__________: i32,
+LL | ) -> &i32 {
+   |      ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL ~ fn long<'a>(
+LL ~     foo_bar: &'a Vec<&'a i32>,
+LL |     something_very_long_so_that_the_line_will_wrap_around__________: i32,
+LL ~ ) -> &'a i32 {
+   |
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/multiline-multipart-suggestion.rs:16:29
+   |
+LL |     foo_bar: &Vec<&i32>) -> &i32 {
+   |              ----------     ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but the signature does not say which one of `foo_bar`'s 2 lifetimes it is borrowed from
+help: consider introducing a named lifetime parameter
+   |
+LL ~ fn long2<'a>(
+LL ~     foo_bar: &'a Vec<&'a i32>) -> &'a i32 {
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0106`.