Skip to content

Commit a93bcc9

Browse files
committed
Recurse into function bodies, but don't typeck closures
Previously, rustdoc would issue a delay_span_bug ICE on the following code: ```rust pub fn a() -> impl Fn() -> u32 { || content::doesnt::matter() } ``` This wasn't picked up earlier because having `type Alias = impl Trait;` in the same module caused _all closures_ to be typechecked, even if they wouldn't normally. Additionally, if _any_ error was emitted, no delay_span_bug would be emitted. So as part of this commit all of the tests were separated out into different files.
1 parent 768d6a4 commit a93bcc9

16 files changed

+145
-76
lines changed

src/librustdoc/core.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,18 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
377377
external_providers.lint_mod = |_, _| {};
378378
//let old_typeck = local_providers.typeck_tables_of;
379379
local_providers.typeck_tables_of = move |tcx, def_id| {
380+
// Closures' tables come from their outermost function,
381+
// as they are part of the same "inference environment".
382+
// This avoids emitting errors for the parent twice (see similar code in `typeck_tables_of_with_fallback`)
383+
let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local();
384+
if outer_def_id != def_id {
385+
return tcx.typeck_tables_of(outer_def_id);
386+
}
387+
380388
let hir = tcx.hir();
381389
let body = hir.body(hir.body_owned_by(hir.as_local_hir_id(def_id)));
382390
debug!("visiting body for {:?}", def_id);
383-
EmitIgnoredResolutionErrors::new(&tcx.sess).visit_body(body);
391+
EmitIgnoredResolutionErrors::new(&tcx.sess, hir).visit_body(body);
384392
rustc_typeck::check::typeck_tables_of(tcx, def_id)
385393
//DEFAULT_TYPECK.with(|typeck| typeck(tcx, def_id))
386394
};
@@ -600,22 +608,24 @@ thread_local!(static DEFAULT_TYPECK: for<'tcx> fn(rustc_middle::ty::TyCtxt<'tcx>
600608
/// the name resolution pass may find errors that are never emitted.
601609
/// If typeck is called after this happens, then we'll get an ICE:
602610
/// 'Res::Error found but not reported'. To avoid this, emit the errors now.
603-
struct EmitIgnoredResolutionErrors<'a> {
611+
struct EmitIgnoredResolutionErrors<'a, 'hir> {
604612
session: &'a Session,
613+
hir_map: Map<'hir>,
605614
}
606615

607-
impl<'a> EmitIgnoredResolutionErrors<'a> {
608-
fn new(session: &'a Session) -> Self {
609-
Self { session }
616+
impl<'a, 'hir> EmitIgnoredResolutionErrors<'a, 'hir> {
617+
fn new(session: &'a Session, hir_map: Map<'hir>) -> Self {
618+
Self { session, hir_map }
610619
}
611620
}
612621

613-
impl<'a> Visitor<'a> for EmitIgnoredResolutionErrors<'_> {
614-
type Map = Map<'a>;
622+
impl<'hir> Visitor<'hir> for EmitIgnoredResolutionErrors<'_, 'hir> {
623+
type Map = Map<'hir>;
615624

616625
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
617-
// If we visit nested bodies, then we will report errors twice for e.g. nested closures
618-
NestedVisitorMap::None
626+
// We need to recurse into nested closures,
627+
// since those will fallback to the parent for type checking.
628+
NestedVisitorMap::OnlyBodies(self.hir_map)
619629
}
620630

621631
fn visit_path(&mut self, path: &'v Path<'v>, _id: HirId) {

src/test/rustdoc-ui/error-in-impl-trait.rs

Lines changed: 0 additions & 28 deletions
This file was deleted.

src/test/rustdoc-ui/error-in-impl-trait.stderr

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Each of these needs to be in a separate file,
2+
because the `delay_span_bug` ICE in rustdoc won't be triggerred
3+
if even a single other error was emitted.
4+
5+
However, conceptually they are all testing basically the same thing.
6+
See https://github.com/rust-lang/rust/pull/73566#issuecomment-653689128
7+
for more details.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// edition:2018
2+
3+
/// This used to work with ResolveBodyWithLoop.
4+
/// However now that we ignore type checking instead of modifying the function body,
5+
/// the return type is seen as `impl Future<Output = u32>`, not a `u32`.
6+
/// So it no longer allows errors in the function body.
7+
pub async fn a() -> u32 {
8+
error::_in::async_fn()
9+
//~^ ERROR failed to resolve
10+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0433]: failed to resolve: could not resolve path `error::_in::async_fn`
2+
--> $DIR/async.rs:8:5
3+
|
4+
LL | error::_in::async_fn()
5+
| ^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::async_fn`
6+
|
7+
= note: this error was originally ignored because you are running `rustdoc`
8+
= note: try running again with `rustc` and you may get a more detailed error
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0433`.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// manually desugared version of an `async fn` (but with a closure instead of a generator)
2+
pub fn a() -> impl Fn() -> u32 {
3+
|| content::doesnt::matter()
4+
//~^ ERROR failed to resolve
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0433]: failed to resolve: could not resolve path `content::doesnt::matter`
2+
--> $DIR/closure.rs:3:8
3+
|
4+
LL | || content::doesnt::matter()
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `content::doesnt::matter`
6+
|
7+
= note: this error was originally ignored because you are running `rustdoc`
8+
= note: try running again with `rustc` and you may get a more detailed error
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0433`.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub trait ValidTrait {}
2+
/// This returns impl trait
3+
pub fn g() -> impl ValidTrait {
4+
(|| error::_in::impl_trait::alias::nested::closure())()
5+
//~^ ERROR failed to resolve
6+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0433]: failed to resolve: could not resolve path `error::_in::impl_trait::alias::nested::closure`
2+
--> $DIR/impl-keyword-closure.rs:4:9
3+
|
4+
LL | (|| error::_in::impl_trait::alias::nested::closure())()
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ could not resolve path `error::_in::impl_trait::alias::nested::closure`
6+
|
7+
= note: this error was originally ignored because you are running `rustdoc`
8+
= note: try running again with `rustc` and you may get a more detailed error
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0433`.

0 commit comments

Comments
 (0)