Skip to content

Commit 1b8accb

Browse files
committed
Add an option not to report resolution errors for rustdoc
- Remove unnecessary `should_loop` variable - Report errors for trait implementations These should give resolution errors because they are visible outside the current scope. Without these errors, rustdoc will give ICEs: ``` thread 'rustc' panicked at 'attempted .def_id() on invalid res: Err', /home/joshua/src/rust/src/libstd/macros.rs:16:9 15: rustc_hir::def::Res<Id>::def_id at /home/joshua/src/rust/src/librustc_hir/def.rs:382 16: rustdoc::clean::utils::register_res at src/librustdoc/clean/utils.rs:627 17: rustdoc::clean::utils::resolve_type at src/librustdoc/clean/utils.rs:587 ``` - Add much more extensive tests + fn -> impl -> fn + fn -> impl -> fn -> macro + errors in function parameters + errors in trait bounds + errors in the type implementing the trait + unknown bounds for the type + unknown types in function bodies + errors generated by macros - Use explicit state instead of trying to reconstruct it from random info - Use an enum instead of a boolean - Add example of ignored error
1 parent b3187aa commit 1b8accb

File tree

6 files changed

+201
-39
lines changed

6 files changed

+201
-39
lines changed

src/librustc_interface/passes.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ fn configure_and_expand_inner<'a>(
233233
resolver_arenas: &'a ResolverArenas<'a>,
234234
metadata_loader: &'a MetadataLoaderDyn,
235235
) -> Result<(ast::Crate, Resolver<'a>)> {
236+
use rustc_resolve::IgnoreState;
237+
236238
log::trace!("configure_and_expand_inner");
237239
pre_expansion_lint(sess, lint_store, &krate);
238240

@@ -354,13 +356,18 @@ fn configure_and_expand_inner<'a>(
354356
)
355357
});
356358

357-
let crate_types = sess.crate_types();
358-
let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
359+
if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
360+
log::debug!("replacing bodies with loop {{}}");
361+
util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
362+
}
359363

360364
let has_proc_macro_decls = sess.time("AST_validation", || {
361365
rustc_ast_passes::ast_validation::check_crate(sess, &krate, &mut resolver.lint_buffer())
362366
});
363367

368+
let crate_types = sess.crate_types();
369+
let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
370+
364371
// For backwards compatibility, we don't try to run proc macro injection
365372
// if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being
366373
// specified. This should only affect users who manually invoke 'rustdoc', as
@@ -407,17 +414,9 @@ fn configure_and_expand_inner<'a>(
407414
}
408415

409416
// If we're actually rustdoc then avoid giving a name resolution error for `cfg()` items.
410-
// anything, so switch everything to just looping
411-
resolver.resolve_crate(&krate, sess.opts.actually_rustdoc);
412-
413-
let mut should_loop = false;
414-
if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
415-
should_loop |= true;
416-
}
417-
if should_loop {
418-
log::debug!("replacing bodies with loop {{}}");
419-
util::ReplaceBodyWithLoop::new(&mut resolver).visit_crate(&mut krate);
420-
}
417+
let ignore_bodies =
418+
if sess.opts.actually_rustdoc { IgnoreState::Ignore } else { IgnoreState::Report };
419+
resolver.resolve_crate(&krate, ignore_bodies);
421420

422421
// Needs to go *after* expansion to be able to check the results of macro expansion.
423422
sess.time("complete_gated_feature_checking", || {

src/librustc_resolve/late.rs

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,19 @@ struct DiagnosticMetadata<'ast> {
376376
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
377377
}
378378

379+
/// Keeps track of whether errors should be reported.
380+
///
381+
/// Used by rustdoc to ignore errors in function bodies.
382+
/// This is just a fancy boolean so it can have doc-comments.
383+
#[derive(Copy, Clone, Debug)]
384+
pub enum IgnoreState {
385+
/// We are at global scope or in a trait implementation, so all errors should be reported.
386+
Report,
387+
/// We are in a function body, so errors shouldn't be reported.
388+
Ignore,
389+
// Note that we don't need to worry about macros, which must always be resolved (or we wouldn't have gotten to the late pass).
390+
}
391+
379392
struct LateResolutionVisitor<'a, 'b, 'ast> {
380393
r: &'b mut Resolver<'a>,
381394

@@ -395,10 +408,12 @@ struct LateResolutionVisitor<'a, 'b, 'ast> {
395408
/// Fields used to add information to diagnostic errors.
396409
diagnostic_metadata: DiagnosticMetadata<'ast>,
397410

398-
/// Whether to report resolution errors for item bodies.
411+
/// State used to know whether to ignore resolution errors for item bodies.
399412
///
400413
/// In particular, rustdoc uses this to avoid giving errors for `cfg()` items.
401-
ignore_bodies: bool,
414+
/// In most cases this will be `None`, in which case errors will always be reported.
415+
/// If it is `Some(_)`, then it will be updated when entering a nested function or trait body.
416+
ignore_bodies: Option<IgnoreState>,
402417
}
403418

404419
/// Walks the whole crate in DFS order, visiting each item, resolving names as it goes.
@@ -502,13 +517,18 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
502517

503518
visit::walk_fn_ret_ty(this, &declaration.output);
504519

520+
let previous_ignore = this.ignore_bodies.take();
521+
// Ignore errors in function bodies if originally passed `ignore_state: true`
522+
// Be sure not to set this until the function signature has been resolved.
523+
this.ignore_bodies = previous_ignore.and(Some(IgnoreState::Ignore));
505524
// Resolve the function body, potentially inside the body of an async closure
506525
match fn_kind {
507526
FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
508527
FnKind::Closure(_, body) => this.visit_expr(body),
509528
};
510529

511530
debug!("(resolving function) leaving function");
531+
this.ignore_bodies = previous_ignore;
512532
})
513533
});
514534
self.diagnostic_metadata.current_function = previous_value;
@@ -634,7 +654,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
634654
impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
635655
fn new(
636656
resolver: &'b mut Resolver<'a>,
637-
ignore_bodies: bool,
657+
ignore_bodies: IgnoreState,
638658
) -> LateResolutionVisitor<'a, 'b, 'ast> {
639659
// During late resolution we only track the module component of the parent scope,
640660
// although it may be useful to track other components as well for diagnostics.
@@ -652,7 +672,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
652672
label_ribs: Vec::new(),
653673
current_trait_ref: None,
654674
diagnostic_metadata: DiagnosticMetadata::default(),
655-
ignore_bodies,
675+
ignore_bodies: match ignore_bodies {
676+
// errors at module scope should always be reported
677+
IgnoreState::Ignore => Some(IgnoreState::Report),
678+
IgnoreState::Report => None,
679+
},
656680
}
657681
}
658682

@@ -842,7 +866,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
842866
};
843867
let report_error = |this: &Self, ns| {
844868
let what = if ns == TypeNS { "type parameters" } else { "local variables" };
845-
this.r.session.span_err(ident.span, &format!("imports cannot refer to {}", what));
869+
if this.should_report_errs() {
870+
this.r
871+
.session
872+
.span_err(ident.span, &format!("imports cannot refer to {}", what));
873+
}
846874
};
847875

848876
for &ns in nss {
@@ -1166,6 +1194,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
11661194
impl_items: &'ast [P<AssocItem>],
11671195
) {
11681196
debug!("resolve_implementation");
1197+
let old_ignore = self.ignore_bodies.take();
1198+
// Never ignore errors in trait implementations.
1199+
self.ignore_bodies = old_ignore.and(Some(IgnoreState::Report));
11691200
// If applicable, create a rib for the type parameters.
11701201
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
11711202
// Dummy self type for better errors if `Self` is used in the trait path.
@@ -1261,6 +1292,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
12611292
});
12621293
});
12631294
});
1295+
self.ignore_bodies = old_ignore;
12641296
}
12651297

12661298
fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err: F)
@@ -1298,6 +1330,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
12981330
}
12991331

13001332
fn resolve_local(&mut self, local: &'ast Local) {
1333+
debug!("resolving local ({:?})", local);
13011334
// Resolve the type.
13021335
walk_list!(self, visit_ty, &local.ty);
13031336

@@ -1686,18 +1719,27 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
16861719
source: PathSource<'ast>,
16871720
crate_lint: CrateLint,
16881721
) -> PartialRes {
1722+
log::debug!("smart_resolve_path_fragment(id={:?},qself={:?},path={:?}", id, qself, path);
16891723
let ns = source.namespace();
16901724
let is_expected = &|res| source.is_expected(res);
16911725

16921726
let report_errors = |this: &mut Self, res: Option<Res>| {
1693-
let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res);
1694-
1695-
let def_id = this.parent_scope.module.normal_ancestor_id;
1696-
let instead = res.is_some();
1697-
let suggestion =
1698-
if res.is_none() { this.report_missing_type_error(path) } else { None };
1699-
1700-
this.r.use_injections.push(UseError { err, candidates, def_id, instead, suggestion });
1727+
if this.should_report_errs() {
1728+
let (err, candidates) = this.smart_resolve_report_errors(path, span, source, res);
1729+
1730+
let def_id = this.parent_scope.module.normal_ancestor_id;
1731+
let instead = res.is_some();
1732+
let suggestion =
1733+
if res.is_none() { this.report_missing_type_error(path) } else { None };
1734+
1735+
this.r.use_injections.push(UseError {
1736+
err,
1737+
candidates,
1738+
def_id,
1739+
instead,
1740+
suggestion,
1741+
});
1742+
}
17011743

17021744
PartialRes::new(Res::Err)
17031745
};
@@ -1755,13 +1797,17 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
17551797

17561798
let def_id = this.parent_scope.module.normal_ancestor_id;
17571799

1758-
this.r.use_injections.push(UseError {
1759-
err,
1760-
candidates,
1761-
def_id,
1762-
instead: false,
1763-
suggestion: None,
1764-
});
1800+
if this.should_report_errs() {
1801+
this.r.use_injections.push(UseError {
1802+
err,
1803+
candidates,
1804+
def_id,
1805+
instead: false,
1806+
suggestion: None,
1807+
});
1808+
} else {
1809+
err.cancel();
1810+
}
17651811

17661812
// We don't return `Some(parent_err)` here, because the error will
17671813
// be already printed as part of the `use` injections
@@ -1856,11 +1902,20 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
18561902
///
18571903
/// This doesn't emit errors for function bodies if `ignore_bodies` is set.
18581904
fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
1859-
if !self.ignore_bodies || self.diagnostic_metadata.current_function.is_none() {
1905+
if self.should_report_errs() {
18601906
self.r.report_error(span, resolution_error);
18611907
}
18621908
}
18631909

1910+
#[inline]
1911+
fn should_report_errs(&self) -> bool {
1912+
debug!("should_report_errs(state={:?})", self.ignore_bodies);
1913+
match self.ignore_bodies {
1914+
None | Some(IgnoreState::Report) => true,
1915+
Some(IgnoreState::Ignore) => false,
1916+
}
1917+
}
1918+
18641919
// Resolve in alternative namespaces if resolution in the primary namespace fails.
18651920
fn resolve_qpath_anywhere(
18661921
&mut self,
@@ -2357,7 +2412,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
23572412
}
23582413

23592414
impl<'a> Resolver<'a> {
2360-
pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) {
2415+
pub(crate) fn late_resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) {
23612416
let mut late_resolution_visitor = LateResolutionVisitor::new(self, ignore_bodies);
23622417
visit::walk_crate(&mut late_resolution_visitor, krate);
23632418
for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() {

src/librustc_resolve/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#![feature(or_patterns)]
1616
#![recursion_limit = "256"]
1717

18+
pub use late::IgnoreState;
1819
pub use rustc_hir::def::{Namespace, PerNS};
1920

2021
use Determinacy::*;
@@ -1441,7 +1442,7 @@ impl<'a> Resolver<'a> {
14411442
}
14421443

14431444
/// Entry point to crate resolution.
1444-
pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: bool) {
1445+
pub fn resolve_crate(&mut self, krate: &Crate, ignore_bodies: IgnoreState) {
14451446
let _prof_timer = self.session.prof.generic_activity("resolve_crate");
14461447

14471448
ImportResolver { r: self }.finalize_imports();
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Ensure that rustdoc gives errors for trait impls inside function bodies that don't resolve.
2+
// See https://github.com/rust-lang/rust/pull/73566
3+
pub struct ValidType;
4+
pub trait ValidTrait {}
5+
pub trait NeedsBody {
6+
type Item;
7+
fn f();
8+
}
9+
10+
/// This function has docs
11+
pub fn f<B: UnknownBound>(a: UnknownType, b: B) {
12+
//~^ ERROR cannot find trait `UnknownBound` in this scope
13+
//~| ERROR cannot find type `UnknownType` in this scope
14+
impl UnknownTrait for ValidType {} //~ ERROR cannot find trait `UnknownTrait`
15+
impl<T: UnknownBound> UnknownTrait for T {}
16+
//~^ ERROR cannot find trait `UnknownBound` in this scope
17+
//~| ERROR cannot find trait `UnknownTrait` in this scope
18+
impl ValidTrait for UnknownType {}
19+
//~^ ERROR cannot find type `UnknownType` in this scope
20+
impl ValidTrait for ValidType where ValidTrait: UnknownBound {}
21+
//~^ ERROR cannot find trait `UnknownBound` in this scope
22+
23+
/// This impl has documentation
24+
impl NeedsBody for ValidType {
25+
type Item = UnknownType;
26+
//~^ ERROR cannot find type `UnknownType` in this scope
27+
28+
/// This function has documentation
29+
fn f() {
30+
<UnknownTypeShouldBeIgnored>::a();
31+
content::shouldnt::matter();
32+
unknown_macro!();
33+
//~^ ERROR cannot find macro `unknown_macro` in this scope
34+
35+
/// This is documentation for a macro
36+
macro_rules! can_define_macros_here_too {
37+
() => {
38+
this::content::should::also::be::ignored()
39+
}
40+
}
41+
can_define_macros_here_too!();
42+
43+
/// This also is documented.
44+
pub fn doubly_nested(c: UnknownTypeShouldBeIgnored) {
45+
46+
}
47+
}
48+
}
49+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
error: cannot find macro `unknown_macro` in this scope
2+
--> $DIR/impl-fn-nesting.rs:32:13
3+
|
4+
LL | unknown_macro!();
5+
| ^^^^^^^^^^^^^
6+
7+
error[E0405]: cannot find trait `UnknownBound` in this scope
8+
--> $DIR/impl-fn-nesting.rs:11:13
9+
|
10+
LL | pub fn f<B: UnknownBound>(a: UnknownType, b: B) {
11+
| ^^^^^^^^^^^^ not found in this scope
12+
13+
error[E0412]: cannot find type `UnknownType` in this scope
14+
--> $DIR/impl-fn-nesting.rs:11:30
15+
|
16+
LL | pub fn f<B: UnknownBound>(a: UnknownType, b: B) {
17+
| ^^^^^^^^^^^ not found in this scope
18+
19+
error[E0405]: cannot find trait `UnknownTrait` in this scope
20+
--> $DIR/impl-fn-nesting.rs:14:10
21+
|
22+
LL | impl UnknownTrait for ValidType {}
23+
| ^^^^^^^^^^^^ not found in this scope
24+
25+
error[E0405]: cannot find trait `UnknownTrait` in this scope
26+
--> $DIR/impl-fn-nesting.rs:15:27
27+
|
28+
LL | impl<T: UnknownBound> UnknownTrait for T {}
29+
| ^^^^^^^^^^^^ not found in this scope
30+
31+
error[E0405]: cannot find trait `UnknownBound` in this scope
32+
--> $DIR/impl-fn-nesting.rs:15:13
33+
|
34+
LL | impl<T: UnknownBound> UnknownTrait for T {}
35+
| ^^^^^^^^^^^^ not found in this scope
36+
37+
error[E0412]: cannot find type `UnknownType` in this scope
38+
--> $DIR/impl-fn-nesting.rs:18:25
39+
|
40+
LL | impl ValidTrait for UnknownType {}
41+
| ^^^^^^^^^^^ not found in this scope
42+
43+
error[E0405]: cannot find trait `UnknownBound` in this scope
44+
--> $DIR/impl-fn-nesting.rs:20:53
45+
|
46+
LL | impl ValidTrait for ValidType where ValidTrait: UnknownBound {}
47+
| ^^^^^^^^^^^^ not found in this scope
48+
49+
error[E0412]: cannot find type `UnknownType` in this scope
50+
--> $DIR/impl-fn-nesting.rs:25:21
51+
|
52+
LL | type Item = UnknownType;
53+
| ^^^^^^^^^^^ not found in this scope
54+
55+
error: Compilation failed, aborting rustdoc
56+
57+
error: aborting due to 10 previous errors
58+
59+
Some errors have detailed explanations: E0405, E0412.
60+
For more information about an error, try `rustc --explain E0405`.

src/test/rustdoc/doc-cfg.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,5 @@ pub unsafe fn uses_target_feature() {
5757
// 'This is supported with target feature avx only.'
5858
#[doc(cfg(target_feature = "avx"))]
5959
pub fn uses_cfg_target_feature() {
60-
unsafe {
61-
uses_target_feature();
62-
}
60+
uses_target_feature();
6361
}

0 commit comments

Comments
 (0)