about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-04 22:42:15 +0000
committerbors <bors@rust-lang.org>2022-07-04 22:42:15 +0000
commite1d1848cc60a407d06f90fd16877a19bed6edd9b (patch)
tree41964c0f3baccf5b045bb76104ebecee450ed6b9 /src
parent27eb6d7018e397cf98d51c205e3576951d766323 (diff)
parent08d558dbe94618d910d0218e8323b76db1b9a863 (diff)
downloadrust-e1d1848cc60a407d06f90fd16877a19bed6edd9b.tar.gz
rust-e1d1848cc60a407d06f90fd16877a19bed6edd9b.zip
Auto merge of #98904 - matthiaskrgr:rollup-05owsx7, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #98738 (Clarify MIR semantics of checked binary operations)
 - #98782 (Improve spans for specialization error)
 - #98793 (Lint against executable files in the root directory)
 - #98814 (rustdoc: Censor certain complex unevaluated const exprs)
 - #98878 (add more `rustc_pass_by_value`)
 - #98879 (Fix "wrap closure in parenthesis" suggestion for `async` closure)
 - #98886 (incr.comp.: Make split-dwarf commandline options [TRACKED].)
 - #98898 (Add "no-div-regex" eslint rule)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/utils.rs93
-rw-r--r--src/librustdoc/html/render/mod.rs4
-rw-r--r--src/librustdoc/html/render/print_item.rs9
-rw-r--r--src/librustdoc/html/static/.eslintrc.js1
-rw-r--r--src/test/incremental/split_debuginfo_mode.rs33
-rw-r--r--src/test/rustdoc/assoc-consts.rs4
-rw-r--r--src/test/rustdoc/const-value-display.rs4
-rw-r--r--src/test/rustdoc/hide-complex-unevaluated-const-arguments.rs82
-rw-r--r--src/test/rustdoc/hide-complex-unevaluated-consts.rs71
-rw-r--r--src/test/rustdoc/show-const-contents.rs2
-rw-r--r--src/test/ui/specialization/min_specialization/repeated_projection_type.stderr6
-rw-r--r--src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr4
-rw-r--r--src/test/ui/specialization/min_specialization/specialization_super_trait.stderr4
-rw-r--r--src/test/ui/specialization/min_specialization/specialization_trait.stderr4
-rw-r--r--src/test/ui/specialization/min_specialization/specialize_on_trait.stderr4
-rw-r--r--src/test/ui/suggestions/suggest-on-bare-closure-call.rs7
-rw-r--r--src/test/ui/suggestions/suggest-on-bare-closure-call.stderr17
-rw-r--r--src/tools/tidy/src/bins.rs23
-rw-r--r--src/tools/tidy/src/main.rs9
19 files changed, 350 insertions, 31 deletions
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 16574f94c00..22b1e2335fd 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -340,17 +340,98 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
     false
 }
 
+/// Build a textual representation of an unevaluated constant expression.
+///
+/// If the const expression is too complex, an underscore `_` is returned.
+/// For const arguments, it's `{ _ }` to be precise.
+/// This means that the output is not necessarily valid Rust code.
+///
+/// Currently, only
+///
+/// * literals (optionally with a leading `-`)
+/// * unit `()`
+/// * blocks (`{ … }`) around simple expressions and
+/// * paths without arguments
+///
+/// are considered simple enough. Simple blocks are included since they are
+/// necessary to disambiguate unit from the unit type.
+/// This list might get extended in the future.
+///
+/// Without this censoring, in a lot of cases the output would get too large
+/// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
+/// Further, private and `doc(hidden)` fields of structs would get leaked
+/// since HIR datatypes like the `body` parameter do not contain enough
+/// semantic information for this function to be able to hide them –
+/// at least not without significant performance overhead.
+///
+/// Whenever possible, prefer to evaluate the constant first and try to
+/// use a different method for pretty-printing. Ideally this function
+/// should only ever be used as a fallback.
 pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String {
     let hir = tcx.hir();
     let value = &hir.body(body).value;
 
-    let snippet = if !value.span.from_expansion() {
-        tcx.sess.source_map().span_to_snippet(value.span).ok()
-    } else {
-        None
-    };
+    #[derive(PartialEq, Eq)]
+    enum Classification {
+        Literal,
+        Simple,
+        Complex,
+    }
 
-    snippet.unwrap_or_else(|| rustc_hir_pretty::id_to_string(&hir, body.hir_id))
+    use Classification::*;
+
+    fn classify(expr: &hir::Expr<'_>) -> Classification {
+        match &expr.kind {
+            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
+                if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex }
+            }
+            hir::ExprKind::Lit(_) => Literal,
+            hir::ExprKind::Tup([]) => Simple,
+            hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => {
+                if classify(expr) == Complex { Complex } else { Simple }
+            }
+            // Paths with a self-type or arguments are too “complex” following our measure since
+            // they may leak private fields of structs (with feature `adt_const_params`).
+            // Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
+            // Paths without arguments are definitely harmless though.
+            hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => {
+                if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex }
+            }
+            // FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
+            //        contains const arguments. Is there a *concise* way to check for this?
+            hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple,
+            // FIXME: Can they contain const arguments and thus leak private struct fields?
+            hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple,
+            _ => Complex,
+        }
+    }
+
+    let classification = classify(value);
+
+    if classification == Literal
+    && !value.span.from_expansion()
+    && let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) {
+        // For literals, we avoid invoking the pretty-printer and use the source snippet instead to
+        // preserve certain stylistic choices the user likely made for the sake legibility like
+        //
+        // * hexadecimal notation
+        // * underscores
+        // * character escapes
+        //
+        // FIXME: This passes through `-/*spacer*/0` verbatim.
+        snippet
+    } else if classification == Simple {
+        // Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
+        // other formatting artifacts.
+        rustc_hir_pretty::id_to_string(&hir, body.hir_id)
+    } else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
+        // FIXME: Omit the curly braces if the enclosing expression is an array literal
+        //        with a repeated element (an `ExprKind::Repeat`) as in such case it
+        //        would not actually need any disambiguation.
+        "{ _ }".to_owned()
+    } else {
+        "_".to_owned()
+    }
 }
 
 /// Given a type Path, resolve it to a Type using the TyCtxt
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 1ef41d62e5e..548f6c3a987 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -716,12 +716,14 @@ fn assoc_const(
         ty = ty.print(cx),
     );
     if let Some(default) = default {
+        write!(w, " = ");
+
         // FIXME: `.value()` uses `clean::utils::format_integer_with_underscore_sep` under the
         //        hood which adds noisy underscores and a type suffix to number literals.
         //        This hurts readability in this context especially when more complex expressions
         //        are involved and it doesn't add much of value.
         //        Find a way to print constants here without all that jazz.
-        write!(w, " = {}", default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx())));
+        write!(w, "{}", Escape(&default.value(cx.tcx()).unwrap_or_else(|| default.expr(cx.tcx()))));
     }
 }
 
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 0fe99463f1d..fe00f952e04 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1360,6 +1360,15 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
                 typ = c.type_.print(cx),
             );
 
+            // FIXME: The code below now prints
+            //            ` = _; // 100i32`
+            //        if the expression is
+            //            `50 + 50`
+            //        which looks just wrong.
+            //        Should we print
+            //            ` = 100i32;`
+            //        instead?
+
             let value = c.value(cx.tcx());
             let is_literal = c.is_literal(cx.tcx());
             let expr = c.expr(cx.tcx());
diff --git a/src/librustdoc/html/static/.eslintrc.js b/src/librustdoc/html/static/.eslintrc.js
index 2817a8fe144..fcd925bb358 100644
--- a/src/librustdoc/html/static/.eslintrc.js
+++ b/src/librustdoc/html/static/.eslintrc.js
@@ -91,5 +91,6 @@ module.exports = {
         "no-script-url": "error",
         "no-sequences": "error",
         "no-throw-literal": "error",
+        "no-div-regex": "error",
     }
 };
diff --git a/src/test/incremental/split_debuginfo_mode.rs b/src/test/incremental/split_debuginfo_mode.rs
new file mode 100644
index 00000000000..f2533e4146a
--- /dev/null
+++ b/src/test/incremental/split_debuginfo_mode.rs
@@ -0,0 +1,33 @@
+// This test case makes sure that changing split-debuginfo commandline options triggers a full re-compilation.
+// We only test on x86_64-unknown-linux-gnu because there all combinations split-debuginfo settings are valid
+// and the test is platform-independent otherwise.
+
+// ignore-tidy-linelength
+// only-x86_64-unknown-linux-gnu
+// revisions:rpass1 rpass2 rpass3 rpass4
+
+// [rpass1]compile-flags: -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=unpacked -Zsplit-dwarf-kind=single -Zsplit-dwarf-inlining=on
+// [rpass2]compile-flags: -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=single -Zsplit-dwarf-inlining=on
+// [rpass3]compile-flags: -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split -Zsplit-dwarf-inlining=on
+// [rpass4]compile-flags: -Zquery-dep-graph -Zunstable-options -Csplit-debuginfo=packed -Zsplit-dwarf-kind=split -Zsplit-dwarf-inlining=off
+
+#![feature(rustc_attrs)]
+// For rpass2 we change -Csplit-debuginfo and thus expect every CGU to be recompiled
+#![rustc_partition_codegened(module = "split_debuginfo_mode", cfg = "rpass2")]
+#![rustc_partition_codegened(module = "split_debuginfo_mode-another_module", cfg = "rpass2")]
+// For rpass3 we change -Zsplit-dwarf-kind and thus also expect every CGU to be recompiled
+#![rustc_partition_codegened(module = "split_debuginfo_mode", cfg = "rpass3")]
+#![rustc_partition_codegened(module = "split_debuginfo_mode-another_module", cfg = "rpass3")]
+// For rpass4 we change -Zsplit-dwarf-inlining and thus also expect every CGU to be recompiled
+#![rustc_partition_codegened(module = "split_debuginfo_mode", cfg = "rpass4")]
+#![rustc_partition_codegened(module = "split_debuginfo_mode-another_module", cfg = "rpass4")]
+
+mod another_module {
+    pub fn foo() -> &'static str {
+        "hello world"
+    }
+}
+
+pub fn main() {
+    println!("{}", another_module::foo());
+}
diff --git a/src/test/rustdoc/assoc-consts.rs b/src/test/rustdoc/assoc-consts.rs
index 0ac6dc763df..a79e93145ba 100644
--- a/src/test/rustdoc/assoc-consts.rs
+++ b/src/test/rustdoc/assoc-consts.rs
@@ -27,6 +27,10 @@ impl Bar {
     // @has assoc_consts/struct.Bar.html '//*[@id="associatedconstant.BAR"]' \
     //      'const BAR: usize'
     pub const BAR: usize = 3;
+
+    // @has - '//*[@id="associatedconstant.BAR_ESCAPED"]' \
+    //      "const BAR_ESCAPED: &'static str = \"<em>markup</em>\""
+    pub const BAR_ESCAPED: &'static str = "<em>markup</em>";
 }
 
 pub struct Baz<'a, U: 'a, T>(T, &'a [U]);
diff --git a/src/test/rustdoc/const-value-display.rs b/src/test/rustdoc/const-value-display.rs
index 0ae52592b64..5b2f3c48d57 100644
--- a/src/test/rustdoc/const-value-display.rs
+++ b/src/test/rustdoc/const-value-display.rs
@@ -1,9 +1,9 @@
 #![crate_name = "foo"]
 
 // @has 'foo/constant.HOUR_IN_SECONDS.html'
-// @has - '//*[@class="docblock item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = 60 * 60; // 3_600u64'
+// @has - '//*[@class="docblock item-decl"]//code' 'pub const HOUR_IN_SECONDS: u64 = _; // 3_600u64'
 pub const HOUR_IN_SECONDS: u64 = 60 * 60;
 
 // @has 'foo/constant.NEGATIVE.html'
-// @has - '//*[@class="docblock item-decl"]//code' 'pub const NEGATIVE: i64 = -60 * 60; // -3_600i64'
+// @has - '//*[@class="docblock item-decl"]//code' 'pub const NEGATIVE: i64 = _; // -3_600i64'
 pub const NEGATIVE: i64 = -60 * 60;
diff --git a/src/test/rustdoc/hide-complex-unevaluated-const-arguments.rs b/src/test/rustdoc/hide-complex-unevaluated-const-arguments.rs
new file mode 100644
index 00000000000..644a6e1cf33
--- /dev/null
+++ b/src/test/rustdoc/hide-complex-unevaluated-const-arguments.rs
@@ -0,0 +1,82 @@
+// Test that certain unevaluated constant expression arguments that are
+// deemed too verbose or complex and that may leak private or
+// `doc(hidden)` struct fields are not displayed in the documentation.
+//
+// Read the documentation of `rustdoc::clean::utils::print_const_expr`
+// for further details.
+#![feature(const_trait_impl, generic_const_exprs)]
+#![allow(incomplete_features)]
+
+// @has hide_complex_unevaluated_const_arguments/trait.Stage.html
+pub trait Stage {
+    // A helper constant that prevents const expressions containing it
+    // from getting fully evaluated since it doesn't have a body and
+    // thus is non-reducible. This allows us to specifically test the
+    // pretty-printing of *unevaluated* consts.
+    const ABSTRACT: usize;
+
+    // Currently considered "overly complex" by the `generic_const_exprs`
+    // feature. If / once this expression kind gets supported, this
+    // unevaluated const expression could leak the private struct field.
+    //
+    // FIXME: Once the line below compiles, make this a test that
+    //        ensures that the private field is not printed.
+    //
+    //const ARRAY0: [u8; Struct { private: () } + Self::ABSTRACT];
+
+    // This assoc. const could leak the private assoc. function `Struct::new`.
+    // Ensure that this does not happen.
+    //
+    // @has - '//*[@id="associatedconstant.ARRAY1"]' \
+    //        'const ARRAY1: [u8; { _ }]'
+    const ARRAY1: [u8; Struct::new(/* ... */) + Self::ABSTRACT * 1_000];
+
+    // @has - '//*[@id="associatedconstant.VERBOSE"]' \
+    //        'const VERBOSE: [u16; { _ }]'
+    const VERBOSE: [u16; compute("thing", 9 + 9) * Self::ABSTRACT];
+
+    // Check that we do not leak the private struct field contained within
+    // the path. The output could definitely be improved upon
+    // (e.g. printing sth. akin to `<Self as Helper<{ _ }>>::OUT`) but
+    // right now “safe is safe”.
+    //
+    // @has - '//*[@id="associatedconstant.PATH"]' \
+    //        'const PATH: usize = _'
+    const PATH: usize = <Self as Helper<{ Struct { private: () } }>>::OUT;
+}
+
+const fn compute(input: &str, extra: usize) -> usize {
+    input.len() + extra
+}
+
+pub trait Helper<const S: Struct> {
+    const OUT: usize;
+}
+
+impl<const S: Struct, St: Stage + ?Sized> Helper<S> for St {
+    const OUT: usize = St::ABSTRACT;
+}
+
+// Currently in rustdoc, const arguments are not evaluated in this position
+// and therefore they fall under the realm of `print_const_expr`.
+// If rustdoc gets patched to evaluate const arguments, it is fine to replace
+// this test as long as one can ensure that private fields are not leaked!
+//
+// @has hide_complex_unevaluated_const_arguments/trait.Sub.html \
+//      '//*[@class="rust trait"]' \
+//      'pub trait Sub: Sup<{ _ }, { _ }> { }'
+pub trait Sub: Sup<{ 90 * 20 * 4 }, { Struct { private: () } }> {}
+
+pub trait Sup<const N: usize, const S: Struct> {}
+
+pub struct Struct { private: () }
+
+impl Struct {
+    const fn new() -> Self { Self { private: () } }
+}
+
+impl const std::ops::Add<usize> for Struct {
+    type Output = usize;
+
+    fn add(self, _: usize) -> usize { 0 }
+}
diff --git a/src/test/rustdoc/hide-complex-unevaluated-consts.rs b/src/test/rustdoc/hide-complex-unevaluated-consts.rs
new file mode 100644
index 00000000000..ba623246a01
--- /dev/null
+++ b/src/test/rustdoc/hide-complex-unevaluated-consts.rs
@@ -0,0 +1,71 @@
+// Regression test for issue #97933.
+//
+// Test that certain unevaluated constant expressions that are
+// deemed too verbose or complex and that may leak private or
+// `doc(hidden)` struct fields are not displayed in the documentation.
+//
+// Read the documentation of `rustdoc::clean::utils::print_const_expr`
+// for further details.
+
+// @has hide_complex_unevaluated_consts/trait.Container.html
+pub trait Container {
+    // A helper constant that prevents const expressions containing it
+    // from getting fully evaluated since it doesn't have a body and
+    // thus is non-reducible. This allows us to specifically test the
+    // pretty-printing of *unevaluated* consts.
+    const ABSTRACT: i32;
+
+    // Ensure that the private field does not get leaked:
+    //
+    // @has - '//*[@id="associatedconstant.STRUCT0"]' \
+    //        'const STRUCT0: Struct = _'
+    const STRUCT0: Struct = Struct { private: () };
+
+    // @has - '//*[@id="associatedconstant.STRUCT1"]' \
+    //        'const STRUCT1: (Struct,) = _'
+    const STRUCT1: (Struct,) = (Struct{private: /**/()},);
+
+    // Although the struct field is public here, check that it is not
+    // displayed. In a future version of rustdoc, we definitely want to
+    // show it. However for the time being, the printing logic is a bit
+    // conservative.
+    //
+    // @has - '//*[@id="associatedconstant.STRUCT2"]' \
+    //        'const STRUCT2: Record = _'
+    const STRUCT2: Record = Record { public: 5 };
+
+    // Test that we do not show the incredibly verbose match expr:
+    //
+    // @has - '//*[@id="associatedconstant.MATCH0"]' \
+    //        'const MATCH0: i32 = _'
+    const MATCH0: i32 = match 234 {
+        0 => 1,
+        _ => Self::ABSTRACT,
+    };
+
+    // @has - '//*[@id="associatedconstant.MATCH1"]' \
+    //        'const MATCH1: bool = _'
+    const MATCH1: bool = match Self::ABSTRACT {
+        _ => true,
+    };
+
+    // Check that we hide complex (arithmetic) operations.
+    // In this case, it is a bit unfortunate since the expression
+    // is not *that* verbose and it might be quite useful to the reader.
+    //
+    // However in general, the expression might be quite large and
+    // contain match expressions and structs with private fields.
+    // We would need to recurse over the whole expression and even more
+    // importantly respect operator precedence when pretty-printing
+    // the potentially partially censored expression.
+    // For now, the implementation is quite simple and the choices
+    // rather conservative.
+    //
+    // @has - '//*[@id="associatedconstant.ARITH_OPS"]' \
+    //        'const ARITH_OPS: i32 = _'
+    const ARITH_OPS: i32 = Self::ABSTRACT * 2 + 1;
+}
+
+pub struct Struct { private: () }
+
+pub struct Record { pub public: i32 }
diff --git a/src/test/rustdoc/show-const-contents.rs b/src/test/rustdoc/show-const-contents.rs
index f5a356bcae6..48b60885974 100644
--- a/src/test/rustdoc/show-const-contents.rs
+++ b/src/test/rustdoc/show-const-contents.rs
@@ -21,7 +21,7 @@ pub const CONST_NEG_I32: i32 = -42;
 // @!has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '// 42i32'
 pub const CONST_EQ_TO_VALUE_I32: i32 = 42i32;
 
-// @has show_const_contents/constant.CONST_CALC_I32.html '= 42 + 1; // 43i32'
+// @has show_const_contents/constant.CONST_CALC_I32.html '= _; // 43i32'
 pub const CONST_CALC_I32: i32 = 42 + 1;
 
 // @!has show_const_contents/constant.CONST_REF_I32.html '= &42;'
diff --git a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr
index 450984aacc7..a751ba79347 100644
--- a/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr
+++ b/src/test/ui/specialization/min_specialization/repeated_projection_type.stderr
@@ -1,8 +1,8 @@
-error: cannot specialize on `Binder(ProjectionPredicate(ProjectionTy { substs: [V], item_def_id: DefId(0:6 ~ repeated_projection_type[54ea]::Id::This) }, Ty((I,))), [])`
-  --> $DIR/repeated_projection_type.rs:19:1
+error: cannot specialize on associated type `<V as Id>::This == (I,)`
+  --> $DIR/repeated_projection_type.rs:19:15
    |
 LL | impl<I, V: Id<This = (I,)>> X for V {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |               ^^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr
index b1ab58551e6..ba9d6bbe300 100644
--- a/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr
+++ b/src/test/ui/specialization/min_specialization/spec-marker-supertraits.stderr
@@ -1,8 +1,8 @@
 error: cannot specialize on trait `HasMethod`
-  --> $DIR/spec-marker-supertraits.rs:22:1
+  --> $DIR/spec-marker-supertraits.rs:22:9
    |
 LL | impl<T: Marker> Spec for T {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr
index 1f2ff99d415..e935786624b 100644
--- a/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr
+++ b/src/test/ui/specialization/min_specialization/specialization_super_trait.stderr
@@ -1,8 +1,8 @@
 error: cannot specialize on trait `Default`
-  --> $DIR/specialization_super_trait.rs:13:1
+  --> $DIR/specialization_super_trait.rs:13:9
    |
 LL | impl<T: Default> SpecMarker for T {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/specialization/min_specialization/specialization_trait.stderr b/src/test/ui/specialization/min_specialization/specialization_trait.stderr
index ecb07ba908e..bc87ae0f8b8 100644
--- a/src/test/ui/specialization/min_specialization/specialization_trait.stderr
+++ b/src/test/ui/specialization/min_specialization/specialization_trait.stderr
@@ -11,10 +11,10 @@ LL | impl<T> SpecMarker for (T, T) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: cannot specialize on trait `Clone`
-  --> $DIR/specialization_trait.rs:21:1
+  --> $DIR/specialization_trait.rs:21:9
    |
 LL | impl<T: Clone> SpecMarker for [T] {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr
index 92daddbd800..7b79c7eb4ad 100644
--- a/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr
+++ b/src/test/ui/specialization/min_specialization/specialize_on_trait.stderr
@@ -1,8 +1,8 @@
 error: cannot specialize on trait `SpecMarker`
-  --> $DIR/specialize_on_trait.rs:15:1
+  --> $DIR/specialize_on_trait.rs:15:9
    |
 LL | impl<T: SpecMarker> X for T {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/suggestions/suggest-on-bare-closure-call.rs b/src/test/ui/suggestions/suggest-on-bare-closure-call.rs
index 355708c08ec..496c305bc2a 100644
--- a/src/test/ui/suggestions/suggest-on-bare-closure-call.rs
+++ b/src/test/ui/suggestions/suggest-on-bare-closure-call.rs
@@ -1,4 +1,11 @@
+// edition:2021
+
+#![feature(async_closure)]
+
 fn main() {
     let _ = ||{}();
     //~^ ERROR expected function, found `()`
+
+    let _ = async ||{}();
+    //~^ ERROR expected function, found `()`
 }
diff --git a/src/test/ui/suggestions/suggest-on-bare-closure-call.stderr b/src/test/ui/suggestions/suggest-on-bare-closure-call.stderr
index 81f2e498fe5..e65a6eb4939 100644
--- a/src/test/ui/suggestions/suggest-on-bare-closure-call.stderr
+++ b/src/test/ui/suggestions/suggest-on-bare-closure-call.stderr
@@ -1,5 +1,5 @@
 error[E0618]: expected function, found `()`
-  --> $DIR/suggest-on-bare-closure-call.rs:2:15
+  --> $DIR/suggest-on-bare-closure-call.rs:6:15
    |
 LL |     let _ = ||{}();
    |               ^^--
@@ -11,6 +11,19 @@ help: if you meant to create this closure and immediately call it, surround the
 LL |     let _ = (||{})();
    |             +    +
 
-error: aborting due to previous error
+error[E0618]: expected function, found `()`
+  --> $DIR/suggest-on-bare-closure-call.rs:9:21
+   |
+LL |     let _ = async ||{}();
+   |                     ^^--
+   |                     |
+   |                     call expression requires function
+   |
+help: if you meant to create this closure and immediately call it, surround the closure with parentheses
+   |
+LL |     let _ = (async ||{})();
+   |             +          +
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0618`.
diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs
index 503df537047..9615c4db6b4 100644
--- a/src/tools/tidy/src/bins.rs
+++ b/src/tools/tidy/src/bins.rs
@@ -96,9 +96,25 @@ mod os_impl {
 
     #[cfg(unix)]
     pub fn check(path: &Path, bad: &mut bool) {
+        const ALLOWED: &[&str] = &["configure"];
+
         crate::walk_no_read(
             path,
-            &mut |path| crate::filter_dirs(path) || path.ends_with("src/etc"),
+            &mut |path| {
+                crate::filter_dirs(path)
+                    || path.ends_with("src/etc")
+                    // This is a list of directories that we almost certainly
+                    // don't need to walk. A future PR will likely want to
+                    // remove these in favor of crate::walk_no_read using git
+                    // ls-files to discover the paths we should check, which
+                    // would naturally ignore all of these directories. It's
+                    // also likely faster than walking the directory tree
+                    // directly (since git is just reading from a couple files
+                    // to produce the results).
+                    || path.ends_with("target")
+                    || path.ends_with("build")
+                    || path.ends_with(".git")
+            },
             &mut |entry| {
                 let file = entry.path();
                 let filename = file.file_name().unwrap().to_string_lossy();
@@ -110,6 +126,11 @@ mod os_impl {
                 if t!(is_executable(&file), file) {
                     let rel_path = file.strip_prefix(path).unwrap();
                     let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
+
+                    if ALLOWED.contains(&git_friendly_path.as_str()) {
+                        return;
+                    }
+
                     let output = Command::new("git")
                         .arg("ls-files")
                         .arg(&git_friendly_path)
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index d555f7c8e34..aa8d8b4f64d 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -78,13 +78,8 @@ fn main() {
         check!(unit_tests, &compiler_path);
         check!(unit_tests, &library_path);
 
-        if bins::check_filesystem_support(
-            &[&src_path, &compiler_path, &library_path],
-            &output_directory,
-        ) {
-            check!(bins, &src_path);
-            check!(bins, &compiler_path);
-            check!(bins, &library_path);
+        if bins::check_filesystem_support(&[&root_path], &output_directory) {
+            check!(bins, &root_path);
         }
 
         check!(style, &src_path);