about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-16 22:48:31 +0000
committerbors <bors@rust-lang.org>2021-06-16 22:48:31 +0000
commit444a85ac38d6dcd361c66c4db097efd0fd9b9472 (patch)
treefa217f11406001f674d75850358ce50706e27ce6
parenta85f584aebd9b08314bf30b9adc17b4a752143e5 (diff)
parent27d5426bcfd430a0f1ca6b42cdfe6b92e16266e9 (diff)
downloadrust-444a85ac38d6dcd361c66c4db097efd0fd9b9472.tar.gz
rust-444a85ac38d6dcd361c66c4db097efd0fd9b9472.zip
Auto merge of #86379 - JohnTitor:rollup-mkz9x36, r=JohnTitor
Rollup of 10 pull requests

Successful merges:

 - #85870 (Allow whitespace in dump_mir filter)
 - #86104 (Fix span calculation in format strings)
 - #86140 (Mention the `Borrow` guarantee on the `Hash` implementations for Arrays and `Vec`)
 - #86141 (Link reference in `dyn` keyword documentation)
 - #86260 (Open trait implementations' toggles by default.)
 - #86339 (Mention #79078 on compatibility notes of 1.52)
 - #86341 (Stop returning a value from `report_assert_as_lint`)
 - #86353 (Remove `projection_ty_from_predicates`)
 - #86361 (Add missing backslashes to prevent unwanted newlines in rustdoc HTML)
 - #86372 (Typo correction: s/is/its)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--RELEASES.md2
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs15
-rw-r--r--compiler/rustc_middle/src/dep_graph/dep_node.rs2
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_mir/src/transform/const_prop.rs25
-rw-r--r--compiler/rustc_mir/src/util/pretty.rs5
-rw-r--r--compiler/rustc_typeck/src/collect.rs24
-rw-r--r--library/alloc/src/vec/mod.rs17
-rw-r--r--library/core/src/array/mod.rs17
-rw-r--r--library/core/src/ptr/non_null.rs2
-rw-r--r--library/std/src/keyword_docs.rs7
-rw-r--r--src/librustdoc/html/render/mod.rs9
-rw-r--r--src/librustdoc/html/render/print_item.rs8
-rw-r--r--src/librustdoc/html/static/main.js14
-rw-r--r--src/test/rustdoc-gui/toggled-open-implementations.goml5
-rw-r--r--src/test/ui/fmt/format-concat-span.rs15
-rw-r--r--src/test/ui/fmt/format-concat-span.stderr11
-rw-r--r--src/test/ui/fmt/issue-86085.rs6
-rw-r--r--src/test/ui/fmt/issue-86085.stderr11
19 files changed, 136 insertions, 63 deletions
diff --git a/RELEASES.md b/RELEASES.md
index a49091373df..da8333e6287 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -306,6 +306,7 @@ Compatibility Notes
 - [Rustc now catches more cases of `pub_use_of_private_extern_crate`][80763]
 - [Changes in how proc macros handle whitespace may lead to panics when used
   with older `proc-macro-hack` versions. A `cargo update` should be sufficient to fix this in all cases.][84136]
+- [Turn `#[derive]` into a regular macro attribute][79078]
 
 [84136]: https://github.com/rust-lang/rust/issues/84136
 [80763]: https://github.com/rust-lang/rust/pull/80763
@@ -332,6 +333,7 @@ Compatibility Notes
 [78429]: https://github.com/rust-lang/rust/pull/78429
 [82733]: https://github.com/rust-lang/rust/pull/82733
 [82594]: https://github.com/rust-lang/rust/pull/82594
+[79078]: https://github.com/rust-lang/rust/pull/79078
 [cargo/9181]: https://github.com/rust-lang/cargo/pull/9181
 [`char::MAX`]: https://doc.rust-lang.org/std/primitive.char.html#associatedconstant.MAX
 [`char::REPLACEMENT_CHARACTER`]: https://doc.rust-lang.org/std/primitive.char.html#associatedconstant.REPLACEMENT_CHARACTER
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 7e88b58c0e2..00f2f37146d 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -939,6 +939,7 @@ pub fn expand_preparsed_format_args(
 
     let msg = "format argument must be a string literal";
     let fmt_sp = efmt.span;
+    let efmt_kind_is_lit: bool = matches!(efmt.kind, ast::ExprKind::Lit(_));
     let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
         Ok(mut fmt) if append_newline => {
             fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
@@ -989,7 +990,19 @@ pub fn expand_preparsed_format_args(
 
     if !parser.errors.is_empty() {
         let err = parser.errors.remove(0);
-        let sp = fmt_span.from_inner(err.span);
+        let sp = if efmt_kind_is_lit {
+            fmt_span.from_inner(err.span)
+        } else {
+            // The format string could be another macro invocation, e.g.:
+            //     format!(concat!("abc", "{}"), 4);
+            // However, `err.span` is an inner span relative to the *result* of
+            // the macro invocation, which is why we would get a nonsensical
+            // result calling `fmt_span.from_inner(err.span)` as above, and
+            // might even end up inside a multibyte character (issue #86085).
+            // Therefore, we conservatively report the error for the entire
+            // argument span here.
+            fmt_span
+        };
         let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", err.description));
         e.span_label(sp, err.label + " in format string");
         if let Some(note) = err.note {
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index 8476929eaec..aa54d1ae7b9 100644
--- a/compiler/rustc_middle/src/dep_graph/dep_node.rs
+++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs
@@ -285,7 +285,7 @@ pub type DepNode = rustc_query_system::dep_graph::DepNode<DepKind>;
 // required that their size stay the same, but we don't want to change
 // it inadvertently. This assert just ensures we're aware of any change.
 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-static_assert_size!(DepNode, 18);
+static_assert_size!(DepNode, 17);
 
 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
 static_assert_size!(DepNode, 24);
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a6f9a7c96f0..dda407940e3 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -191,10 +191,6 @@ rustc_queries! {
         desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
     }
 
-    query projection_ty_from_predicates(key: (DefId, DefId)) -> Option<ty::ProjectionTy<'tcx>> {
-        desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) }
-    }
-
     query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
         desc { "looking up the native libraries of a linked crate" }
     }
diff --git a/compiler/rustc_mir/src/transform/const_prop.rs b/compiler/rustc_mir/src/transform/const_prop.rs
index 681d63c6fc9..73a0f5537c3 100644
--- a/compiler/rustc_mir/src/transform/const_prop.rs
+++ b/compiler/rustc_mir/src/transform/const_prop.rs
@@ -528,14 +528,14 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         source_info: SourceInfo,
         message: &'static str,
         panic: AssertKind<impl std::fmt::Debug>,
-    ) -> Option<()> {
-        let lint_root = self.lint_root(source_info)?;
-        self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| {
-            let mut err = lint.build(message);
-            err.span_label(source_info.span, format!("{:?}", panic));
-            err.emit()
-        });
-        None
+    ) {
+        if let Some(lint_root) = self.lint_root(source_info) {
+            self.tcx.struct_span_lint_hir(lint, lint_root, source_info.span, |lint| {
+                let mut err = lint.build(message);
+                err.span_label(source_info.span, format!("{:?}", panic));
+                err.emit()
+            });
+        }
     }
 
     fn check_unary_op(
@@ -557,7 +557,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                 source_info,
                 "this arithmetic operation will overflow",
                 AssertKind::OverflowNeg(val.to_const_int()),
-            )?;
+            );
+            return None;
         }
 
         Some(())
@@ -602,7 +603,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                         },
                         r.to_const_int(),
                     ),
-                )?;
+                );
+                return None;
             }
         }
 
@@ -617,7 +619,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                     source_info,
                     "this arithmetic operation will overflow",
                     AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()),
-                )?;
+                );
+                return None;
             }
         }
         Some(())
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index c4e1e184ac5..f8325a3646f 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -99,7 +99,10 @@ pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) ->
     });
     filters.split('|').any(|or_filter| {
         or_filter.split('&').all(|and_filter| {
-            and_filter == "all" || pass_name.contains(and_filter) || node_path.contains(and_filter)
+            let and_filter_trimmed = and_filter.trim();
+            and_filter_trimmed == "all"
+                || pass_name.contains(and_filter_trimmed)
+                || node_path.contains(and_filter_trimmed)
         })
     })
 }
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index ee3ac3b62d9..55de04bfba0 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -77,7 +77,6 @@ pub fn provide(providers: &mut Providers) {
         generics_of,
         predicates_of,
         predicates_defined_on,
-        projection_ty_from_predicates,
         explicit_predicates_of,
         super_predicates_of,
         super_predicates_that_define_assoc_type,
@@ -2352,29 +2351,6 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
     }
 }
 
-fn projection_ty_from_predicates(
-    tcx: TyCtxt<'tcx>,
-    key: (
-        // ty_def_id
-        DefId,
-        // def_id of `N` in `<T as Trait>::N`
-        DefId,
-    ),
-) -> Option<ty::ProjectionTy<'tcx>> {
-    let (ty_def_id, item_def_id) = key;
-    let mut projection_ty = None;
-    for (predicate, _) in tcx.predicates_of(ty_def_id).predicates {
-        if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder()
-        {
-            if item_def_id == projection_predicate.projection_ty.item_def_id {
-                projection_ty = Some(projection_predicate.projection_ty);
-                break;
-            }
-        }
-    }
-    projection_ty
-}
-
 /// Converts a specific `GenericBound` from the AST into a set of
 /// predicates that apply to the self type. A vector is returned
 /// because this can be anywhere from zero predicates (`T: ?Sized` adds no
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 4a1d564e2ab..b59d2977add 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -2407,6 +2407,23 @@ impl<T: Clone, A: Allocator + Clone> Clone for Vec<T, A> {
     }
 }
 
+/// The hash of a vector is the same as that of the corresponding slice,
+/// as required by the `core::borrow::Borrow` implementation.
+///
+/// ```
+/// use std::hash::{BuildHasher, Hash, Hasher};
+///
+/// fn hash_of(x: impl Hash, b: &impl BuildHasher) -> u64 {
+///     let mut h = b.build_hasher();
+///     x.hash(&mut h);
+///     h.finish()
+/// }
+///
+/// let b = std::collections::hash_map::RandomState::new();
+/// let v: Vec<u8> = vec![0xa8, 0x3c, 0x09];
+/// let s: &[u8] = &[0xa8, 0x3c, 0x09];
+/// assert_eq!(hash_of(v, &b), hash_of(s, &b));
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Hash, A: Allocator> Hash for Vec<T, A> {
     #[inline]
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 37af3557fdd..f44e22b3dbd 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -139,6 +139,23 @@ impl<'a, T, const N: usize> TryFrom<&'a mut [T]> for &'a mut [T; N] {
     }
 }
 
+/// The hash of an array is the same as that of the corresponding slice,
+/// as required by the `Borrow` implementation.
+///
+/// ```
+/// use std::hash::{BuildHasher, Hash, Hasher};
+///
+/// fn hash_of(x: impl Hash, b: &impl BuildHasher) -> u64 {
+///     let mut h = b.build_hasher();
+///     x.hash(&mut h);
+///     h.finish()
+/// }
+///
+/// let b = std::collections::hash_map::RandomState::new();
+/// let a: [u8; 3] = [0xa8, 0x3c, 0x09];
+/// let s: &[u8] = &[0xa8, 0x3c, 0x09];
+/// assert_eq!(hash_of(a, &b), hash_of(s, &b));
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Hash, const N: usize> Hash for [T; N] {
     fn hash<H: hash::Hasher>(&self, state: &mut H) {
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 1c65518af04..3ab40f1faa1 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -194,7 +194,7 @@ impl<T: ?Sized> NonNull<T> {
         }
     }
 
-    /// Decompose a (possibly wide) pointer into is address and metadata components.
+    /// Decompose a (possibly wide) pointer into its address and metadata components.
     ///
     /// The pointer can be later reconstructed with [`NonNull::from_raw_parts`].
     #[unstable(feature = "ptr_metadata", issue = "81513")]
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index c2d21ad23ac..a609567ce93 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -2256,6 +2256,9 @@ mod await_keyword {}
 /// At run-time, when a method needs to be called on the `dyn Trait`, the vtable is consulted to get
 /// the function pointer and then that function pointer is called.
 ///
+/// See the Reference for more information on [trait objects][ref-trait-obj]
+/// and [object safety][ref-obj-safety].
+///
 /// ## Trade-offs
 ///
 /// The above indirection is the additional runtime cost of calling a function on a `dyn Trait`.
@@ -2264,9 +2267,9 @@ mod await_keyword {}
 /// However, `dyn Trait` is likely to produce smaller code than `impl Trait` / generic parameters as
 /// the method won't be duplicated for each concrete type.
 ///
-/// Read more about `object safety` and [trait object]s.
-///
 /// [trait object]: ../book/ch17-02-trait-objects.html
+/// [ref-trait-obj]: ../reference/types/trait-object.html
+/// [ref-obj-safety]: ../reference/items/traits.html#object-safety
 /// [erased]: https://en.wikipedia.org/wiki/Type_erasure
 mod dyn_keyword {}
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 46fe3e2408f..0efa014b127 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -488,7 +488,7 @@ fn settings(root_path: &str, suffix: &str, themes: &[StylePath]) -> Result<Strin
             .into(),
         ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
         ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
-        ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", true)
+        ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false)
             .into(),
         ("auto-collapse-implementors", "Auto-hide implementors of a trait", true).into(),
         ("go-to-only-result", "Directly go to item in search if there is only one result", false)
@@ -1543,15 +1543,10 @@ fn render_impl(
         }
     }
     if render_mode == RenderMode::Normal {
-        let is_implementing_trait = i.inner_impl().trait_.is_some();
         let toggled = !impl_items.is_empty() || !default_impl_items.is_empty();
         if toggled {
             close_tags.insert_str(0, "</details>");
-            if is_implementing_trait {
-                write!(w, "<details class=\"rustdoc-toggle implementors-toggle\">");
-            } else {
-                write!(w, "<details class=\"rustdoc-toggle implementors-toggle\" open>");
-            }
+            write!(w, "<details class=\"rustdoc-toggle implementors-toggle\" open>");
         }
         if toggled {
             write!(w, "<summary>")
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 04464b622d7..88ec172a18b 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -864,7 +864,7 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni
     if fields.peek().is_some() {
         write!(
             w,
-            "<h2 id=\"fields\" class=\"fields small-section-header\">
+            "<h2 id=\"fields\" class=\"fields small-section-header\">\
                    Fields<a href=\"#fields\" class=\"anchor\"></a></h2>"
         );
         for (field, ty) in fields {
@@ -953,8 +953,8 @@ fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum
     if !e.variants.is_empty() {
         write!(
             w,
-            "<h2 id=\"variants\" class=\"variants small-section-header\">
-                   Variants{}<a href=\"#variants\" class=\"anchor\"></a></h2>\n",
+            "<h2 id=\"variants\" class=\"variants small-section-header\">\
+                   Variants{}<a href=\"#variants\" class=\"anchor\"></a></h2>",
             document_non_exhaustive_header(it)
         );
         document_non_exhaustive(w, it);
@@ -1139,7 +1139,7 @@ fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::St
         if fields.peek().is_some() {
             write!(
                 w,
-                "<h2 id=\"fields\" class=\"fields small-section-header\">
+                "<h2 id=\"fields\" class=\"fields small-section-header\">\
                        Fields{}<a href=\"#fields\" class=\"anchor\"></a></h2>",
                 document_non_exhaustive_header(it)
             );
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 1a15a444a70..e43a231d757 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -779,25 +779,25 @@ function hideThemeButtonState() {
 
         var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
         var hideImplementors = getSettingValue("auto-collapse-implementors") !== "false";
-        var hideImplementations = getSettingValue("auto-hide-trait-implementations") !== "false";
+        var hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
         var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
 
-        function openImplementors(id) {
+        function setImplementorsTogglesOpen(id, open) {
             var list = document.getElementById(id);
             if (list !== null) {
                 onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
-                    e.open = true;
+                    e.open = open;
                 });
             }
         }
 
-        if (!hideImplementations) {
-            openImplementors("trait-implementations-list");
-            openImplementors("blanket-implementations-list");
+        if (hideImplementations) {
+            setImplementorsTogglesOpen("trait-implementations-list", false);
+            setImplementorsTogglesOpen("blanket-implementations-list", false);
         }
 
         if (!hideImplementors) {
-            openImplementors("implementors-list");
+            setImplementorsTogglesOpen("implementors-list", true);
         }
 
         onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function (e) {
diff --git a/src/test/rustdoc-gui/toggled-open-implementations.goml b/src/test/rustdoc-gui/toggled-open-implementations.goml
new file mode 100644
index 00000000000..96a5492edef
--- /dev/null
+++ b/src/test/rustdoc-gui/toggled-open-implementations.goml
@@ -0,0 +1,5 @@
+// This tests that the "implementations" section on struct/enum pages
+// has all the implementations toggled open by default, so users can
+// find method names in those implementations with Ctrl-F.
+goto: file://|DOC_PATH|/test_docs/struct.Foo.html
+assert: (".rustdoc-toggle.implementors-toggle", "open", "")
diff --git a/src/test/ui/fmt/format-concat-span.rs b/src/test/ui/fmt/format-concat-span.rs
new file mode 100644
index 00000000000..ce92df0ad92
--- /dev/null
+++ b/src/test/ui/fmt/format-concat-span.rs
@@ -0,0 +1,15 @@
+// If the format string is another macro invocation, rustc would previously
+// compute nonsensical spans, such as:
+//
+//   error: invalid format string: unmatched `}` found
+//    --> test.rs:2:17
+//     |
+//   2 |     format!(concat!("abc}"));
+//     |                 ^ unmatched `}` in format string
+//
+// This test checks that this behavior has been fixed.
+
+fn main() {
+    format!(concat!("abc}"));
+    //~^ ERROR: invalid format string: unmatched `}` found
+}
diff --git a/src/test/ui/fmt/format-concat-span.stderr b/src/test/ui/fmt/format-concat-span.stderr
new file mode 100644
index 00000000000..da46f40abcb
--- /dev/null
+++ b/src/test/ui/fmt/format-concat-span.stderr
@@ -0,0 +1,11 @@
+error: invalid format string: unmatched `}` found
+  --> $DIR/format-concat-span.rs:13:13
+   |
+LL |     format!(concat!("abc}"));
+   |             ^^^^^^^^^^^^^^^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+   = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/fmt/issue-86085.rs b/src/test/ui/fmt/issue-86085.rs
new file mode 100644
index 00000000000..63d42b76969
--- /dev/null
+++ b/src/test/ui/fmt/issue-86085.rs
@@ -0,0 +1,6 @@
+// Tests for an ICE with the fuzzed input below.
+
+fn main ( ) {
+format ! ( concat ! ( r#"lJ𐏿Æ�.𐏿�"# , "r} {}" )     ) ;
+//~^ ERROR: invalid format string: unmatched `}` found
+}
diff --git a/src/test/ui/fmt/issue-86085.stderr b/src/test/ui/fmt/issue-86085.stderr
new file mode 100644
index 00000000000..ee7d8a5cc23
--- /dev/null
+++ b/src/test/ui/fmt/issue-86085.stderr
@@ -0,0 +1,11 @@
+error: invalid format string: unmatched `}` found
+  --> $DIR/issue-86085.rs:4:12
+   |
+LL | format ! ( concat ! ( r#"lJ𐏿Æ�.𐏿�"# , "r} {}" )     ) ;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unmatched `}` in format string
+   |
+   = note: if you intended to print `}`, you can escape it using `}}`
+   = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+