1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use rustc_driver
::{self, driver, target_features, abort_on_err}
;
13 use rustc
::session
::{self, config}
;
14 use rustc
::hir
::def_id
::{DefId, DefIndex, DefIndexAddressSpace, CrateNum, LOCAL_CRATE}
;
15 use rustc
::hir
::def
::Def
;
16 use rustc
::hir
::{self, HirVec}
;
17 use rustc
::middle
::cstore
::CrateStore
;
18 use rustc
::middle
::privacy
::AccessLevels
;
19 use rustc
::ty
::{self, TyCtxt, AllArenas}
;
20 use rustc
::hir
::map
as hir_map
;
21 use rustc
::lint
::{self, LintPass}
;
22 use rustc
::session
::config
::ErrorOutputType
;
23 use rustc
::util
::nodemap
::{FxHashMap, FxHashSet}
;
24 use rustc_resolve
as resolve
;
25 use rustc_metadata
::creader
::CrateLoader
;
26 use rustc_metadata
::cstore
::CStore
;
27 use rustc_target
::spec
::TargetTriple
;
29 use syntax
::ast
::{self, Ident}
;
30 use syntax
::source_map
;
31 use syntax
::edition
::Edition
;
32 use syntax
::feature_gate
::UnstableFeatures
;
33 use syntax
::json
::JsonEmitter
;
35 use syntax
::symbol
::keywords
;
36 use syntax_pos
::DUMMY_SP
;
38 use errors
::emitter
::{Emitter, EmitterWriter}
;
40 use std
::cell
::{RefCell, Cell}
;
42 use rustc_data_structures
::sync
::{self, Lrc}
;
44 use std
::path
::PathBuf
;
46 use visit_ast
::RustdocVisitor
;
48 use clean
::{get_path_for_type, Clean, MAX_DEF_ID, AttributesExt}
;
49 use html
::render
::RenderInfo
;
52 pub use rustc
::session
::config
::{Input, Options, CodegenOptions}
;
53 pub use rustc
::session
::search_paths
::SearchPaths
;
55 pub type ExternalPaths
= FxHashMap
<DefId
, (Vec
<String
>, clean
::TypeKind
)>;
57 pub struct DocContext
<'a
, 'tcx
: 'a
, 'rcx
: 'a
, 'cstore
: 'rcx
> {
58 pub tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
59 pub resolver
: &'a RefCell
<resolve
::Resolver
<'rcx
, 'cstore
>>,
60 /// The stack of module NodeIds up till this point
61 pub crate_name
: Option
<String
>,
62 pub cstore
: Rc
<CStore
>,
63 pub populated_all_crate_impls
: Cell
<bool
>,
64 // Note that external items for which `doc(hidden)` applies to are shown as
65 // non-reachable while local items aren't. This is because we're reusing
66 // the access levels from crateanalysis.
67 /// Later on moved into `clean::Crate`
68 pub access_levels
: RefCell
<AccessLevels
<DefId
>>,
69 /// Later on moved into `html::render::CACHE_KEY`
70 pub renderinfo
: RefCell
<RenderInfo
>,
71 /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
72 pub external_traits
: RefCell
<FxHashMap
<DefId
, clean
::Trait
>>,
73 /// Used while populating `external_traits` to ensure we don't process the same trait twice at
75 pub active_extern_traits
: RefCell
<Vec
<DefId
>>,
76 // The current set of type and lifetime substitutions,
77 // for expanding type aliases at the HIR level:
79 /// Table type parameter definition -> substituted type
80 pub ty_substs
: RefCell
<FxHashMap
<Def
, clean
::Type
>>,
81 /// Table node id of lifetime parameter definition -> substituted lifetime
82 pub lt_substs
: RefCell
<FxHashMap
<DefId
, clean
::Lifetime
>>,
83 /// Table DefId of `impl Trait` in argument position -> bounds
84 pub impl_trait_bounds
: RefCell
<FxHashMap
<DefId
, Vec
<clean
::GenericBound
>>>,
85 pub send_trait
: Option
<DefId
>,
86 pub fake_def_ids
: RefCell
<FxHashMap
<CrateNum
, DefId
>>,
87 pub all_fake_def_ids
: RefCell
<FxHashSet
<DefId
>>,
88 /// Maps (type_id, trait_id) -> auto trait impl
89 pub generated_synthetics
: RefCell
<FxHashSet
<(DefId
, DefId
)>>,
90 pub all_traits
: Vec
<DefId
>,
93 impl<'a
, 'tcx
, 'rcx
, 'cstore
> DocContext
<'a
, 'tcx
, 'rcx
, 'cstore
> {
94 pub fn sess(&self) -> &session
::Session
{
98 /// Call the closure with the given parameters set as
99 /// the substitutions for a type alias' RHS.
100 pub fn enter_alias
<F
, R
>(&self,
101 ty_substs
: FxHashMap
<Def
, clean
::Type
>,
102 lt_substs
: FxHashMap
<DefId
, clean
::Lifetime
>,
104 where F
: FnOnce() -> R
{
105 let (old_tys
, old_lts
) =
106 (mem
::replace(&mut *self.ty_substs
.borrow_mut(), ty_substs
),
107 mem
::replace(&mut *self.lt_substs
.borrow_mut(), lt_substs
));
109 *self.ty_substs
.borrow_mut() = old_tys
;
110 *self.lt_substs
.borrow_mut() = old_lts
;
114 // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
115 // refactoring either librustdoc or librustc. In particular, allowing new DefIds to be
116 // registered after the AST is constructed would require storing the defid mapping in a
117 // RefCell, decreasing the performance for normal compilation for very little gain.
119 // Instead, we construct 'fake' def ids, which start immediately after the last DefId in
120 // DefIndexAddressSpace::Low. In the Debug impl for clean::Item, we explicitly check for fake
121 // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
122 pub fn next_def_id(&self, crate_num
: CrateNum
) -> DefId
{
124 let next_id
= if crate_num
== LOCAL_CRATE
{
129 .next_id(DefIndexAddressSpace
::Low
)
132 .def_path_table(crate_num
)
133 .next_id(DefIndexAddressSpace
::Low
)
142 let mut fake_ids
= self.fake_def_ids
.borrow_mut();
144 let def_id
= fake_ids
.entry(crate_num
).or_insert(start_def_id
).clone();
149 index
: DefIndex
::from_array_index(
150 def_id
.index
.as_array_index() + 1,
151 def_id
.index
.address_space(),
156 MAX_DEF_ID
.with(|m
| {
158 .entry(def_id
.krate
.clone())
159 .or_insert(start_def_id
);
162 self.all_fake_def_ids
.borrow_mut().insert(def_id
);
167 pub fn get_real_ty
<F
>(&self,
170 real_name
: &Option
<Ident
>,
171 generics
: &ty
::Generics
,
173 where F
: Fn(DefId
) -> Def
{
174 let path
= get_path_for_type(self.tcx
, def_id
, def_ctor
);
175 let mut segments
= path
.segments
.into_vec();
176 let last
= segments
.pop().expect("segments were empty");
178 segments
.push(hir
::PathSegment
::new(
179 real_name
.unwrap_or(last
.ident
),
180 self.generics_to_path_params(generics
.clone()),
184 let new_path
= hir
::Path
{
187 segments
: HirVec
::from_vec(segments
),
191 id
: ast
::DUMMY_NODE_ID
,
192 node
: hir
::TyKind
::Path(hir
::QPath
::Resolved(None
, P(new_path
))),
194 hir_id
: hir
::DUMMY_HIR_ID
,
198 pub fn generics_to_path_params(&self, generics
: ty
::Generics
) -> hir
::GenericArgs
{
199 let mut args
= vec
![];
201 for param
in generics
.params
.iter() {
203 ty
::GenericParamDefKind
::Lifetime
=> {
204 let name
= if param
.name
== "" {
205 hir
::ParamName
::Plain(keywords
::StaticLifetime
.ident())
207 hir
::ParamName
::Plain(ast
::Ident
::from_interned_str(param
.name
))
210 args
.push(hir
::GenericArg
::Lifetime(hir
::Lifetime
{
211 id
: ast
::DUMMY_NODE_ID
,
213 name
: hir
::LifetimeName
::Param(name
),
216 ty
::GenericParamDefKind
::Type {..}
=> {
217 args
.push(hir
::GenericArg
::Type(self.ty_param_to_ty(param
.clone())));
223 args
: HirVec
::from_vec(args
),
224 bindings
: HirVec
::new(),
225 parenthesized
: false,
229 pub fn ty_param_to_ty(&self, param
: ty
::GenericParamDef
) -> hir
::Ty
{
230 debug
!("ty_param_to_ty({:?}) {:?}", param
, param
.def_id
);
232 id
: ast
::DUMMY_NODE_ID
,
233 node
: hir
::TyKind
::Path(hir
::QPath
::Resolved(
237 def
: Def
::TyParam(param
.def_id
),
238 segments
: HirVec
::from_vec(vec
![
239 hir
::PathSegment
::from_ident(Ident
::from_interned_str(param
.name
))
244 hir_id
: hir
::DUMMY_HIR_ID
,
249 pub trait DocAccessLevels
{
250 fn is_doc_reachable(&self, did
: DefId
) -> bool
;
253 impl DocAccessLevels
for AccessLevels
<DefId
> {
254 fn is_doc_reachable(&self, did
: DefId
) -> bool
{
259 /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
261 /// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one
262 /// will be created for the handler.
263 pub fn new_handler(error_format
: ErrorOutputType
, source_map
: Option
<Lrc
<source_map
::SourceMap
>>)
266 // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so
267 // stick to the defaults
268 let sessopts
= Options
::default();
269 let emitter
: Box
<dyn Emitter
+ sync
::Send
> = match error_format
{
270 ErrorOutputType
::HumanReadable(color_config
) => Box
::new(
271 EmitterWriter
::stderr(
273 source_map
.map(|cm
| cm
as _
),
275 sessopts
.debugging_opts
.teach
,
276 ).ui_testing(sessopts
.debugging_opts
.ui_testing
)
278 ErrorOutputType
::Json(pretty
) => {
279 let source_map
= source_map
.unwrap_or_else(
280 || Lrc
::new(source_map
::SourceMap
::new(sessopts
.file_path_mapping())));
286 ).ui_testing(sessopts
.debugging_opts
.ui_testing
)
289 ErrorOutputType
::Short(color_config
) => Box
::new(
290 EmitterWriter
::stderr(
292 source_map
.map(|cm
| cm
as _
),
298 errors
::Handler
::with_emitter_and_flags(
300 errors
::HandlerFlags
{
301 can_emit_warnings
: true,
302 treat_err_as_bug
: false,
303 report_delayed_bugs
: false,
304 external_macro_backtrace
: false,
310 pub fn run_core(search_paths
: SearchPaths
,
312 externs
: config
::Externs
,
314 triple
: Option
<TargetTriple
>,
315 maybe_sysroot
: Option
<PathBuf
>,
316 allow_warnings
: bool
,
317 crate_name
: Option
<String
>,
318 force_unstable_if_unmarked
: bool
,
321 error_format
: ErrorOutputType
,
322 cmd_lints
: Vec
<(String
, lint
::Level
)>,
323 lint_cap
: Option
<lint
::Level
>,
324 describe_lints
: bool
,
325 mut manual_passes
: Vec
<String
>,
326 mut default_passes
: passes
::DefaultPassOption
)
327 -> (clean
::Crate
, RenderInfo
, Vec
<String
>)
329 // Parse, resolve, and typecheck the given crate.
331 let cpath
= match input
{
332 Input
::File(ref p
) => Some(p
.clone()),
336 let intra_link_resolution_failure_name
= lint
::builtin
::INTRA_DOC_LINK_RESOLUTION_FAILURE
.name
;
337 let warnings_lint_name
= lint
::builtin
::WARNINGS
.name
;
338 let missing_docs
= rustc_lint
::builtin
::MISSING_DOCS
.name
;
340 // In addition to those specific lints, we also need to whitelist those given through
341 // command line, otherwise they'll get ignored and we don't want that.
342 let mut whitelisted_lints
= vec
![warnings_lint_name
.to_owned(),
343 intra_link_resolution_failure_name
.to_owned(),
344 missing_docs
.to_owned()];
346 whitelisted_lints
.extend(cmd_lints
.iter().map(|(lint
, _
)| lint
).cloned());
348 let lints
= lint
::builtin
::HardwiredLints
.get_lints()
350 .chain(rustc_lint
::SoftLints
.get_lints().into_iter())
352 if lint
.name
== warnings_lint_name
||
353 lint
.name
== intra_link_resolution_failure_name
{
356 Some((lint
.name_lower(), lint
::Allow
))
359 .chain(cmd_lints
.into_iter())
360 .collect
::<Vec
<_
>>();
362 let host_triple
= TargetTriple
::from_triple(config
::host_triple());
363 // plays with error output here!
364 let sessopts
= config
::Options
{
367 crate_types
: vec
![config
::CrateType
::Rlib
],
368 lint_opts
: if !allow_warnings
{
373 lint_cap
: Some(lint_cap
.unwrap_or_else(|| lint
::Forbid
)),
376 target_triple
: triple
.unwrap_or(host_triple
),
377 // Ensure that rustdoc works even if rustc is feature-staged
378 unstable_features
: UnstableFeatures
::Allow
,
379 actually_rustdoc
: true,
380 debugging_opts
: config
::DebuggingOptions
{
381 force_unstable_if_unmarked
,
382 ..config
::basic_debugging_options()
389 driver
::spawn_thread_pool(sessopts
, move |sessopts
| {
390 let source_map
= Lrc
::new(source_map
::SourceMap
::new(sessopts
.file_path_mapping()));
391 let diagnostic_handler
= new_handler(error_format
, Some(source_map
.clone()));
393 let mut sess
= session
::build_session_(
394 sessopts
, cpath
, diagnostic_handler
, source_map
,
397 lint
::builtin
::HardwiredLints
.get_lints()
399 .chain(rustc_lint
::SoftLints
.get_lints().into_iter())
401 // We don't want to whitelist *all* lints so let's
402 // ignore those ones.
403 if whitelisted_lints
.iter().any(|l
| &lint
.name
== l
) {
410 sess
.driver_lint_caps
.insert(lint
::LintId
::of(l
),
414 let codegen_backend
= rustc_driver
::get_codegen_backend(&sess
);
415 let cstore
= Rc
::new(CStore
::new(codegen_backend
.metadata_loader()));
416 rustc_lint
::register_builtins(&mut sess
.lint_store
.borrow_mut(), Some(&sess
));
418 let mut cfg
= config
::build_configuration(&sess
, config
::parse_cfgspecs(cfgs
));
419 target_features
::add_configuration(&mut cfg
, &sess
, &*codegen_backend
);
420 sess
.parse_sess
.config
= cfg
;
422 let control
= &driver
::CompileController
::basic();
424 let krate
= panictry
!(driver
::phase_1_parse_input(control
, &sess
, &input
));
426 let name
= match crate_name
{
427 Some(ref crate_name
) => crate_name
.clone(),
428 None
=> ::rustc_codegen_utils
::link
::find_crate_name(Some(&sess
), &krate
.attrs
, &input
),
431 let mut crate_loader
= CrateLoader
::new(&sess
, &cstore
, &name
);
433 let resolver_arenas
= resolve
::Resolver
::arenas();
434 let result
= driver
::phase_2_configure_and_expand_inner(&sess
,
440 resolve
::MakeGlobMap
::No
,
444 let driver
::InnerExpansionResult
{
448 } = abort_on_err(result
, &sess
);
450 // We need to hold on to the complete resolver, so we clone everything
451 // for the analysis passes to use. Suboptimal, but necessary in the
452 // current architecture.
453 let defs
= resolver
.definitions
.clone();
454 let resolutions
= ty
::Resolutions
{
455 freevars
: resolver
.freevars
.clone(),
456 export_map
: resolver
.export_map
.clone(),
457 trait_map
: resolver
.trait_map
.clone(),
458 maybe_unused_trait_imports
: resolver
.maybe_unused_trait_imports
.clone(),
459 maybe_unused_extern_crates
: resolver
.maybe_unused_extern_crates
.clone(),
461 let analysis
= ty
::CrateAnalysis
{
462 access_levels
: Lrc
::new(AccessLevels
::default()),
463 name
: name
.to_string(),
464 glob_map
: if resolver
.make_glob_map { Some(resolver.glob_map.clone()) }
else { None }
,
467 let arenas
= AllArenas
::new();
468 let hir_map
= hir_map
::map_crate(&sess
, &*cstore
, &mut hir_forest
, &defs
);
469 let output_filenames
= driver
::build_output_filenames(&input
,
475 let resolver
= RefCell
::new(resolver
);
476 abort_on_err(driver
::phase_3_run_analysis_passes(&*codegen_backend
,
486 |tcx
, analysis
, _
, result
| {
488 sess
.fatal("Compilation failed, aborting rustdoc");
491 let ty
::CrateAnalysis { access_levels, .. }
= analysis
;
493 // Convert from a NodeId set to a DefId set since we don't always have easy access
494 // to the map from defid -> nodeid
495 let access_levels
= AccessLevels
{
496 map
: access_levels
.map
.iter()
497 .map(|(&k
, &v
)| (tcx
.hir
.local_def_id(k
), v
))
501 let send_trait
= if crate_name
== Some("core".to_string()) {
502 clean
::path_to_def_local(&tcx
, &["marker", "Send"])
504 clean
::path_to_def(&tcx
, &["core", "marker", "Send"])
507 let ctxt
= DocContext
{
511 cstore
: cstore
.clone(),
512 populated_all_crate_impls
: Cell
::new(false),
513 access_levels
: RefCell
::new(access_levels
),
514 external_traits
: Default
::default(),
515 active_extern_traits
: Default
::default(),
516 renderinfo
: Default
::default(),
517 ty_substs
: Default
::default(),
518 lt_substs
: Default
::default(),
519 impl_trait_bounds
: Default
::default(),
520 send_trait
: send_trait
,
521 fake_def_ids
: RefCell
::new(FxHashMap()),
522 all_fake_def_ids
: RefCell
::new(FxHashSet()),
523 generated_synthetics
: RefCell
::new(FxHashSet()),
524 all_traits
: tcx
.all_traits(LOCAL_CRATE
).to_vec(),
526 debug
!("crate: {:?}", tcx
.hir
.krate());
529 let mut v
= RustdocVisitor
::new(&ctxt
);
530 v
.visit(tcx
.hir
.krate());
534 fn report_deprecated_attr(name
: &str, diag
: &errors
::Handler
) {
535 let mut msg
= diag
.struct_warn(&format
!("the `#![doc({})]` attribute is \
536 considered deprecated", name
));
537 msg
.warn("please see https://github.com/rust-lang/rust/issues/44136");
539 if name
== "no_default_passes" {
540 msg
.help("you may want to use `#![doc(document_private_items)]`");
546 // Process all of the crate attributes, extracting plugin metadata along
547 // with the passes which we are supposed to run.
548 for attr
in krate
.module
.as_ref().unwrap().attrs
.lists("doc") {
549 let diag
= ctxt
.sess().diagnostic();
551 let name
= attr
.name().map(|s
| s
.as_str());
552 let name
= name
.as_ref().map(|s
| &s
[..]);
554 if name
== Some("no_default_passes") {
555 report_deprecated_attr("no_default_passes", diag
);
556 if default_passes
== passes
::DefaultPassOption
::Default
{
557 default_passes
= passes
::DefaultPassOption
::None
;
560 } else if let Some(value
) = attr
.value_str() {
561 let sink
= match name
{
563 report_deprecated_attr("passes = \"...\"", diag
);
567 report_deprecated_attr("plugins = \"...\"", diag
);
568 eprintln
!("WARNING: #![doc(plugins = \"...\")] no longer functions; \
569 see CVE-2018-1000622");
574 for p
in value
.as_str().split_whitespace() {
575 sink
.push(p
.to_string());
579 if attr
.is_word() && name
== Some("document_private_items") {
580 if default_passes
== passes
::DefaultPassOption
::Default
{
581 default_passes
= passes
::DefaultPassOption
::Private
;
586 let mut passes
: Vec
<String
> =
587 passes
::defaults(default_passes
).iter().map(|p
| p
.to_string()).collect();
588 passes
.extend(manual_passes
);
590 for pass
in &passes
{
591 // the "unknown pass" error will be reported when late passes are run
592 if let Some(pass
) = passes
::find_pass(pass
).and_then(|p
| p
.early_fn()) {
593 krate
= pass(krate
, &ctxt
);
597 ctxt
.sess().abort_if_errors();
599 (krate
, ctxt
.renderinfo
.into_inner(), passes
)