about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-02-23 17:24:33 +0000
committerbors <bors@rust-lang.org>2021-02-23 17:24:33 +0000
commitfe1bf8e05c39bdcc73fc09e246b7209444e389bc (patch)
tree9265dd81347434a5b0b6785222fa0b5901e43a1e
parent019610754363d1d92a8d0f364d2c0909d6f53dfd (diff)
parent51511c75b558beddab7fb4b6d8277a872714b87a (diff)
downloadrust-fe1bf8e05c39bdcc73fc09e246b7209444e389bc.tar.gz
rust-fe1bf8e05c39bdcc73fc09e246b7209444e389bc.zip
Auto merge of #82443 - Dylan-DPC:rollup-yni7uio, r=Dylan-DPC
Rollup of 10 pull requests

Successful merges:

 - #81629 (Point out implicit deref coercions in borrow)
 - #82113 (Improve non_fmt_panic lint.)
 - #82258 (Implement -Z hir-stats for nested foreign items)
 - #82296 (Support `pub` on `macro_rules`)
 - #82297 (Consider auto derefs before warning about write only fields)
 - #82305 (Remove many RefCells from DocContext)
 - #82308 (Lower condition of `if` expression before it's "then" block)
 - #82311 (Jsondocck improvements)
 - #82362 (Fix mir-cfg dumps)
 - #82391 (disable atomic_max/min tests in Miri)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs3
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_feature/src/active.rs3
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs73
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs41
-rw-r--r--compiler/rustc_mir/src/util/graphviz.rs23
-rw-r--r--compiler/rustc_parse/src/parser/item.rs10
-rw-r--r--compiler/rustc_passes/src/dead.rs28
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs5
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs9
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--library/alloc/src/macros.rs1
-rw-r--r--library/core/tests/atomic.rs4
-rw-r--r--src/librustdoc/clean/auto_trait.rs2
-rw-r--r--src/librustdoc/clean/blanket_impl.rs6
-rw-r--r--src/librustdoc/clean/inline.rs21
-rw-r--r--src/librustdoc/clean/mod.rs14
-rw-r--r--src/librustdoc/clean/utils.rs13
-rw-r--r--src/librustdoc/core.rs35
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs2
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs5
-rw-r--r--src/librustdoc/passes/doc_test_lints.rs3
-rw-r--r--src/librustdoc/passes/strip_private.rs2
-rw-r--r--src/librustdoc/visit_ast.rs11
-rw-r--r--src/librustdoc/visit_lib.rs2
-rw-r--r--src/test/rustdoc-json/nested.rs24
-rw-r--r--src/test/ui/borrowck/issue-81365-1.rs26
-rw-r--r--src/test/ui/borrowck/issue-81365-1.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-10.rs26
-rw-r--r--src/test/ui/borrowck/issue-81365-10.stderr13
-rw-r--r--src/test/ui/borrowck/issue-81365-11.rs32
-rw-r--r--src/test/ui/borrowck/issue-81365-11.stderr13
-rw-r--r--src/test/ui/borrowck/issue-81365-2.rs30
-rw-r--r--src/test/ui/borrowck/issue-81365-2.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-3.rs37
-rw-r--r--src/test/ui/borrowck/issue-81365-3.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-4.rs38
-rw-r--r--src/test/ui/borrowck/issue-81365-4.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-5.rs33
-rw-r--r--src/test/ui/borrowck/issue-81365-5.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-6.rs23
-rw-r--r--src/test/ui/borrowck/issue-81365-6.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-7.rs24
-rw-r--r--src/test/ui/borrowck/issue-81365-7.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-8.rs26
-rw-r--r--src/test/ui/borrowck/issue-81365-8.stderr20
-rw-r--r--src/test/ui/borrowck/issue-81365-9.rs26
-rw-r--r--src/test/ui/borrowck/issue-81365-9.stderr13
-rw-r--r--src/test/ui/did_you_mean/pub-macro-rules.rs16
-rw-r--r--src/test/ui/did_you_mean/pub-macro-rules.stderr8
-rw-r--r--src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs10
-rw-r--r--src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr39
-rw-r--r--src/test/ui/issues/issue-81918.rs11
-rw-r--r--src/test/ui/lint/dead-code/write-only-field.rs49
-rw-r--r--src/test/ui/lint/dead-code/write-only-field.stderr20
-rw-r--r--src/test/ui/macros/macro-export-on-modularized-macros.rs11
-rw-r--r--src/test/ui/macros/macro-export-on-modularized-macros.stderr14
-rw-r--r--src/test/ui/macros/pub-macro-rules-fail.rs28
-rw-r--r--src/test/ui/macros/pub-macro-rules-fail.stderr48
-rw-r--r--src/test/ui/macros/pub-macro-rules.rs20
-rw-r--r--src/test/ui/non-fmt-panic.rs11
-rw-r--r--src/test/ui/non-fmt-panic.stderr69
-rw-r--r--src/test/ui/pattern/issue-82290.rs7
-rw-r--r--src/test/ui/pattern/issue-82290.stderr20
-rw-r--r--src/tools/jsondocck/src/cache.rs2
-rw-r--r--src/tools/jsondocck/src/main.rs71
66 files changed, 1160 insertions, 157 deletions
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index b118c0eaed4..6d44feec2c4 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -347,8 +347,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::ExprKind<'hir> {
         macro_rules! make_if {
             ($opt:expr) => {{
+                let cond = self.lower_expr(cond);
                 let then_expr = self.lower_block_expr(then);
-                hir::ExprKind::If(self.lower_expr(cond), self.arena.alloc(then_expr), $opt)
+                hir::ExprKind::If(cond, self.arena.alloc(then_expr), $opt)
             }};
         }
         if let Some(rslt) = else_opt {
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 6514de2b813..474ec2b589b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -665,6 +665,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
         // involved, so we only emit errors where there are no other parsing errors.
         gate_all!(destructuring_assignment, "destructuring assignments are unstable");
     }
+    gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 2d0009c225c..3f484ab5686 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -638,6 +638,9 @@ declare_features! (
     /// Allows macro attributes to observe output of `#[derive]`.
     (active, macro_attributes_in_derive_output, "1.51.0", Some(81119), None),
 
+    /// Allows `pub` on `macro_rules` items.
+    (active, pub_macro_rules, "1.52.0", Some(78855), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index e98297b692c..bfe37ce6959 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -69,23 +69,65 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
 
     let (span, panic) = panic_call(cx, f);
 
-    cx.struct_span_lint(NON_FMT_PANIC, arg.span, |lint| {
+    // Find the span of the argument to `panic!()`, before expansion in the
+    // case of `panic!(some_macro!())`.
+    // We don't use source_callsite(), because this `panic!(..)` might itself
+    // be expanded from another macro, in which case we want to stop at that
+    // expansion.
+    let mut arg_span = arg.span;
+    let mut arg_macro = None;
+    while !span.contains(arg_span) {
+        let expn = arg_span.ctxt().outer_expn_data();
+        if expn.is_root() {
+            break;
+        }
+        arg_macro = expn.macro_def_id;
+        arg_span = expn.call_site;
+    }
+
+    cx.struct_span_lint(NON_FMT_PANIC, arg_span, |lint| {
         let mut l = lint.build("panic message is not a string literal");
         l.note("this is no longer accepted in Rust 2021");
-        if span.contains(arg.span) {
+        if !span.contains(arg_span) {
+            // No clue where this argument is coming from.
+            l.emit();
+            return;
+        }
+        if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
+            // A case of `panic!(format!(..))`.
+            l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here");
+            if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
+                l.multipart_suggestion(
+                    "remove the `format!(..)` macro call",
+                    vec![
+                        (arg_span.until(open.shrink_to_hi()), "".into()),
+                        (close.until(arg_span.shrink_to_hi()), "".into()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            }
+        } else {
             l.span_suggestion_verbose(
-                arg.span.shrink_to_lo(),
+                arg_span.shrink_to_lo(),
                 "add a \"{}\" format string to Display the message",
                 "\"{}\", ".into(),
                 Applicability::MaybeIncorrect,
             );
             if panic == sym::std_panic_macro {
-                l.span_suggestion_verbose(
-                    span.until(arg.span),
-                    "or use std::panic::panic_any instead",
-                    "std::panic::panic_any(".into(),
-                    Applicability::MachineApplicable,
-                );
+                if let Some((open, close, del)) = find_delimiters(cx, span) {
+                    l.multipart_suggestion(
+                        "or use std::panic::panic_any instead",
+                        if del == '(' {
+                            vec![(span.until(open), "std::panic::panic_any".into())]
+                        } else {
+                            vec![
+                                (span.until(open.shrink_to_hi()), "std::panic::panic_any(".into()),
+                                (close, ")".into()),
+                            ]
+                        },
+                        Applicability::MachineApplicable,
+                    );
+                }
             }
         }
         l.emit();
@@ -175,6 +217,19 @@ fn check_panic_str<'tcx>(
     }
 }
 
+/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
+/// and the type of (opening) delimiter used.
+fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
+    let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
+    let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
+    let close = snippet.rfind(|c| ")]}".contains(c))?;
+    Some((
+        span.from_inner(InnerSpan { start: open, end: open + 1 }),
+        span.from_inner(InnerSpan { start: close, end: close + 1 }),
+        open_ch,
+    ))
+}
+
 fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) {
     let mut expn = f.span.ctxt().outer_expn_data();
 
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index cd16a88e5fc..b0b58a8d003 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -8,11 +8,10 @@ use rustc_index::vec::Idx;
 use rustc_middle::mir::{
     self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
     FakeReadCause, Local, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
-    ProjectionElem, Rvalue, Statement, StatementKind, TerminatorKind, VarBindingForm,
+    ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
 };
-use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
-use rustc_span::source_map::DesugaringKind;
-use rustc_span::Span;
+use rustc_middle::ty::{self, suggest_constraining_type_param, Instance, Ty};
+use rustc_span::{source_map::DesugaringKind, symbol::sym, Span};
 
 use crate::dataflow::drop_flag_effects;
 use crate::dataflow::indexes::{MoveOutIndex, MovePathIndex};
@@ -1543,9 +1542,43 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             None,
         );
 
+        self.explain_deref_coercion(loan, &mut err);
+
         err.buffer(&mut self.errors_buffer);
     }
 
+    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
+        let tcx = self.infcx.tcx;
+        if let (
+            Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
+            Some((method_did, method_substs)),
+        ) = (
+            &self.body[loan.reserve_location.block].terminator,
+            crate::util::find_self_call(
+                tcx,
+                self.body,
+                loan.assigned_place.local,
+                loan.reserve_location.block,
+            ),
+        ) {
+            if tcx.is_diagnostic_item(sym::deref_method, method_did) {
+                let deref_target =
+                    tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
+                        Instance::resolve(tcx, self.param_env, deref_target, method_substs)
+                            .transpose()
+                    });
+                if let Some(Ok(instance)) = deref_target {
+                    let deref_target_ty = instance.ty(tcx, self.param_env);
+                    err.note(&format!(
+                        "borrow occurs due to deref coercion to `{}`",
+                        deref_target_ty
+                    ));
+                    err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
+                }
+            }
+        }
+    }
+
     /// Reports an illegal reassignment; for example, an assignment to
     /// (part of) a non-`mut` local that occurs potentially after that
     /// local has already been initialized. `place` is the path being
diff --git a/compiler/rustc_mir/src/util/graphviz.rs b/compiler/rustc_mir/src/util/graphviz.rs
index 37498e50c0e..92c7a358c0a 100644
--- a/compiler/rustc_mir/src/util/graphviz.rs
+++ b/compiler/rustc_mir/src/util/graphviz.rs
@@ -2,7 +2,7 @@ use gsgdt::GraphvizSettings;
 use rustc_graphviz as dot;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir::*;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{self, TyCtxt};
 use std::fmt::Debug;
 use std::io::{self, Write};
 
@@ -16,14 +16,27 @@ where
 {
     let def_ids = dump_mir_def_ids(tcx, single);
 
-    let use_subgraphs = def_ids.len() > 1;
+    let mirs =
+        def_ids
+            .iter()
+            .flat_map(|def_id| {
+                if tcx.is_const_fn_raw(*def_id) {
+                    vec![tcx.optimized_mir(*def_id), tcx.mir_for_ctfe(*def_id)]
+                } else {
+                    vec![tcx.instance_mir(ty::InstanceDef::Item(ty::WithOptConstParam::unknown(
+                        *def_id,
+                    )))]
+                }
+            })
+            .collect::<Vec<_>>();
+
+    let use_subgraphs = mirs.len() > 1;
     if use_subgraphs {
         writeln!(w, "digraph __crate__ {{")?;
     }
 
-    for def_id in def_ids {
-        let body = &tcx.optimized_mir(def_id);
-        write_mir_fn_graphviz(tcx, body, use_subgraphs, w)?;
+    for mir in mirs {
+        write_mir_fn_graphviz(tcx, mir, use_subgraphs, w)?;
     }
 
     if use_subgraphs {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index f3f5fc9af64..073a2d8bd51 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1475,15 +1475,7 @@ impl<'a> Parser<'a> {
         let vstr = pprust::vis_to_string(vis);
         let vstr = vstr.trim_end();
         if macro_rules {
-            let msg = format!("can't qualify macro_rules invocation with `{}`", vstr);
-            self.struct_span_err(vis.span, &msg)
-                .span_suggestion(
-                    vis.span,
-                    "try exporting the macro",
-                    "#[macro_export]".to_owned(),
-                    Applicability::MaybeIncorrect, // speculative
-                )
-                .emit();
+            self.sess.gated_spans.gate(sym::pub_macro_rules, vis.span);
         } else {
             self.struct_span_err(vis.span, "can't qualify macro invocation with `pub`")
                 .span_suggestion(
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index d51b501f7ae..62a95aa57c2 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -37,15 +37,6 @@ fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
     )
 }
 
-fn base_expr<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
-    loop {
-        match expr.kind {
-            hir::ExprKind::Field(base, ..) => expr = base,
-            _ => return expr,
-        }
-    }
-}
-
 struct MarkSymbolVisitor<'tcx> {
     worklist: Vec<hir::HirId>,
     tcx: TyCtxt<'tcx>,
@@ -143,6 +134,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         }
     }
 
+    fn handle_assign(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+        if self
+            .typeck_results()
+            .expr_adjustments(expr)
+            .iter()
+            .any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
+        {
+            self.visit_expr(expr);
+        } else if let hir::ExprKind::Field(base, ..) = expr.kind {
+            // Ignore write to field
+            self.handle_assign(base);
+        } else {
+            self.visit_expr(expr);
+        }
+    }
+
     fn handle_field_pattern_match(
         &mut self,
         lhs: &hir::Pat<'_>,
@@ -272,8 +279,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
                 self.lookup_and_handle_method(expr.hir_id);
             }
             hir::ExprKind::Assign(ref left, ref right, ..) => {
-                // Ignore write to field
-                self.visit_expr(base_expr(left));
+                self.handle_assign(left);
                 self.visit_expr(right);
                 return;
             }
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index fac28281593..a2b6dd17ad9 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -114,6 +114,11 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
         self.visit_impl_item(nested_impl_item)
     }
 
+    fn visit_nested_foreign_item(&mut self, id: hir::ForeignItemId) {
+        let nested_foreign_item = self.krate.unwrap().foreign_item(id);
+        self.visit_foreign_item(nested_foreign_item);
+    }
+
     fn visit_nested_body(&mut self, body_id: hir::BodyId) {
         let nested_body = self.krate.unwrap().body(body_id);
         self.visit_body(nested_body)
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 79ed0b5308d..65e5b0dddea 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1230,13 +1230,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         };
 
         let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id());
+        let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
         self.r.macro_map.insert(def_id.to_def_id(), ext);
         self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
 
-        if macro_rules {
+        if macro_rules && matches!(item.vis.kind, ast::VisibilityKind::Inherited) {
             let ident = ident.normalize_to_macros_2_0();
             self.r.macro_names.insert(ident);
-            let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
             let vis = if is_macro_export {
                 ty::Visibility::Public
             } else {
@@ -1261,6 +1261,11 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 }),
             ))
         } else {
+            if is_macro_export {
+                let what = if macro_rules { "`macro_rules` with `pub`" } else { "`macro` items" };
+                let msg = format!("`#[macro_export]` cannot be used on {what}");
+                self.r.session.span_err(item.span, &msg);
+            }
             let module = parent_scope.module;
             let vis = match item.kind {
                 // Visibilities must not be resolved non-speculatively twice
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 4ed0262bf2c..b112402ffe3 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -560,6 +560,7 @@ symbols! {
         format_args,
         format_args_capture,
         format_args_nl,
+        format_macro,
         freeze,
         freg,
         frem_fast,
@@ -880,6 +881,7 @@ symbols! {
         ptr_guaranteed_eq,
         ptr_guaranteed_ne,
         ptr_offset_from,
+        pub_macro_rules,
         pub_restricted,
         pure,
         pushpop_unsafe,
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index a64a8b32ad7..6a64587a223 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -107,6 +107,7 @@ macro_rules! vec {
 /// ```
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")]
 macro_rules! format {
     ($($arg:tt)*) => {{
         let res = $crate::fmt::format($crate::__export::format_args!($($arg)*));
diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs
index b735957666f..539982eb0e4 100644
--- a/library/core/tests/atomic.rs
+++ b/library/core/tests/atomic.rs
@@ -61,6 +61,7 @@ fn uint_xor() {
 
 #[test]
 #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins
+#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min
 fn uint_min() {
     let x = AtomicUsize::new(0xf731);
     assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731);
@@ -71,6 +72,7 @@ fn uint_min() {
 
 #[test]
 #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins
+#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max
 fn uint_max() {
     let x = AtomicUsize::new(0x137f);
     assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f);
@@ -109,6 +111,7 @@ fn int_xor() {
 
 #[test]
 #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins
+#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_min
 fn int_min() {
     let x = AtomicIsize::new(0xf731);
     assert_eq!(x.fetch_min(0x137f, SeqCst), 0xf731);
@@ -119,6 +122,7 @@ fn int_min() {
 
 #[test]
 #[cfg(any(not(target_arch = "arm"), target_os = "linux"))] // Missing intrinsic in compiler-builtins
+#[cfg_attr(miri, ignore)] // FIXME: Miri does not support atomic_max
 fn int_max() {
     let x = AtomicIsize::new(0x137f);
     assert_eq!(x.fetch_max(0xf731, SeqCst), 0x137f);
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index 711b0298565..a24cb0a0f93 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -41,7 +41,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
     ) -> Option<Item> {
         let tcx = self.cx.tcx;
         let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) };
-        if !self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id)) {
+        if !self.cx.generated_synthetics.insert((ty, trait_def_id)) {
             debug!("get_auto_trait_impl_for({:?}): already generated, aborting", trait_ref);
             return None;
         }
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index a9d19a725c4..94b82037e75 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -22,8 +22,8 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
         debug!("get_blanket_impls({:?})", ty);
         let mut impls = Vec::new();
         for &trait_def_id in self.cx.tcx.all_traits(LOCAL_CRATE).iter() {
-            if !self.cx.renderinfo.borrow().access_levels.is_public(trait_def_id)
-                || self.cx.generated_synthetics.borrow_mut().get(&(ty, trait_def_id)).is_some()
+            if !self.cx.renderinfo.access_levels.is_public(trait_def_id)
+                || self.cx.generated_synthetics.get(&(ty, trait_def_id)).is_some()
             {
                 continue;
             }
@@ -94,7 +94,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     return;
                 }
 
-                self.cx.generated_synthetics.borrow_mut().insert((ty, trait_def_id));
+                self.cx.generated_synthetics.insert((ty, trait_def_id));
                 let provided_trait_methods = self
                     .cx
                     .tcx
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index fded0499ba6..ea75d1614bd 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -122,7 +122,7 @@ crate fn try_inline(
     let target_attrs = load_attrs(cx, did);
     let attrs = box merge_attrs(cx, Some(parent_module), target_attrs, attrs_clone);
 
-    cx.renderinfo.borrow_mut().inlined.insert(did);
+    cx.renderinfo.inlined.insert(did);
     let what_rustc_thinks = clean::Item::from_def_id_and_parts(did, Some(name), kind, cx);
     ret.push(clean::Item { attrs, ..what_rustc_thinks });
     Some(ret)
@@ -156,7 +156,7 @@ crate fn load_attrs<'hir>(cx: &DocContext<'hir>, did: DefId) -> Attrs<'hir> {
 ///
 /// These names are used later on by HTML rendering to generate things like
 /// source links back to the original item.
-crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKind) {
+crate fn record_extern_fqn(cx: &mut DocContext<'_>, did: DefId, kind: clean::TypeKind) {
     let crate_name = cx.tcx.crate_name(did.krate).to_string();
 
     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
@@ -181,9 +181,9 @@ crate fn record_extern_fqn(cx: &DocContext<'_>, did: DefId, kind: clean::TypeKin
     };
 
     if did.is_local() {
-        cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn);
+        cx.renderinfo.exact_paths.insert(did, fqn);
     } else {
-        cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
+        cx.renderinfo.external_paths.insert(did, (fqn, kind));
     }
 }
 
@@ -317,7 +317,7 @@ crate fn build_impl(
     attrs: Option<Attrs<'_>>,
     ret: &mut Vec<clean::Item>,
 ) {
-    if !cx.renderinfo.borrow_mut().inlined.insert(did) {
+    if !cx.renderinfo.inlined.insert(did) {
         return;
     }
 
@@ -329,7 +329,7 @@ crate fn build_impl(
     if !did.is_local() {
         if let Some(traitref) = associated_trait {
             let did = traitref.def_id;
-            if !cx.renderinfo.borrow().access_levels.is_public(did) {
+            if !cx.renderinfo.access_levels.is_public(did) {
                 return;
             }
 
@@ -361,7 +361,7 @@ crate fn build_impl(
     // reachable in rustdoc generated documentation
     if !did.is_local() {
         if let Some(did) = for_.def_id() {
-            if !cx.renderinfo.borrow().access_levels.is_public(did) {
+            if !cx.renderinfo.access_levels.is_public(did) {
                 return;
             }
 
@@ -613,20 +613,19 @@ crate fn record_extern_trait(cx: &mut DocContext<'_>, did: DefId) {
     }
 
     {
-        if cx.external_traits.borrow().contains_key(&did)
-            || cx.active_extern_traits.borrow().contains(&did)
+        if cx.external_traits.borrow().contains_key(&did) || cx.active_extern_traits.contains(&did)
         {
             return;
         }
     }
 
     {
-        cx.active_extern_traits.borrow_mut().insert(did);
+        cx.active_extern_traits.insert(did);
     }
 
     debug!("record_extern_trait: {:?}", did);
     let trait_ = build_external_trait(cx, did);
 
     cx.external_traits.borrow_mut().insert(did, trait_);
-    cx.active_extern_traits.borrow_mut().remove(&did);
+    cx.active_extern_traits.remove(&did);
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 98e1299df2f..b6e70462105 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -357,7 +357,7 @@ impl Clean<Lifetime> for hir::Lifetime {
                 | rl::Region::LateBound(_, node_id, _)
                 | rl::Region::Free(_, node_id),
             ) => {
-                if let Some(lt) = cx.lt_substs.borrow().get(&node_id).cloned() {
+                if let Some(lt) = cx.lt_substs.get(&node_id).cloned() {
                     return lt;
                 }
             }
@@ -644,7 +644,7 @@ impl Clean<Generics> for hir::Generics<'_> {
                 match param.kind {
                     GenericParamDefKind::Lifetime => unreachable!(),
                     GenericParamDefKind::Type { did, ref bounds, .. } => {
-                        cx.impl_trait_bounds.borrow_mut().insert(did.into(), bounds.clone());
+                        cx.impl_trait_bounds.insert(did.into(), bounds.clone());
                     }
                     GenericParamDefKind::Const { .. } => unreachable!(),
                 }
@@ -803,7 +803,7 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics, ty::GenericPredicates<'tcx
                 unreachable!();
             }
 
-            cx.impl_trait_bounds.borrow_mut().insert(param, bounds);
+            cx.impl_trait_bounds.insert(param, bounds);
         }
 
         // Now that `cx.impl_trait_bounds` is populated, we can process
@@ -1291,10 +1291,10 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
     match qpath {
         hir::QPath::Resolved(None, ref path) => {
             if let Res::Def(DefKind::TyParam, did) = path.res {
-                if let Some(new_ty) = cx.ty_substs.borrow().get(&did).cloned() {
+                if let Some(new_ty) = cx.ty_substs.get(&did).cloned() {
                     return new_ty;
                 }
-                if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&did.into()) {
+                if let Some(bounds) = cx.impl_trait_bounds.remove(&did.into()) {
                     return ImplTrait(bounds);
                 }
             }
@@ -1304,7 +1304,7 @@ fn clean_qpath(hir_ty: &hir::Ty<'_>, cx: &mut DocContext<'_>) -> Type {
                 // Substitute private type aliases
                 if let Some(def_id) = def_id.as_local() {
                     let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
-                    if !cx.renderinfo.borrow().access_levels.is_exported(def_id.to_def_id()) {
+                    if !cx.renderinfo.access_levels.is_exported(def_id.to_def_id()) {
                         alias = Some(&cx.tcx.hir().expect_item(hir_id).kind);
                     }
                 }
@@ -1651,7 +1651,7 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
             ty::Projection(ref data) => data.clean(cx),
 
             ty::Param(ref p) => {
-                if let Some(bounds) = cx.impl_trait_bounds.borrow_mut().remove(&p.index.into()) {
+                if let Some(bounds) = cx.impl_trait_bounds.remove(&p.index.into()) {
                     ImplTrait(bounds)
                 } else {
                     Generic(p.name)
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index c7bfd363a12..d2eee49f0c9 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -23,10 +23,9 @@ crate fn krate(mut cx: &mut DocContext<'_>) -> Crate {
     let krate = cx.tcx.hir().krate();
     let module = crate::visit_ast::RustdocVisitor::new(&mut cx).visit(krate);
 
-    let mut r = cx.renderinfo.get_mut();
-    r.deref_trait_did = cx.tcx.lang_items().deref_trait();
-    r.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait();
-    r.owned_box_did = cx.tcx.lang_items().owned_box();
+    cx.renderinfo.deref_trait_did = cx.tcx.lang_items().deref_trait();
+    cx.renderinfo.deref_mut_trait_did = cx.tcx.lang_items().deref_mut_trait();
+    cx.renderinfo.owned_box_did = cx.tcx.lang_items().owned_box();
 
     let mut externs = Vec::new();
     for &cnum in cx.tcx.crates().iter() {
@@ -494,10 +493,10 @@ crate fn enter_impl_trait<F, R>(cx: &mut DocContext<'_>, f: F) -> R
 where
     F: FnOnce(&mut DocContext<'_>) -> R,
 {
-    let old_bounds = mem::take(&mut *cx.impl_trait_bounds.get_mut());
+    let old_bounds = mem::take(&mut cx.impl_trait_bounds);
     let r = f(cx);
-    assert!(cx.impl_trait_bounds.borrow().is_empty());
-    *cx.impl_trait_bounds.get_mut() = old_bounds;
+    assert!(cx.impl_trait_bounds.is_empty());
+    cx.impl_trait_bounds = old_bounds;
     r
 }
 
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index dbf202a7321..8fceb00eeae 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -42,32 +42,37 @@ crate type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
 
 crate struct DocContext<'tcx> {
     crate tcx: TyCtxt<'tcx>,
+    /// Name resolver. Used for intra-doc links.
+    ///
+    /// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by
+    /// [`Queries::expansion()`].
+    // FIXME: see if we can get rid of this RefCell somehow
     crate resolver: Rc<RefCell<interface::BoxedResolver>>,
     /// Used for normalization.
     ///
     /// Most of this logic is copied from rustc_lint::late.
     crate param_env: ParamEnv<'tcx>,
     /// Later on moved into `cache`
-    crate renderinfo: RefCell<RenderInfo>,
+    crate renderinfo: RenderInfo,
     /// Later on moved through `clean::Crate` into `cache`
     crate external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>,
     /// Used while populating `external_traits` to ensure we don't process the same trait twice at
     /// the same time.
-    crate active_extern_traits: RefCell<FxHashSet<DefId>>,
+    crate active_extern_traits: FxHashSet<DefId>,
     // The current set of type and lifetime substitutions,
     // for expanding type aliases at the HIR level:
     /// Table `DefId` of type parameter -> substituted type
-    crate ty_substs: RefCell<FxHashMap<DefId, clean::Type>>,
+    crate ty_substs: FxHashMap<DefId, clean::Type>,
     /// Table `DefId` of lifetime parameter -> substituted lifetime
-    crate lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
+    crate lt_substs: FxHashMap<DefId, clean::Lifetime>,
     /// Table `DefId` of const parameter -> substituted const
-    crate ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>,
+    crate ct_substs: FxHashMap<DefId, clean::Constant>,
     /// Table synthetic type parameter for `impl Trait` in argument position -> bounds
-    crate impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>,
+    crate impl_trait_bounds: FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>,
     crate fake_def_ids: FxHashMap<CrateNum, DefIndex>,
     /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
     // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
-    crate generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
+    crate generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>,
     crate auto_traits: Vec<DefId>,
     /// The options given to rustdoc that could be relevant to a pass.
     crate render_options: RenderOptions,
@@ -112,14 +117,14 @@ impl<'tcx> DocContext<'tcx> {
         F: FnOnce(&mut Self) -> R,
     {
         let (old_tys, old_lts, old_cts) = (
-            mem::replace(&mut *self.ty_substs.get_mut(), ty_substs),
-            mem::replace(&mut *self.lt_substs.get_mut(), lt_substs),
-            mem::replace(&mut *self.ct_substs.get_mut(), ct_substs),
+            mem::replace(&mut self.ty_substs, ty_substs),
+            mem::replace(&mut self.lt_substs, lt_substs),
+            mem::replace(&mut self.ct_substs, ct_substs),
         );
         let r = f(self);
-        *self.ty_substs.get_mut() = old_tys;
-        *self.lt_substs.get_mut() = old_lts;
-        *self.ct_substs.get_mut() = old_cts;
+        self.ty_substs = old_tys;
+        self.lt_substs = old_lts;
+        self.ct_substs = old_cts;
         r
     }
 
@@ -509,7 +514,7 @@ crate fn run_global_ctxt(
         param_env: ParamEnv::empty(),
         external_traits: Default::default(),
         active_extern_traits: Default::default(),
-        renderinfo: RefCell::new(renderinfo),
+        renderinfo,
         ty_substs: Default::default(),
         lt_substs: Default::default(),
         ct_substs: Default::default(),
@@ -642,7 +647,7 @@ crate fn run_global_ctxt(
     // The main crate doc comments are always collapsed.
     krate.collapsed = true;
 
-    (krate, ctxt.renderinfo.into_inner(), ctxt.render_options)
+    (krate, ctxt.renderinfo, ctxt.render_options)
 }
 
 /// Due to <https://github.com/rust-lang/rust/pull/73566>,
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 542cf6d2c27..c3365b844ec 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -127,7 +127,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
     }
 
     fn print_results(&self) {
-        let output_format = self.ctx.renderinfo.borrow().output_format;
+        let output_format = self.ctx.renderinfo.output_format;
         if output_format.is_json() {
             println!("{}", self.to_json());
             return;
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 0b6d81d1b44..0271a5b78a7 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -48,11 +48,10 @@ crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
                 if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) {
                     let self_ty = cx.tcx.type_of(def_id);
                     let impls = get_auto_trait_and_blanket_impls(cx, self_ty, def_id);
-                    let mut renderinfo = cx.renderinfo.borrow_mut();
 
-                    new_items.extend(impls.filter(|i| renderinfo.inlined.insert(i.def_id)));
+                    new_items.extend(impls.filter(|i| cx.renderinfo.inlined.insert(i.def_id)));
                 }
-            })
+            });
         }
     }
 
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 042a895d2fa..e8e1bead84f 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -97,8 +97,7 @@ crate fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
                 |lint| lint.build("missing code example in this documentation").emit(),
             );
         }
-    } else if tests.found_tests > 0 && !cx.renderinfo.borrow().access_levels.is_public(item.def_id)
-    {
+    } else if tests.found_tests > 0 && !cx.renderinfo.access_levels.is_public(item.def_id) {
         cx.tcx.struct_span_lint_hir(
             lint::builtin::PRIVATE_DOC_TESTS,
             hir_id,
diff --git a/src/librustdoc/passes/strip_private.rs b/src/librustdoc/passes/strip_private.rs
index c0bb05af3ed..f83eab6799e 100644
--- a/src/librustdoc/passes/strip_private.rs
+++ b/src/librustdoc/passes/strip_private.rs
@@ -17,7 +17,7 @@ crate const STRIP_PRIVATE: Pass = Pass {
 crate fn strip_private(mut krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
     // This stripper collects all *retained* nodes.
     let mut retained = DefIdSet::default();
-    let access_levels = cx.renderinfo.borrow().access_levels.clone();
+    let access_levels = cx.renderinfo.access_levels.clone();
 
     // strip all private items
     {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e92ea55caa7..4d42c181d8c 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -113,7 +113,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             assert_eq!(cur_mod_def_id, macro_parent_def_id);
             cur_mod.macros.push((def, None));
         }
-        self.cx.renderinfo.get_mut().exact_paths = self.exact_paths;
+        self.cx.renderinfo.exact_paths = self.exact_paths;
         top_level_module
     }
 
@@ -199,12 +199,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     } else {
                         // All items need to be handled here in case someone wishes to link
                         // to them with intra-doc links
-                        self.cx
-                            .renderinfo
-                            .get_mut()
-                            .access_levels
-                            .map
-                            .insert(did, AccessLevel::Public);
+                        self.cx.renderinfo.access_levels.map.insert(did, AccessLevel::Public);
                     }
                 }
             }
@@ -216,7 +211,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             None => return false,
         };
 
-        let is_private = !self.cx.renderinfo.borrow().access_levels.is_public(res_did);
+        let is_private = !self.cx.renderinfo.access_levels.is_public(res_did);
         let is_hidden = inherits_doc_hidden(self.cx, res_hir_id);
 
         // Only inline if requested or if the item would otherwise be stripped.
diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs
index 0bf22562eae..daed5bd107d 100644
--- a/src/librustdoc/visit_lib.rs
+++ b/src/librustdoc/visit_lib.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> LibEmbargoVisitor<'a, 'tcx> {
     crate fn new(cx: &'a mut crate::core::DocContext<'tcx>) -> LibEmbargoVisitor<'a, 'tcx> {
         LibEmbargoVisitor {
             tcx: cx.tcx,
-            access_levels: &mut cx.renderinfo.get_mut().access_levels,
+            access_levels: &mut cx.renderinfo.access_levels,
             prev_level: Some(AccessLevel::Public),
             visited_mods: FxHashSet::default(),
         }
diff --git a/src/test/rustdoc-json/nested.rs b/src/test/rustdoc-json/nested.rs
index 7e705255d98..a3d4935f496 100644
--- a/src/test/rustdoc-json/nested.rs
+++ b/src/test/rustdoc-json/nested.rs
@@ -1,24 +1,28 @@
 // edition:2018
 
-// @has nested.json "$.index[*][?(@.name=='nested')].kind" \"module\"
-// @has - "$.index[*][?(@.name=='nested')].inner.is_crate" true
+// @is nested.json "$.index[*][?(@.name=='nested')].kind" \"module\"
+// @is - "$.index[*][?(@.name=='nested')].inner.is_crate" true
 // @count - "$.index[*][?(@.name=='nested')].inner.items[*]" 1
 
-// @has nested.json "$.index[*][?(@.name=='l1')].kind" \"module\"
-// @has - "$.index[*][?(@.name=='l1')].inner.is_crate" false
+// @is nested.json "$.index[*][?(@.name=='l1')].kind" \"module\"
+// @is - "$.index[*][?(@.name=='l1')].inner.is_crate" false
 // @count - "$.index[*][?(@.name=='l1')].inner.items[*]" 2
 pub mod l1 {
 
-    // @has nested.json "$.index[*][?(@.name=='l3')].kind" \"module\"
-    // @has - "$.index[*][?(@.name=='l3')].inner.is_crate" false
+    // @is nested.json "$.index[*][?(@.name=='l3')].kind" \"module\"
+    // @is - "$.index[*][?(@.name=='l3')].inner.is_crate" false
     // @count - "$.index[*][?(@.name=='l3')].inner.items[*]" 1
+    // @set l3_id = - "$.index[*][?(@.name=='l3')].id"
+    // @has - "$.index[*][?(@.name=='l1')].inner.items[*]" $l3_id
     pub mod l3 {
 
-        // @has nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\"
-        // @has - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\"
+        // @is nested.json "$.index[*][?(@.name=='L4')].kind" \"struct\"
+        // @is - "$.index[*][?(@.name=='L4')].inner.struct_type" \"unit\"
+        // @set l4_id = - "$.index[*][?(@.name=='L4')].id"
+        // @has - "$.index[*][?(@.name=='l3')].inner.items[*]" $l4_id
         pub struct L4;
     }
-    // @has nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\"
-    // @has - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false
+    // @is nested.json "$.index[*][?(@.inner.span=='l3::L4')].kind" \"import\"
+    // @is - "$.index[*][?(@.inner.span=='l3::L4')].inner.glob" false
     pub use l3::L4;
 }
diff --git a/src/test/ui/borrowck/issue-81365-1.rs b/src/test/ui/borrowck/issue-81365-1.rs
new file mode 100644
index 00000000000..8e212a77016
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-1.rs
@@ -0,0 +1,26 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &self.target_field;
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-1.stderr b/src/test/ui/borrowck/issue-81365-1.stderr
new file mode 100644
index 00000000000..ef88d7f14a3
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-1.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-1.rs:21:9
+   |
+LL |         let first = &self.target_field;
+   |                      ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `DerefTarget`
+note: deref defined here
+  --> $DIR/issue-81365-1.rs:12:5
+   |
+LL |     type Target = DerefTarget;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-10.rs b/src/test/ui/borrowck/issue-81365-10.rs
new file mode 100644
index 00000000000..7602e184a9f
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-10.rs
@@ -0,0 +1,26 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &self.deref().target_field;
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-10.stderr b/src/test/ui/borrowck/issue-81365-10.stderr
new file mode 100644
index 00000000000..891f70ed7f6
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-10.stderr
@@ -0,0 +1,13 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-10.rs:21:9
+   |
+LL |         let first = &self.deref().target_field;
+   |                      ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-11.rs b/src/test/ui/borrowck/issue-81365-11.rs
new file mode 100644
index 00000000000..6b558c65d3d
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-11.rs
@@ -0,0 +1,32 @@
+use std::ops::{Deref, DerefMut};
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl DerefMut for Container {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &mut self.target_field;
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-11.stderr b/src/test/ui/borrowck/issue-81365-11.stderr
new file mode 100644
index 00000000000..0770c136632
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-11.stderr
@@ -0,0 +1,13 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-11.rs:27:9
+   |
+LL |         let first = &mut self.target_field;
+   |                          ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-2.rs b/src/test/ui/borrowck/issue-81365-2.rs
new file mode 100644
index 00000000000..fbbdd93b97c
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-2.rs
@@ -0,0 +1,30 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+struct Outer {
+    container: Container,
+}
+
+impl Outer {
+    fn bad_borrow(&mut self) {
+        let first = &self.container.target_field;
+        self.container.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-2.stderr b/src/test/ui/borrowck/issue-81365-2.stderr
new file mode 100644
index 00000000000..e71edb50964
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-2.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container.container_field` because it is borrowed
+  --> $DIR/issue-81365-2.rs:25:9
+   |
+LL |         let first = &self.container.target_field;
+   |                      -------------- borrow of `self.container.container_field` occurs here
+LL |         self.container.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `DerefTarget`
+note: deref defined here
+  --> $DIR/issue-81365-2.rs:12:5
+   |
+LL |     type Target = DerefTarget;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-3.rs b/src/test/ui/borrowck/issue-81365-3.rs
new file mode 100644
index 00000000000..9a9e3a31358
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-3.rs
@@ -0,0 +1,37 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+struct Outer {
+    container: Container,
+}
+
+impl Deref for Outer {
+    type Target = Container;
+    fn deref(&self) -> &Self::Target {
+        &self.container
+    }
+}
+
+impl Outer {
+    fn bad_borrow(&mut self) {
+        let first = &self.target_field;
+        self.container.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-3.stderr b/src/test/ui/borrowck/issue-81365-3.stderr
new file mode 100644
index 00000000000..70bb6bb93a9
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-3.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container.container_field` because it is borrowed
+  --> $DIR/issue-81365-3.rs:32:9
+   |
+LL |         let first = &self.target_field;
+   |                      ---- borrow of `self.container.container_field` occurs here
+LL |         self.container.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `Container`
+note: deref defined here
+  --> $DIR/issue-81365-3.rs:23:5
+   |
+LL |     type Target = Container;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-4.rs b/src/test/ui/borrowck/issue-81365-4.rs
new file mode 100644
index 00000000000..b2643eb3358
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-4.rs
@@ -0,0 +1,38 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+struct Outer {
+    container: Container,
+    outer_field: bool,
+}
+
+impl Deref for Outer {
+    type Target = Container;
+    fn deref(&self) -> &Self::Target {
+        &self.container
+    }
+}
+
+impl Outer {
+    fn bad_borrow(&mut self) {
+        let first = &self.target_field;
+        self.outer_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-4.stderr b/src/test/ui/borrowck/issue-81365-4.stderr
new file mode 100644
index 00000000000..e714bb86d1c
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-4.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.outer_field` because it is borrowed
+  --> $DIR/issue-81365-4.rs:33:9
+   |
+LL |         let first = &self.target_field;
+   |                      ---- borrow of `self.outer_field` occurs here
+LL |         self.outer_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.outer_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `Container`
+note: deref defined here
+  --> $DIR/issue-81365-4.rs:24:5
+   |
+LL |     type Target = Container;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-5.rs b/src/test/ui/borrowck/issue-81365-5.rs
new file mode 100644
index 00000000000..d36b79615e3
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-5.rs
@@ -0,0 +1,33 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+
+impl DerefTarget {
+    fn get(&self) -> &bool {
+        &self.target_field
+    }
+}
+
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = self.get();
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-5.stderr b/src/test/ui/borrowck/issue-81365-5.stderr
new file mode 100644
index 00000000000..7c0e9f43bd0
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-5.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-5.rs:28:9
+   |
+LL |         let first = self.get();
+   |                     ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `DerefTarget`
+note: deref defined here
+  --> $DIR/issue-81365-5.rs:19:5
+   |
+LL |     type Target = DerefTarget;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-6.rs b/src/test/ui/borrowck/issue-81365-6.rs
new file mode 100644
index 00000000000..85ea77756b3
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-6.rs
@@ -0,0 +1,23 @@
+use std::ops::Deref;
+
+struct Container {
+    target: Vec<()>,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = [()];
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &self[0];
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-6.stderr b/src/test/ui/borrowck/issue-81365-6.stderr
new file mode 100644
index 00000000000..85ed6acca3d
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-6.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-6.rs:18:9
+   |
+LL |         let first = &self[0];
+   |                      ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `[()]`
+note: deref defined here
+  --> $DIR/issue-81365-6.rs:9:5
+   |
+LL |     type Target = [()];
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-7.rs b/src/test/ui/borrowck/issue-81365-7.rs
new file mode 100644
index 00000000000..cbf70f11a9a
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-7.rs
@@ -0,0 +1,24 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+fn bad_borrow(c: &mut Container) {
+    let first = &c.target_field;
+    c.container_field = true; //~ ERROR E0506
+    first;
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-7.stderr b/src/test/ui/borrowck/issue-81365-7.stderr
new file mode 100644
index 00000000000..506732ec0c5
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-7.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `c.container_field` because it is borrowed
+  --> $DIR/issue-81365-7.rs:20:5
+   |
+LL |     let first = &c.target_field;
+   |                  - borrow of `c.container_field` occurs here
+LL |     c.container_field = true;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `c.container_field` occurs here
+LL |     first;
+   |     ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `DerefTarget`
+note: deref defined here
+  --> $DIR/issue-81365-7.rs:12:5
+   |
+LL |     type Target = DerefTarget;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-8.rs b/src/test/ui/borrowck/issue-81365-8.rs
new file mode 100644
index 00000000000..0bb1033fb42
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-8.rs
@@ -0,0 +1,26 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &(*self).target_field;
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-8.stderr b/src/test/ui/borrowck/issue-81365-8.stderr
new file mode 100644
index 00000000000..716b6e9b51f
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-8.stderr
@@ -0,0 +1,20 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-8.rs:21:9
+   |
+LL |         let first = &(*self).target_field;
+   |                      ------- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+   |
+   = note: borrow occurs due to deref coercion to `DerefTarget`
+note: deref defined here
+  --> $DIR/issue-81365-8.rs:12:5
+   |
+LL |     type Target = DerefTarget;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/borrowck/issue-81365-9.rs b/src/test/ui/borrowck/issue-81365-9.rs
new file mode 100644
index 00000000000..cd57afa288d
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-9.rs
@@ -0,0 +1,26 @@
+use std::ops::Deref;
+
+struct DerefTarget {
+    target_field: bool,
+}
+struct Container {
+    target: DerefTarget,
+    container_field: bool,
+}
+
+impl Deref for Container {
+    type Target = DerefTarget;
+    fn deref(&self) -> &Self::Target {
+        &self.target
+    }
+}
+
+impl Container {
+    fn bad_borrow(&mut self) {
+        let first = &Deref::deref(self).target_field;
+        self.container_field = true; //~ ERROR E0506
+        first;
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/borrowck/issue-81365-9.stderr b/src/test/ui/borrowck/issue-81365-9.stderr
new file mode 100644
index 00000000000..c7d48214fd4
--- /dev/null
+++ b/src/test/ui/borrowck/issue-81365-9.stderr
@@ -0,0 +1,13 @@
+error[E0506]: cannot assign to `self.container_field` because it is borrowed
+  --> $DIR/issue-81365-9.rs:21:9
+   |
+LL |         let first = &Deref::deref(self).target_field;
+   |                                   ---- borrow of `self.container_field` occurs here
+LL |         self.container_field = true;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+LL |         first;
+   |         ----- borrow later used here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0506`.
diff --git a/src/test/ui/did_you_mean/pub-macro-rules.rs b/src/test/ui/did_you_mean/pub-macro-rules.rs
deleted file mode 100644
index c5393703f70..00000000000
--- a/src/test/ui/did_you_mean/pub-macro-rules.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#[macro_use] mod bleh {
-    pub macro_rules! foo { //~ ERROR can't qualify macro_rules invocation
-        ($n:ident) => (
-            fn $n () -> i32 {
-                1
-            }
-        )
-    }
-
-}
-
-foo!(meh);
-
-fn main() {
-    println!("{}", meh());
-}
diff --git a/src/test/ui/did_you_mean/pub-macro-rules.stderr b/src/test/ui/did_you_mean/pub-macro-rules.stderr
deleted file mode 100644
index 0bde5783b8c..00000000000
--- a/src/test/ui/did_you_mean/pub-macro-rules.stderr
+++ /dev/null
@@ -1,8 +0,0 @@
-error: can't qualify macro_rules invocation with `pub`
-  --> $DIR/pub-macro-rules.rs:2:5
-   |
-LL |     pub macro_rules! foo {
-   |     ^^^ help: try exporting the macro: `#[macro_export]`
-
-error: aborting due to previous error
-
diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs
new file mode 100644
index 00000000000..5504ec317ae
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.rs
@@ -0,0 +1,10 @@
+pub macro_rules! m1 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
+
+#[cfg(FALSE)]
+pub macro_rules! m2 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
+
+pub(crate) macro_rules! m3 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
+
+pub(in self) macro_rules! m4 { () => {} } //~ ERROR `pub` on `macro_rules` items is unstable
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr
new file mode 100644
index 00000000000..bfaec398d9a
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-pub_macro_rules.stderr
@@ -0,0 +1,39 @@
+error[E0658]: `pub` on `macro_rules` items is unstable
+  --> $DIR/feature-gate-pub_macro_rules.rs:1:1
+   |
+LL | pub macro_rules! m1 { () => {} }
+   | ^^^
+   |
+   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
+   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
+
+error[E0658]: `pub` on `macro_rules` items is unstable
+  --> $DIR/feature-gate-pub_macro_rules.rs:4:1
+   |
+LL | pub macro_rules! m2 { () => {} }
+   | ^^^
+   |
+   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
+   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
+
+error[E0658]: `pub` on `macro_rules` items is unstable
+  --> $DIR/feature-gate-pub_macro_rules.rs:6:1
+   |
+LL | pub(crate) macro_rules! m3 { () => {} }
+   | ^^^^^^^^^^
+   |
+   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
+   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
+
+error[E0658]: `pub` on `macro_rules` items is unstable
+  --> $DIR/feature-gate-pub_macro_rules.rs:8:1
+   |
+LL | pub(in self) macro_rules! m4 { () => {} }
+   | ^^^^^^^^^^^^
+   |
+   = note: see issue #78855 <https://github.com/rust-lang/rust/issues/78855> for more information
+   = help: add `#![feature(pub_macro_rules)]` to the crate attributes to enable
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/issues/issue-81918.rs b/src/test/ui/issues/issue-81918.rs
new file mode 100644
index 00000000000..8938b8a6f2c
--- /dev/null
+++ b/src/test/ui/issues/issue-81918.rs
@@ -0,0 +1,11 @@
+// check-pass
+// dont-check-compiler-stdout
+// compile-flags: -Z unpretty=mir-cfg
+
+// This checks that unpretty=mir-cfg does not panic. See #81918.
+
+const TAG: &'static str = "ABCD";
+
+fn main() {
+    if TAG == "" {}
+}
diff --git a/src/test/ui/lint/dead-code/write-only-field.rs b/src/test/ui/lint/dead-code/write-only-field.rs
index 78cfcfda8f9..7b3f1e9f5b6 100644
--- a/src/test/ui/lint/dead-code/write-only-field.rs
+++ b/src/test/ui/lint/dead-code/write-only-field.rs
@@ -17,4 +17,53 @@ fn field_write(s: &mut S) {
 fn main() {
     let mut s = S { f: 0, sub: Sub { f: 0 } };
     field_write(&mut s);
+
+    auto_deref();
+    nested_boxes();
+}
+
+fn auto_deref() {
+    struct E {
+        x: bool,
+        y: bool, //~ ERROR: field is never read
+    }
+
+    struct P<'a> {
+        e: &'a mut E
+    }
+
+    impl P<'_> {
+        fn f(&mut self) {
+            self.e.x = true;
+            self.e.y = true;
+        }
+    }
+
+    let mut e = E { x: false, y: false };
+    let mut p = P { e: &mut e };
+    p.f();
+    assert!(e.x);
+}
+
+fn nested_boxes() {
+    struct A {
+        b: Box<B>,
+    }
+
+    struct B {
+        c: Box<C>,
+    }
+
+    struct C {
+        u: u32, //~ ERROR: field is never read
+        v: u32, //~ ERROR: field is never read
+    }
+
+    let mut a = A {
+        b: Box::new(B {
+            c: Box::new(C { u: 0, v: 0 }),
+        }),
+    };
+    a.b.c.v = 10;
+    a.b.c = Box::new(C { u: 1, v: 2 });
 }
diff --git a/src/test/ui/lint/dead-code/write-only-field.stderr b/src/test/ui/lint/dead-code/write-only-field.stderr
index 70d2149665b..a191d22c8b9 100644
--- a/src/test/ui/lint/dead-code/write-only-field.stderr
+++ b/src/test/ui/lint/dead-code/write-only-field.stderr
@@ -22,5 +22,23 @@ error: field is never read: `f`
 LL |     f: i32,
    |     ^^^^^^
 
-error: aborting due to 3 previous errors
+error: field is never read: `y`
+  --> $DIR/write-only-field.rs:28:9
+   |
+LL |         y: bool,
+   |         ^^^^^^^
+
+error: field is never read: `u`
+  --> $DIR/write-only-field.rs:58:9
+   |
+LL |         u: u32,
+   |         ^^^^^^
+
+error: field is never read: `v`
+  --> $DIR/write-only-field.rs:59:9
+   |
+LL |         v: u32,
+   |         ^^^^^^
+
+error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.rs b/src/test/ui/macros/macro-export-on-modularized-macros.rs
new file mode 100644
index 00000000000..467c6ba7b78
--- /dev/null
+++ b/src/test/ui/macros/macro-export-on-modularized-macros.rs
@@ -0,0 +1,11 @@
+#![feature(decl_macro)]
+#![feature(pub_macro_rules)]
+
+#[macro_export]
+macro m1() {} //~ ERROR `#[macro_export]` cannot be used on `macro` items
+
+#[macro_export]
+pub macro_rules! m2 { () => {} }
+//~^ ERROR `#[macro_export]` cannot be used on `macro_rules` with `pub`
+
+fn main() {}
diff --git a/src/test/ui/macros/macro-export-on-modularized-macros.stderr b/src/test/ui/macros/macro-export-on-modularized-macros.stderr
new file mode 100644
index 00000000000..8bb031e12cb
--- /dev/null
+++ b/src/test/ui/macros/macro-export-on-modularized-macros.stderr
@@ -0,0 +1,14 @@
+error: `#[macro_export]` cannot be used on `macro` items
+  --> $DIR/macro-export-on-modularized-macros.rs:5:1
+   |
+LL | macro m1() {}
+   | ^^^^^^^^^^^^^
+
+error: `#[macro_export]` cannot be used on `macro_rules` with `pub`
+  --> $DIR/macro-export-on-modularized-macros.rs:8:1
+   |
+LL | pub macro_rules! m2 { () => {} }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/macros/pub-macro-rules-fail.rs b/src/test/ui/macros/pub-macro-rules-fail.rs
new file mode 100644
index 00000000000..bdb4c73f18b
--- /dev/null
+++ b/src/test/ui/macros/pub-macro-rules-fail.rs
@@ -0,0 +1,28 @@
+#![feature(pub_macro_rules)]
+
+#[macro_use]
+mod m {
+    pub macro_rules! mac { () => {} }
+
+    // `pub` `macro_rules` cannot be redefined in the same module.
+    pub macro_rules! mac { () => {} } //~ ERROR the name `mac` is defined multiple times
+
+    pub(self) macro_rules! private_mac { () => {} }
+}
+
+const _: () = {
+    pub macro_rules! block_mac { () => {} }
+};
+
+mod n {
+    // Scope of `pub` `macro_rules` is not extended by `#[macro_use]`.
+    mac!(); //~ ERROR cannot find macro `mac` in this scope
+
+    // `pub` `macro_rules` doesn't put the macro into the root module, unlike `#[macro_export]`.
+    crate::mac!(); //~ ERROR failed to resolve: maybe a missing crate `mac`
+    crate::block_mac!(); //~ ERROR failed to resolve: maybe a missing crate `block_mac`
+
+    crate::m::private_mac!(); //~ ERROR macro `private_mac` is private
+}
+
+fn main() {}
diff --git a/src/test/ui/macros/pub-macro-rules-fail.stderr b/src/test/ui/macros/pub-macro-rules-fail.stderr
new file mode 100644
index 00000000000..588d79dd76a
--- /dev/null
+++ b/src/test/ui/macros/pub-macro-rules-fail.stderr
@@ -0,0 +1,48 @@
+error[E0428]: the name `mac` is defined multiple times
+  --> $DIR/pub-macro-rules-fail.rs:8:5
+   |
+LL |     pub macro_rules! mac { () => {} }
+   |     -------------------- previous definition of the macro `mac` here
+...
+LL |     pub macro_rules! mac { () => {} }
+   |     ^^^^^^^^^^^^^^^^^^^^ `mac` redefined here
+   |
+   = note: `mac` must be defined only once in the macro namespace of this module
+
+error[E0433]: failed to resolve: maybe a missing crate `mac`?
+  --> $DIR/pub-macro-rules-fail.rs:22:12
+   |
+LL |     crate::mac!();
+   |            ^^^ maybe a missing crate `mac`?
+
+error[E0433]: failed to resolve: maybe a missing crate `block_mac`?
+  --> $DIR/pub-macro-rules-fail.rs:23:12
+   |
+LL |     crate::block_mac!();
+   |            ^^^^^^^^^ maybe a missing crate `block_mac`?
+
+error: cannot find macro `mac` in this scope
+  --> $DIR/pub-macro-rules-fail.rs:19:5
+   |
+LL |     mac!();
+   |     ^^^
+   |
+   = note: consider importing this macro:
+           m::mac
+
+error[E0603]: macro `private_mac` is private
+  --> $DIR/pub-macro-rules-fail.rs:25:15
+   |
+LL |     crate::m::private_mac!();
+   |               ^^^^^^^^^^^ private macro
+   |
+note: the macro `private_mac` is defined here
+  --> $DIR/pub-macro-rules-fail.rs:10:5
+   |
+LL |     pub(self) macro_rules! private_mac { () => {} }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0428, E0433, E0603.
+For more information about an error, try `rustc --explain E0428`.
diff --git a/src/test/ui/macros/pub-macro-rules.rs b/src/test/ui/macros/pub-macro-rules.rs
new file mode 100644
index 00000000000..cd4a845f7c0
--- /dev/null
+++ b/src/test/ui/macros/pub-macro-rules.rs
@@ -0,0 +1,20 @@
+// check-pass
+
+#![feature(pub_macro_rules)]
+
+mod m {
+    // `pub` `macro_rules` can be used earlier in item order than they are defined.
+    foo!();
+
+    pub macro_rules! foo { () => {} }
+
+    // `pub(...)` works too.
+    pub(super) macro_rules! bar { () => {} }
+}
+
+// `pub` `macro_rules` are available by module path.
+m::foo!();
+
+m::bar!();
+
+fn main() {}
diff --git a/src/test/ui/non-fmt-panic.rs b/src/test/ui/non-fmt-panic.rs
index 25c53316e12..c80a90b3eaa 100644
--- a/src/test/ui/non-fmt-panic.rs
+++ b/src/test/ui/non-fmt-panic.rs
@@ -29,6 +29,17 @@ fn main() {
     fancy_panic::fancy_panic!(S);
     //~^ WARN panic message is not a string literal
 
+    macro_rules! a {
+        () => { 123 };
+    }
+
+    panic!(a!()); //~ WARN panic message is not a string literal
+
+    panic!(format!("{}", 1)); //~ WARN panic message is not a string literal
+
+    panic![123]; //~ WARN panic message is not a string literal
+    panic!{123}; //~ WARN panic message is not a string literal
+
     // Check that the lint only triggers for std::panic and core::panic,
     // not any panic macro:
     macro_rules! panic {
diff --git a/src/test/ui/non-fmt-panic.stderr b/src/test/ui/non-fmt-panic.stderr
index 45187c518c4..7a333b3e76a 100644
--- a/src/test/ui/non-fmt-panic.stderr
+++ b/src/test/ui/non-fmt-panic.stderr
@@ -93,7 +93,7 @@ LL |     panic!("{}", C);
 help: or use std::panic::panic_any instead
    |
 LL |     std::panic::panic_any(C);
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^
 
 warning: panic message is not a string literal
   --> $DIR/non-fmt-panic.rs:20:12
@@ -109,7 +109,7 @@ LL |     panic!("{}", S);
 help: or use std::panic::panic_any instead
    |
 LL |     std::panic::panic_any(S);
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^
 
 warning: panic message is not a string literal
   --> $DIR/non-fmt-panic.rs:21:17
@@ -125,7 +125,7 @@ LL |     std::panic!("{}", 123);
 help: or use std::panic::panic_any instead
    |
 LL |     std::panic::panic_any(123);
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^
 
 warning: panic message is not a string literal
   --> $DIR/non-fmt-panic.rs:22:18
@@ -183,5 +183,66 @@ LL |     fancy_panic::fancy_panic!(S);
    |
    = note: this is no longer accepted in Rust 2021
 
-warning: 14 warnings emitted
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:36:12
+   |
+LL |     panic!(a!());
+   |            ^^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+help: add a "{}" format string to Display the message
+   |
+LL |     panic!("{}", a!());
+   |            ^^^^^
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(a!());
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:38:12
+   |
+LL |     panic!(format!("{}", 1));
+   |            ^^^^^^^^^^^^^^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+   = note: the panic!() macro supports formatting, so there's no need for the format!() macro here
+help: remove the `format!(..)` macro call
+   |
+LL |     panic!("{}", 1);
+   |           --     --
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:40:12
+   |
+LL |     panic![123];
+   |            ^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+help: add a "{}" format string to Display the message
+   |
+LL |     panic!["{}", 123];
+   |            ^^^^^
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(123);
+   |     ^^^^^^^^^^^^^^^^^^^^^^   ^
+
+warning: panic message is not a string literal
+  --> $DIR/non-fmt-panic.rs:41:12
+   |
+LL |     panic!{123};
+   |            ^^^
+   |
+   = note: this is no longer accepted in Rust 2021
+help: add a "{}" format string to Display the message
+   |
+LL |     panic!{"{}", 123};
+   |            ^^^^^
+help: or use std::panic::panic_any instead
+   |
+LL |     std::panic::panic_any(123);
+   |     ^^^^^^^^^^^^^^^^^^^^^^   ^
+
+warning: 18 warnings emitted
 
diff --git a/src/test/ui/pattern/issue-82290.rs b/src/test/ui/pattern/issue-82290.rs
new file mode 100644
index 00000000000..67f0274fe74
--- /dev/null
+++ b/src/test/ui/pattern/issue-82290.rs
@@ -0,0 +1,7 @@
+#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete
+
+fn main() {
+    if true && let x = 1 { //~ ERROR `let` expressions are not supported here
+        let _ = x;
+    }
+}
diff --git a/src/test/ui/pattern/issue-82290.stderr b/src/test/ui/pattern/issue-82290.stderr
new file mode 100644
index 00000000000..65ef018dc97
--- /dev/null
+++ b/src/test/ui/pattern/issue-82290.stderr
@@ -0,0 +1,20 @@
+error: `let` expressions are not supported here
+  --> $DIR/issue-82290.rs:4:16
+   |
+LL |     if true && let x = 1 {
+   |                ^^^^^^^^^
+   |
+   = note: only supported directly in conditions of `if`- and `while`-expressions
+   = note: as well as when nested within `&&` and parenthesis in those conditions
+
+warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-82290.rs:1:12
+   |
+LL | #![feature(let_chains)]
+   |            ^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs
index b742f0eb3ee..8a6a911321c 100644
--- a/src/tools/jsondocck/src/cache.rs
+++ b/src/tools/jsondocck/src/cache.rs
@@ -9,6 +9,7 @@ pub struct Cache {
     root: PathBuf,
     files: HashMap<PathBuf, String>,
     values: HashMap<PathBuf, Value>,
+    pub variables: HashMap<String, Value>,
     last_path: Option<PathBuf>,
 }
 
@@ -19,6 +20,7 @@ impl Cache {
             root: Path::new(doc_dir).to_owned(),
             files: HashMap::new(),
             values: HashMap::new(),
+            variables: HashMap::new(),
             last_path: None,
         }
     }
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index 6ec292aba64..5020a4917a0 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -2,6 +2,7 @@ use jsonpath_lib::select;
 use lazy_static::lazy_static;
 use regex::{Regex, RegexBuilder};
 use serde_json::Value;
+use std::borrow::Cow;
 use std::{env, fmt, fs};
 
 mod cache;
@@ -48,13 +49,16 @@ pub struct Command {
 pub enum CommandKind {
     Has,
     Count,
+    Is,
+    Set,
 }
 
 impl CommandKind {
     fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
         let count = match self {
             CommandKind::Has => (1..=3).contains(&args.len()),
-            CommandKind::Count => 3 == args.len(),
+            CommandKind::Count | CommandKind::Is => 3 == args.len(),
+            CommandKind::Set => 4 == args.len(),
         };
 
         if !count {
@@ -83,6 +87,8 @@ impl fmt::Display for CommandKind {
         let text = match self {
             CommandKind::Has => "has",
             CommandKind::Count => "count",
+            CommandKind::Is => "is",
+            CommandKind::Set => "set",
         };
         write!(f, "{}", text)
     }
@@ -127,6 +133,8 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
         let cmd = match cmd {
             "has" => CommandKind::Has,
             "count" => CommandKind::Count,
+            "is" => CommandKind::Is,
+            "set" => CommandKind::Set,
             _ => {
                 print_err(&format!("Unrecognized command name `@{}`", cmd), lineno);
                 errors = true;
@@ -180,6 +188,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
 /// Performs the actual work of ensuring a command passes. Generally assumes the command
 /// is syntactically valid.
 fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
+    // FIXME: Be more granular about why, (e.g. syntax error, count not equal)
     let result = match command.kind {
         CommandKind::Has => {
             match command.args.len() {
@@ -188,23 +197,15 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
                 // @has <path> <jsonpath> = check path exists
                 2 => {
                     let val = cache.get_value(&command.args[0])?;
-
-                    match select(&val, &command.args[1]) {
-                        Ok(results) => !results.is_empty(),
-                        Err(_) => false,
-                    }
+                    let results = select(&val, &command.args[1]).unwrap();
+                    !results.is_empty()
                 }
                 // @has <path> <jsonpath> <value> = check *any* item matched by path equals value
                 3 => {
                     let val = cache.get_value(&command.args[0])?;
-                    match select(&val, &command.args[1]) {
-                        Ok(results) => {
-                            let pat: Value = serde_json::from_str(&command.args[2]).unwrap();
-
-                            !results.is_empty() && results.into_iter().any(|val| *val == pat)
-                        }
-                        Err(_) => false,
-                    }
+                    let results = select(&val, &command.args[1]).unwrap();
+                    let pat = string_to_value(&command.args[2], cache);
+                    results.contains(&pat.as_ref())
                 }
                 _ => unreachable!(),
             }
@@ -215,9 +216,37 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
             let expected: usize = command.args[2].parse().unwrap();
 
             let val = cache.get_value(&command.args[0])?;
-            match select(&val, &command.args[1]) {
-                Ok(results) => results.len() == expected,
-                Err(_) => false,
+            let results = select(&val, &command.args[1]).unwrap();
+            results.len() == expected
+        }
+        CommandKind::Is => {
+            // @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
+            assert_eq!(command.args.len(), 3);
+            let val = cache.get_value(&command.args[0])?;
+            let results = select(&val, &command.args[1]).unwrap();
+            let pat = string_to_value(&command.args[2], cache);
+            results.len() == 1 && results[0] == pat.as_ref()
+        }
+        CommandKind::Set => {
+            // @set <name> = <path> <jsonpath>
+            assert_eq!(command.args.len(), 4);
+            assert_eq!(command.args[1], "=", "Expected an `=`");
+            let val = cache.get_value(&command.args[2])?;
+            let results = select(&val, &command.args[3]).unwrap();
+            assert_eq!(results.len(), 1);
+            match results.len() {
+                0 => false,
+                1 => {
+                    let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
+                    assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
+                    true
+                }
+                _ => {
+                    panic!(
+                        "Got multiple results in `@set` for `{}`: {:?}",
+                        &command.args[3], results
+                    );
+                }
             }
         }
     };
@@ -247,3 +276,11 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
         Ok(())
     }
 }
+
+fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
+    if s.starts_with("$") {
+        Cow::Borrowed(&cache.variables[&s[1..]])
+    } else {
+        Cow::Owned(serde_json::from_str(s).unwrap())
+    }
+}