1 use rustc_data_structures
::fx
::{FxHashMap, FxHashSet}
;
2 use rustc_data_structures
::sync
::{self, Lrc}
;
3 use rustc_driver
::abort_on_err
;
4 use rustc_errors
::emitter
::{Emitter, EmitterWriter}
;
5 use rustc_errors
::json
::JsonEmitter
;
6 use rustc_feature
::UnstableFeatures
;
7 use rustc_hir
::def
::{Namespace::TypeNS, Res}
;
8 use rustc_hir
::def_id
::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}
;
11 intravisit
::{self, NestedVisitorMap, Visitor}
,
14 use rustc_interface
::{interface, Queries}
;
15 use rustc_middle
::hir
::map
::Map
;
16 use rustc_middle
::middle
::privacy
::AccessLevels
;
17 use rustc_middle
::ty
::{ParamEnv, Ty, TyCtxt}
;
18 use rustc_resolve
as resolve
;
19 use rustc_session
::config
::{self, CrateType, ErrorOutputType}
;
20 use rustc_session
::lint
;
21 use rustc_session
::DiagnosticOutput
;
22 use rustc_session
::Session
;
23 use rustc_span
::source_map
;
24 use rustc_span
::symbol
::sym
;
25 use rustc_span
::{Span, DUMMY_SP}
;
27 use std
::cell
::RefCell
;
28 use std
::collections
::hash_map
::Entry
;
33 use crate::clean
::inline
::build_external_trait
;
34 use crate::clean
::{AttributesExt, TraitWithExtraInfo, MAX_DEF_IDX}
;
35 use crate::config
::{Options as RustdocOptions, OutputFormat, RenderOptions}
;
36 use crate::formats
::cache
::Cache
;
37 use crate::passes
::{self, Condition::*, ConditionalPass}
;
39 crate use rustc_session
::config
::{DebuggingOptions, Input, Options}
;
41 crate struct DocContext
<'tcx
> {
42 crate tcx
: TyCtxt
<'tcx
>,
43 /// Name resolver. Used for intra-doc links.
45 /// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by
46 /// [`Queries::expansion()`].
47 // FIXME: see if we can get rid of this RefCell somehow
48 crate resolver
: Rc
<RefCell
<interface
::BoxedResolver
>>,
49 /// Used for normalization.
51 /// Most of this logic is copied from rustc_lint::late.
52 crate param_env
: ParamEnv
<'tcx
>,
53 /// Later on moved through `clean::Crate` into `cache`
54 crate external_traits
: Rc
<RefCell
<FxHashMap
<DefId
, clean
::TraitWithExtraInfo
>>>,
55 /// Used while populating `external_traits` to ensure we don't process the same trait twice at
57 crate active_extern_traits
: FxHashSet
<DefId
>,
58 // The current set of type and lifetime substitutions,
59 // for expanding type aliases at the HIR level:
60 /// Table `DefId` of type parameter -> substituted type
61 crate ty_substs
: FxHashMap
<DefId
, clean
::Type
>,
62 /// Table `DefId` of lifetime parameter -> substituted lifetime
63 crate lt_substs
: FxHashMap
<DefId
, clean
::Lifetime
>,
64 /// Table `DefId` of const parameter -> substituted const
65 crate ct_substs
: FxHashMap
<DefId
, clean
::Constant
>,
66 /// Table synthetic type parameter for `impl Trait` in argument position -> bounds
67 crate impl_trait_bounds
: FxHashMap
<ImplTraitParam
, Vec
<clean
::GenericBound
>>,
68 crate fake_def_ids
: FxHashMap
<CrateNum
, DefIndex
>,
69 /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
70 // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
71 crate generated_synthetics
: FxHashSet
<(Ty
<'tcx
>, DefId
)>,
72 crate auto_traits
: Vec
<DefId
>,
73 /// The options given to rustdoc that could be relevant to a pass.
74 crate render_options
: RenderOptions
,
75 /// The traits in scope for a given module.
77 /// See `collect_intra_doc_links::traits_implemented_by` for more details.
78 /// `map<module, set<trait>>`
79 crate module_trait_cache
: FxHashMap
<DefId
, FxHashSet
<DefId
>>,
80 /// This same cache is used throughout rustdoc, including in [`crate::html::render`].
82 /// Used by [`clean::inline`] to tell if an item has already been inlined.
83 crate inlined
: FxHashSet
<DefId
>,
84 /// Used by `calculate_doc_coverage`.
85 crate output_format
: OutputFormat
,
88 impl<'tcx
> DocContext
<'tcx
> {
89 crate fn sess(&self) -> &'tcx Session
{
93 crate fn with_param_env
<T
, F
: FnOnce(&mut Self) -> T
>(&mut self, def_id
: DefId
, f
: F
) -> T
{
94 let old_param_env
= mem
::replace(&mut self.param_env
, self.tcx
.param_env(def_id
));
96 self.param_env
= old_param_env
;
100 crate fn enter_resolver
<F
, R
>(&self, f
: F
) -> R
102 F
: FnOnce(&mut resolve
::Resolver
<'_
>) -> R
,
104 self.resolver
.borrow_mut().access(f
)
107 /// Call the closure with the given parameters set as
108 /// the substitutions for a type alias' RHS.
109 crate fn enter_alias
<F
, R
>(
111 ty_substs
: FxHashMap
<DefId
, clean
::Type
>,
112 lt_substs
: FxHashMap
<DefId
, clean
::Lifetime
>,
113 ct_substs
: FxHashMap
<DefId
, clean
::Constant
>,
117 F
: FnOnce(&mut Self) -> R
,
119 let (old_tys
, old_lts
, old_cts
) = (
120 mem
::replace(&mut self.ty_substs
, ty_substs
),
121 mem
::replace(&mut self.lt_substs
, lt_substs
),
122 mem
::replace(&mut self.ct_substs
, ct_substs
),
125 self.ty_substs
= old_tys
;
126 self.lt_substs
= old_lts
;
127 self.ct_substs
= old_cts
;
131 /// Create a new "fake" [`DefId`].
133 /// This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
134 /// refactoring either rustdoc or [`rustc_middle`]. In particular, allowing new [`DefId`]s
135 /// to be registered after the AST is constructed would require storing the [`DefId`] mapping
136 /// in a [`RefCell`], decreasing the performance for normal compilation for very little gain.
138 /// Instead, we construct "fake" [`DefId`]s, which start immediately after the last `DefId`.
139 /// In the [`Debug`] impl for [`clean::Item`], we explicitly check for fake `DefId`s,
140 /// as we'll end up with a panic if we use the `DefId` `Debug` impl for fake `DefId`s.
142 /// [`RefCell`]: std::cell::RefCell
143 /// [`Debug`]: std::fmt::Debug
144 /// [`clean::Item`]: crate::clean::types::Item
145 crate fn next_def_id(&mut self, crate_num
: CrateNum
) -> DefId
{
146 let def_index
= match self.fake_def_ids
.entry(crate_num
) {
147 Entry
::Vacant(e
) => {
149 let num_def_idx
= if crate_num
== LOCAL_CRATE
{
150 self.tcx
.hir().definitions().def_path_table().num_def_ids()
152 self.resolver
.borrow_mut().access(|r
| r
.cstore().num_def_ids(crate_num
))
155 DefIndex
::from_usize(num_def_idx
)
158 MAX_DEF_IDX
.with(|m
| {
159 m
.borrow_mut().insert(crate_num
, num_def_idx
);
161 e
.insert(num_def_idx
)
163 Entry
::Occupied(e
) => e
.into_mut(),
165 *def_index
= *def_index
+ 1;
167 DefId { krate: crate_num, index: *def_index }
170 /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds.
171 /// (This avoids a slice-index-out-of-bounds panic.)
172 crate fn as_local_hir_id(tcx
: TyCtxt
<'_
>, def_id
: DefId
) -> Option
<HirId
> {
173 if MAX_DEF_IDX
.with(|m
| {
174 m
.borrow().get(&def_id
.krate
).map(|&idx
| idx
<= def_id
.index
).unwrap_or(false)
178 def_id
.as_local().map(|def_id
| tcx
.hir().local_def_id_to_hir_id(def_id
))
183 /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
185 /// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one
186 /// will be created for the handler.
187 crate fn new_handler(
188 error_format
: ErrorOutputType
,
189 source_map
: Option
<Lrc
<source_map
::SourceMap
>>,
190 debugging_opts
: &DebuggingOptions
,
191 ) -> rustc_errors
::Handler
{
192 let emitter
: Box
<dyn Emitter
+ sync
::Send
> = match error_format
{
193 ErrorOutputType
::HumanReadable(kind
) => {
194 let (short
, color_config
) = kind
.unzip();
196 EmitterWriter
::stderr(
198 source_map
.map(|sm
| sm
as _
),
200 debugging_opts
.teach
,
201 debugging_opts
.terminal_width
,
204 .ui_testing(debugging_opts
.ui_testing
),
207 ErrorOutputType
::Json { pretty, json_rendered }
=> {
208 let source_map
= source_map
.unwrap_or_else(|| {
209 Lrc
::new(source_map
::SourceMap
::new(source_map
::FilePathMapping
::empty()))
217 debugging_opts
.terminal_width
,
220 .ui_testing(debugging_opts
.ui_testing
),
225 rustc_errors
::Handler
::with_emitter_and_flags(
227 debugging_opts
.diagnostic_handler_flags(true),
231 /// Parse, resolve, and typecheck the given crate.
232 crate fn create_config(
252 ) -> rustc_interface
::Config
{
253 // Add the doc cfg into the doc build.
254 cfgs
.push("doc".to_string());
256 let cpath
= Some(input
.clone());
257 let input
= Input
::File(input
);
259 // By default, rustdoc ignores all lints.
260 // Specifically unblock lints relevant to documentation or the lint machinery itself.
261 let mut lints_to_show
= vec
![
262 // it's unclear whether this should be part of rustdoc directly (#77364)
263 rustc_lint
::builtin
::MISSING_DOCS
.name
.to_string(),
264 // these are definitely not part of rustdoc, but we want to warn on them anyway.
265 rustc_lint
::builtin
::RENAMED_AND_REMOVED_LINTS
.name
.to_string(),
266 rustc_lint
::builtin
::UNKNOWN_LINTS
.name
.to_string(),
268 lints_to_show
.extend(crate::lint
::RUSTDOC_LINTS
.iter().map(|lint
| lint
.name
.to_string()));
270 let (lint_opts
, lint_caps
) = crate::lint
::init_lints(lints_to_show
, lint_opts
, |lint
| {
271 // FIXME: why is this necessary?
272 if lint
.name
== crate::lint
::BROKEN_INTRA_DOC_LINKS
.name
273 || lint
.name
== crate::lint
::INVALID_CODEBLOCK_ATTRIBUTES
.name
277 Some((lint
.name_lower(), lint
::Allow
))
282 if proc_macro_crate { vec![CrateType::ProcMacro] }
else { vec![CrateType::Rlib] }
;
283 // plays with error output here!
284 let sessopts
= config
::Options
{
288 lint_opts
: if !display_warnings { lint_opts }
else { vec![] }
,
292 target_triple
: target
,
293 unstable_features
: UnstableFeatures
::from_environment(crate_name
.as_deref()),
294 actually_rustdoc
: true,
305 crate_cfg
: interface
::parse_cfgspecs(cfgs
),
311 diagnostic_output
: DiagnosticOutput
::Default
,
314 parse_sess_created
: None
,
315 register_lints
: Some(box crate::lint
::register_lints
),
316 override_queries
: Some(|_sess
, providers
, _external_providers
| {
317 // Most lints will require typechecking, so just don't run them.
318 providers
.lint_mod
= |_
, _
| {}
;
319 // Prevent `rustc_typeck::check_crate` from calling `typeck` on all bodies.
320 providers
.typeck_item_bodies
= |_
, _
| {}
;
321 // hack so that `used_trait_imports` won't try to call typeck
322 providers
.used_trait_imports
= |_
, _
| {
324 static ref EMPTY_SET
: FxHashSet
<LocalDefId
> = FxHashSet
::default();
328 // In case typeck does end up being called, don't ICE in case there were name resolution errors
329 providers
.typeck
= move |tcx
, def_id
| {
330 // Closures' tables come from their outermost function,
331 // as they are part of the same "inference environment".
332 // This avoids emitting errors for the parent twice (see similar code in `typeck_with_fallback`)
333 let outer_def_id
= tcx
.closure_base_def_id(def_id
.to_def_id()).expect_local();
334 if outer_def_id
!= def_id
{
335 return tcx
.typeck(outer_def_id
);
339 let body
= hir
.body(hir
.body_owned_by(hir
.local_def_id_to_hir_id(def_id
)));
340 debug
!("visiting body for {:?}", def_id
);
341 EmitIgnoredResolutionErrors
::new(tcx
).visit_body(body
);
342 (rustc_interface
::DEFAULT_QUERY_PROVIDERS
.typeck
)(tcx
, def_id
)
345 make_codegen_backend
: None
,
346 registry
: rustc_driver
::diagnostics_registry(),
350 crate fn create_resolver
<'a
>(
351 externs
: config
::Externs
,
352 queries
: &Queries
<'a
>,
354 ) -> Rc
<RefCell
<interface
::BoxedResolver
>> {
355 let extern_names
: Vec
<String
> = externs
357 .filter(|(_
, entry
)| entry
.add_prelude
)
358 .map(|(name
, _
)| name
)
362 let parts
= abort_on_err(queries
.expansion(), sess
).peek();
363 let resolver
= parts
.1.borrow();
365 // Before we actually clone it, let's force all the extern'd crates to
366 // actually be loaded, just in case they're only referred to inside
368 resolver
.borrow_mut().access(|resolver
| {
369 sess
.time("load_extern_crates", || {
370 for extern_name
in &extern_names
{
371 debug
!("loading extern crate {}", extern_name
);
372 if let Err(()) = resolver
373 .resolve_str_path_error(
377 LocalDefId { local_def_index: CRATE_DEF_INDEX }
.to_def_id(),
379 warn
!("unable to resolve external crate {} (do you have an unused `--extern` crate?)", extern_name
)
385 // Now we're good to clone the resolver because everything should be loaded
389 crate fn run_global_ctxt(
391 resolver
: Rc
<RefCell
<interface
::BoxedResolver
>>,
392 mut default_passes
: passes
::DefaultPassOption
,
393 manual_passes
: Vec
<String
>,
394 render_options
: RenderOptions
,
395 output_format
: OutputFormat
,
396 ) -> (clean
::Crate
, RenderOptions
, Cache
) {
397 // Certain queries assume that some checks were run elsewhere
398 // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425),
399 // so type-check everything other than function bodies in this crate before running lints.
401 // NOTE: this does not call `tcx.analysis()` so that we won't
402 // typeck function bodies or run the default rustc lints.
403 // (see `override_queries` in the `config`)
405 // HACK(jynelson) this calls an _extremely_ limited subset of `typeck`
406 // and might break if queries change their assumptions in the future.
408 // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes.
409 tcx
.sess
.time("item_types_checking", || {
410 for &module
in tcx
.hir().krate().modules
.keys() {
411 tcx
.ensure().check_mod_item_types(module
);
414 tcx
.sess
.abort_if_errors();
415 tcx
.sess
.time("missing_docs", || {
416 rustc_lint
::check_crate(tcx
, rustc_lint
::builtin
::MissingDoc
::new
);
418 tcx
.sess
.time("check_mod_attrs", || {
419 for &module
in tcx
.hir().krate().modules
.keys() {
420 tcx
.ensure().check_mod_attrs(module
);
423 rustc_passes
::stability
::check_unused_or_stable_features(tcx
);
425 let access_levels
= tcx
.privacy_access_levels(LOCAL_CRATE
);
426 // Convert from a HirId set to a DefId set since we don't always have easy access
427 // to the map from defid -> hirid
428 let access_levels
= AccessLevels
{
432 .map(|(&k
, &v
)| (tcx
.hir().local_def_id(k
).to_def_id(), v
))
436 let mut ctxt
= DocContext
{
439 param_env
: ParamEnv
::empty(),
440 external_traits
: Default
::default(),
441 active_extern_traits
: Default
::default(),
442 ty_substs
: Default
::default(),
443 lt_substs
: Default
::default(),
444 ct_substs
: Default
::default(),
445 impl_trait_bounds
: Default
::default(),
446 fake_def_ids
: Default
::default(),
447 generated_synthetics
: Default
::default(),
449 .all_traits(LOCAL_CRATE
)
452 .filter(|trait_def_id
| tcx
.trait_is_auto(*trait_def_id
))
454 module_trait_cache
: FxHashMap
::default(),
455 cache
: Cache
::new(access_levels
, render_options
.document_private
),
456 inlined
: FxHashSet
::default(),
461 // Small hack to force the Sized trait to be present.
463 // Note that in case of `#![no_core]`, the trait is not available.
464 if let Some(sized_trait_did
) = ctxt
.tcx
.lang_items().sized_trait() {
465 let mut sized_trait
= build_external_trait(&mut ctxt
, sized_trait_did
);
466 sized_trait
.is_auto
= true;
467 ctxt
.external_traits
.borrow_mut().insert(
469 TraitWithExtraInfo { trait_: sized_trait, is_spotlight: false }
,
473 debug
!("crate: {:?}", tcx
.hir().krate());
475 let mut krate
= tcx
.sess
.time("clean_crate", || clean
::krate(&mut ctxt
));
477 if let Some(ref m
) = krate
.module
{
478 if m
.doc_value().map(|d
| d
.is_empty()).unwrap_or(true) {
479 let help
= "The following guide may be of use:\n\
480 https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html";
481 tcx
.struct_lint_node(
482 crate::lint
::MISSING_CRATE_LEVEL_DOCS
,
483 DocContext
::as_local_hir_id(tcx
, m
.def_id
).unwrap(),
486 lint
.build("no documentation found for this crate's top-level module");
494 fn report_deprecated_attr(name
: &str, diag
: &rustc_errors
::Handler
, sp
: Span
) {
496 diag
.struct_span_warn(sp
, &format
!("the `#![doc({})]` attribute is deprecated", name
));
498 "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
499 for more information",
502 if name
== "no_default_passes" {
503 msg
.help("you may want to use `#![doc(document_private_items)]`");
504 } else if name
.starts_with("plugins") {
505 msg
.warn("`#![doc(plugins = \"...\")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>");
511 let parse_pass
= |name
: &str, sp
: Option
<Span
>| {
512 if let Some(pass
) = passes
::find_pass(name
) {
513 Some(ConditionalPass
::always(pass
))
515 let msg
= &format
!("ignoring unknown pass `{}`", name
);
516 let mut warning
= if let Some(sp
) = sp
{
517 tcx
.sess
.struct_span_warn(sp
, msg
)
519 tcx
.sess
.struct_warn(msg
)
521 if name
== "collapse-docs" {
522 warning
.note("the `collapse-docs` pass was removed in #80261 <https://github.com/rust-lang/rust/pull/80261>");
529 let mut manual_passes
: Vec
<_
> =
530 manual_passes
.into_iter().flat_map(|name
| parse_pass(&name
, None
)).collect();
532 // Process all of the crate attributes, extracting plugin metadata along
533 // with the passes which we are supposed to run.
534 for attr
in krate
.module
.as_ref().unwrap().attrs
.lists(sym
::doc
) {
535 let diag
= ctxt
.sess().diagnostic();
537 let name
= attr
.name_or_empty();
539 if name
== sym
::no_default_passes
{
540 report_deprecated_attr("no_default_passes", diag
, attr
.span());
541 if default_passes
== passes
::DefaultPassOption
::Default
{
542 default_passes
= passes
::DefaultPassOption
::None
;
545 } else if let Some(value
) = attr
.value_str() {
548 report_deprecated_attr("passes = \"...\"", diag
, attr
.span());
551 report_deprecated_attr("plugins = \"...\"", diag
, attr
.span());
556 for name
in value
.as_str().split_whitespace() {
557 let span
= attr
.name_value_literal_span().unwrap_or(attr
.span());
558 manual_passes
.extend(parse_pass(name
, Some(span
)));
562 if attr
.is_word() && name
== sym
::document_private_items
{
563 ctxt
.render_options
.document_private
= true;
567 let passes
= passes
::defaults(default_passes
).iter().copied().chain(manual_passes
);
568 info
!("Executing passes");
571 let run
= match p
.condition
{
573 WhenDocumentPrivate
=> ctxt
.render_options
.document_private
,
574 WhenNotDocumentPrivate
=> !ctxt
.render_options
.document_private
,
575 WhenNotDocumentHidden
=> !ctxt
.render_options
.document_hidden
,
578 debug
!("running pass {}", p
.pass
.name
);
579 krate
= ctxt
.tcx
.sess
.time(p
.pass
.name
, || (p
.pass
.run
)(krate
, &mut ctxt
));
583 ctxt
.sess().abort_if_errors();
585 let render_options
= ctxt
.render_options
;
586 let mut cache
= ctxt
.cache
;
587 krate
= tcx
.sess
.time("create_format_cache", || {
588 cache
.populate(krate
, tcx
, &render_options
.extern_html_root_urls
, &render_options
.output
)
591 // The main crate doc comments are always collapsed.
592 krate
.collapsed
= true;
594 (krate
, render_options
, cache
)
597 /// Due to <https://github.com/rust-lang/rust/pull/73566>,
598 /// the name resolution pass may find errors that are never emitted.
599 /// If typeck is called after this happens, then we'll get an ICE:
600 /// 'Res::Error found but not reported'. To avoid this, emit the errors now.
601 struct EmitIgnoredResolutionErrors
<'tcx
> {
605 impl<'tcx
> EmitIgnoredResolutionErrors
<'tcx
> {
606 fn new(tcx
: TyCtxt
<'tcx
>) -> Self {
611 impl<'tcx
> Visitor
<'tcx
> for EmitIgnoredResolutionErrors
<'tcx
> {
612 type Map
= Map
<'tcx
>;
614 fn nested_visit_map(&mut self) -> NestedVisitorMap
<Self::Map
> {
615 // We need to recurse into nested closures,
616 // since those will fallback to the parent for type checking.
617 NestedVisitorMap
::OnlyBodies(self.tcx
.hir())
620 fn visit_path(&mut self, path
: &'tcx Path
<'_
>, _id
: HirId
) {
621 debug
!("visiting path {:?}", path
);
622 if path
.res
== Res
::Err
{
623 // We have less context here than in rustc_resolve,
624 // so we can only emit the name and span.
625 // However we can give a hint that rustc_resolve will have more info.
627 "could not resolve path `{}`",
630 .map(|segment
| segment
.ident
.as_str().to_string())
634 let mut err
= rustc_errors
::struct_span_err
!(
638 "failed to resolve: {}",
641 err
.span_label(path
.span
, label
);
642 err
.note("this error was originally ignored because you are running `rustdoc`");
643 err
.note("try running again with `rustc` or `cargo check` and you may get a more detailed error");
646 // We could have an outer resolution that succeeded,
647 // but with generic parameters that failed.
648 // Recurse into the segments so we catch those too.
649 intravisit
::walk_path(self, path
);
653 /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter
654 /// for `impl Trait` in argument position.
655 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
656 crate enum ImplTraitParam
{
661 impl From
<DefId
> for ImplTraitParam
{
662 fn from(did
: DefId
) -> Self {
663 ImplTraitParam
::DefId(did
)
667 impl From
<u32> for ImplTraitParam
{
668 fn from(idx
: u32) -> Self {
669 ImplTraitParam
::ParamIndex(idx
)