]> git.proxmox.com Git - rustc.git/blame - src/librustdoc/core.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / src / librustdoc / core.rs
CommitLineData
ba9703b0
XL
1use rustc_ast::ast::CRATE_NODE_ID;
2use rustc_attr as attr;
dfeec247 3use rustc_data_structures::fx::{FxHashMap, FxHashSet};
532ac7d7 4use rustc_driver::abort_on_err;
ba9703b0
XL
5use rustc_errors::emitter::{Emitter, EmitterWriter};
6use rustc_errors::json::JsonEmitter;
60c5eb7d 7use rustc_feature::UnstableFeatures;
dfeec247
XL
8use rustc_hir::def::Namespace::TypeNS;
9use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE};
10use rustc_hir::HirId;
60c5eb7d 11use rustc_interface::interface;
ba9703b0
XL
12use rustc_middle::middle::cstore::CrateStore;
13use rustc_middle::middle::privacy::AccessLevels;
14use rustc_middle::ty::{Ty, TyCtxt};
85aaf69f 15use rustc_resolve as resolve;
ba9703b0 16use rustc_session::config::ErrorOutputType;
dfeec247 17use rustc_session::lint;
ba9703b0
XL
18use rustc_session::DiagnosticOutput;
19use rustc_session::{config, Session};
dfeec247
XL
20use rustc_span::source_map;
21use rustc_span::symbol::sym;
22use rustc_span::DUMMY_SP;
1a4d82fc 23
60c5eb7d 24use rustc_data_structures::sync::{self, Lrc};
0bf4aa26 25use std::cell::RefCell;
9e0c209e 26use std::mem;
532ac7d7 27use std::rc::Rc;
1a4d82fc 28
9fa01778 29use crate::clean;
60c5eb7d
XL
30use crate::clean::{AttributesExt, MAX_DEF_ID};
31use crate::config::{Options as RustdocOptions, RenderOptions};
9fa01778
XL
32use crate::html::render::RenderInfo;
33
60c5eb7d 34use crate::passes::{self, Condition::*, ConditionalPass};
1a4d82fc 35
ba9703b0
XL
36pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options};
37pub use rustc_session::search_paths::SearchPath;
85aaf69f 38
476ff2be 39pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
1a4d82fc 40
532ac7d7 41pub struct DocContext<'tcx> {
dc9dc135 42 pub tcx: TyCtxt<'tcx>,
416331ca 43 pub resolver: Rc<RefCell<interface::BoxedResolver>>,
a7813a04
XL
44 /// Later on moved into `html::render::CACHE_KEY`
45 pub renderinfo: RefCell<RenderInfo>,
46 /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
416331ca 47 pub external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>,
0531ce1d
XL
48 /// Used while populating `external_traits` to ensure we don't process the same trait twice at
49 /// the same time.
416331ca 50 pub active_extern_traits: RefCell<FxHashSet<DefId>>,
9e0c209e
SL
51 // The current set of type and lifetime substitutions,
52 // for expanding type aliases at the HIR level:
48663c56
XL
53 /// Table `DefId` of type parameter -> substituted type
54 pub ty_substs: RefCell<FxHashMap<DefId, clean::Type>>,
55 /// Table `DefId` of lifetime parameter -> substituted lifetime
ea8adc8c 56 pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
48663c56
XL
57 /// Table `DefId` of const parameter -> substituted const
58 pub ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>,
e1599b0c
XL
59 /// Table synthetic type parameter for `impl Trait` in argument position -> bounds
60 pub impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>,
0531ce1d
XL
61 pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
62 pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
48663c56
XL
63 /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
64 // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set.
65 pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>,
dc9dc135 66 pub auto_traits: Vec<DefId>,
476ff2be 67}
1a4d82fc 68
532ac7d7 69impl<'tcx> DocContext<'tcx> {
ba9703b0 70 pub fn sess(&self) -> &Session {
476ff2be 71 &self.tcx.sess
1a4d82fc 72 }
9e0c209e 73
532ac7d7 74 pub fn enter_resolver<F, R>(&self, f: F) -> R
60c5eb7d
XL
75 where
76 F: FnOnce(&mut resolve::Resolver<'_>) -> R,
77 {
416331ca 78 self.resolver.borrow_mut().access(f)
532ac7d7
XL
79 }
80
9e0c209e
SL
81 /// Call the closure with the given parameters set as
82 /// the substitutions for a type alias' RHS.
60c5eb7d
XL
83 pub fn enter_alias<F, R>(
84 &self,
85 ty_substs: FxHashMap<DefId, clean::Type>,
86 lt_substs: FxHashMap<DefId, clean::Lifetime>,
87 ct_substs: FxHashMap<DefId, clean::Constant>,
88 f: F,
89 ) -> R
90 where
91 F: FnOnce() -> R,
92 {
9fa01778
XL
93 let (old_tys, old_lts, old_cts) = (
94 mem::replace(&mut *self.ty_substs.borrow_mut(), ty_substs),
95 mem::replace(&mut *self.lt_substs.borrow_mut(), lt_substs),
96 mem::replace(&mut *self.ct_substs.borrow_mut(), ct_substs),
97 );
9e0c209e
SL
98 let r = f();
99 *self.ty_substs.borrow_mut() = old_tys;
100 *self.lt_substs.borrow_mut() = old_lts;
9fa01778 101 *self.ct_substs.borrow_mut() = old_cts;
9e0c209e
SL
102 r
103 }
b7449926
XL
104
105 // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
ba9703b0 106 // refactoring either librustdoc or librustc_middle. In particular, allowing new DefIds to be
b7449926
XL
107 // registered after the AST is constructed would require storing the defid mapping in a
108 // RefCell, decreasing the performance for normal compilation for very little gain.
109 //
48663c56
XL
110 // Instead, we construct 'fake' def ids, which start immediately after the last DefId.
111 // In the Debug impl for clean::Item, we explicitly check for fake
b7449926
XL
112 // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
113 pub fn next_def_id(&self, crate_num: CrateNum) -> DefId {
114 let start_def_id = {
115 let next_id = if crate_num == LOCAL_CRATE {
60c5eb7d 116 self.tcx.hir().definitions().def_path_table().next_id()
b7449926 117 } else {
e74abb32 118 self.enter_resolver(|r| r.cstore().def_path_table(crate_num).next_id())
b7449926
XL
119 };
120
60c5eb7d 121 DefId { krate: crate_num, index: next_id }
b7449926
XL
122 };
123
124 let mut fake_ids = self.fake_def_ids.borrow_mut();
125
dfeec247 126 let def_id = *fake_ids.entry(crate_num).or_insert(start_def_id);
b7449926
XL
127 fake_ids.insert(
128 crate_num,
60c5eb7d 129 DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) },
b7449926
XL
130 );
131
132 MAX_DEF_ID.with(|m| {
60c5eb7d 133 m.borrow_mut().entry(def_id.krate.clone()).or_insert(start_def_id);
b7449926
XL
134 });
135
136 self.all_fake_def_ids.borrow_mut().insert(def_id);
137
dfeec247 138 def_id
b7449926
XL
139 }
140
0bf4aa26
XL
141 /// Like the function of the same name on the HIR map, but skips calling it on fake DefIds.
142 /// (This avoids a slice-index-out-of-bounds panic.)
532ac7d7
XL
143 pub fn as_local_hir_id(&self, def_id: DefId) -> Option<HirId> {
144 if self.all_fake_def_ids.borrow().contains(&def_id) {
145 None
146 } else {
147 self.tcx.hir().as_local_hir_id(def_id)
148 }
149 }
1a4d82fc 150
416331ca 151 pub fn stability(&self, id: HirId) -> Option<attr::Stability> {
60c5eb7d
XL
152 self.tcx
153 .hir()
154 .opt_local_def_id(id)
ba9703b0 155 .and_then(|def_id| self.tcx.lookup_stability(def_id.to_def_id()))
60c5eb7d 156 .cloned()
416331ca 157 }
1a4d82fc 158
416331ca 159 pub fn deprecation(&self, id: HirId) -> Option<attr::Deprecation> {
ba9703b0
XL
160 self.tcx
161 .hir()
162 .opt_local_def_id(id)
163 .and_then(|def_id| self.tcx.lookup_deprecation(def_id.to_def_id()))
a7813a04
XL
164 }
165}
1a4d82fc 166
94b46f34
XL
167/// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
168///
b7449926 169/// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one
94b46f34 170/// will be created for the handler.
60c5eb7d
XL
171pub fn new_handler(
172 error_format: ErrorOutputType,
173 source_map: Option<Lrc<source_map::SourceMap>>,
dfeec247
XL
174 debugging_opts: &DebuggingOptions,
175) -> rustc_errors::Handler {
94b46f34 176 let emitter: Box<dyn Emitter + sync::Send> = match error_format {
48663c56
XL
177 ErrorOutputType::HumanReadable(kind) => {
178 let (short, color_config) = kind.unzip();
179 Box::new(
180 EmitterWriter::stderr(
181 color_config,
74b04a01 182 source_map.map(|sm| sm as _),
48663c56 183 short,
dfeec247
XL
184 debugging_opts.teach,
185 debugging_opts.terminal_width,
e1599b0c 186 false,
60c5eb7d 187 )
ba9703b0 188 .ui_testing(debugging_opts.ui_testing),
48663c56 189 )
60c5eb7d 190 }
48663c56 191 ErrorOutputType::Json { pretty, json_rendered } => {
60c5eb7d 192 let source_map = source_map.unwrap_or_else(|| {
dfeec247 193 Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty()))
60c5eb7d 194 });
94b46f34 195 Box::new(
60c5eb7d 196 JsonEmitter::stderr(None, source_map, pretty, json_rendered, false)
ba9703b0 197 .ui_testing(debugging_opts.ui_testing),
94b46f34 198 )
60c5eb7d 199 }
94b46f34
XL
200 };
201
dfeec247 202 rustc_errors::Handler::with_emitter_and_flags(
94b46f34 203 emitter,
dfeec247 204 debugging_opts.diagnostic_handler_flags(true),
94b46f34
XL
205 )
206}
207
416331ca 208pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions) {
1a4d82fc
JJ
209 // Parse, resolve, and typecheck the given crate.
210
a1dfa0c6
XL
211 let RustdocOptions {
212 input,
213 crate_name,
e1599b0c 214 proc_macro_crate,
a1dfa0c6
XL
215 error_format,
216 libs,
217 externs,
e74abb32 218 mut cfgs,
a1dfa0c6
XL
219 codegen_options,
220 debugging_options,
221 target,
222 edition,
223 maybe_sysroot,
224 lint_opts,
225 describe_lints,
226 lint_cap,
227 mut default_passes,
60c5eb7d
XL
228 mut document_private,
229 document_hidden,
a1dfa0c6
XL
230 mut manual_passes,
231 display_warnings,
232 render_options,
ba9703b0 233 output_format,
a1dfa0c6
XL
234 ..
235 } = options;
236
60c5eb7d
XL
237 let extern_names: Vec<String> = externs
238 .iter()
239 .filter(|(_, entry)| entry.add_prelude)
240 .map(|(name, _)| name)
241 .cloned()
242 .collect();
243
244 // Add the doc cfg into the doc build.
245 cfgs.push("doc".to_string());
e74abb32 246
a1dfa0c6
XL
247 let cpath = Some(input.clone());
248 let input = Input::File(input);
1a4d82fc 249
94b46f34
XL
250 let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name;
251 let warnings_lint_name = lint::builtin::WARNINGS.name;
252 let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;
0bf4aa26 253 let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name;
a1dfa0c6 254 let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
ba9703b0 255 let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name;
8faf50e0
XL
256
257 // In addition to those specific lints, we also need to whitelist those given through
258 // command line, otherwise they'll get ignored and we don't want that.
60c5eb7d
XL
259 let mut whitelisted_lints = vec![
260 warnings_lint_name.to_owned(),
261 intra_link_resolution_failure_name.to_owned(),
262 missing_docs.to_owned(),
263 missing_doc_example.to_owned(),
264 private_doc_tests.to_owned(),
ba9703b0 265 no_crate_level_docs.to_owned(),
60c5eb7d 266 ];
8faf50e0 267
a1dfa0c6 268 whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
8faf50e0 269
532ac7d7 270 let lints = || {
e74abb32 271 lint::builtin::HardwiredLints::get_lints()
532ac7d7 272 .into_iter()
e74abb32 273 .chain(rustc_lint::SoftLints::get_lints().into_iter())
532ac7d7
XL
274 };
275
60c5eb7d
XL
276 let lint_opts = lints()
277 .filter_map(|lint| {
278 if lint.name == warnings_lint_name || lint.name == intra_link_resolution_failure_name {
279 None
280 } else {
281 Some((lint.name_lower(), lint::Allow))
282 }
283 })
284 .chain(lint_opts.into_iter())
285 .collect::<Vec<_>>();
286
287 let lint_caps = lints()
288 .filter_map(|lint| {
289 // We don't want to whitelist *all* lints so let's
290 // ignore those ones.
74b04a01 291 if whitelisted_lints.iter().any(|l| lint.name == l) {
60c5eb7d
XL
292 None
293 } else {
294 Some((lint::LintId::of(lint), lint::Allow))
295 }
296 })
297 .collect();
1a4d82fc 298
e1599b0c
XL
299 let crate_types = if proc_macro_crate {
300 vec![config::CrateType::ProcMacro]
301 } else {
302 vec![config::CrateType::Rlib]
303 };
83c7162d 304 // plays with error output here!
1a4d82fc 305 let sessopts = config::Options {
3b2f2976 306 maybe_sysroot,
a1dfa0c6 307 search_paths: libs,
e1599b0c 308 crate_types,
60c5eb7d 309 lint_opts: if !display_warnings { lint_opts } else { vec![] },
8faf50e0 310 lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
a1dfa0c6 311 cg: codegen_options,
3b2f2976 312 externs,
e1599b0c 313 target_triple: target,
60c5eb7d 314 unstable_features: UnstableFeatures::from_environment(),
c30ab7b3 315 actually_rustdoc: true,
dc9dc135 316 debugging_opts: debugging_options,
83c7162d
XL
317 error_format,
318 edition,
8faf50e0 319 describe_lints,
b7449926 320 ..Options::default()
a7813a04 321 };
94b46f34 322
532ac7d7
XL
323 let config = interface::Config {
324 opts: sessopts,
e74abb32 325 crate_cfg: interface::parse_cfgspecs(cfgs),
532ac7d7
XL
326 input,
327 input_path: cpath,
328 output_file: None,
329 output_dir: None,
330 file_loader: None,
331 diagnostic_output: DiagnosticOutput::Default,
332 stderr: None,
416331ca 333 crate_name,
532ac7d7 334 lint_caps,
e74abb32 335 register_lints: None,
60c5eb7d
XL
336 override_queries: None,
337 registry: rustc_driver::diagnostics_registry(),
532ac7d7 338 };
94b46f34 339
532ac7d7 340 interface::run_compiler_in_existing_thread_pool(config, |compiler| {
60c5eb7d
XL
341 compiler.enter(|queries| {
342 let sess = compiler.session();
343
344 // We need to hold on to the complete resolver, so we cause everything to be
345 // cloned for the analysis passes to use. Suboptimal, but necessary in the
346 // current architecture.
347 let resolver = {
348 let parts = abort_on_err(queries.expansion(), sess).peek();
349 let resolver = parts.1.borrow();
350
351 // Before we actually clone it, let's force all the extern'd crates to
352 // actually be loaded, just in case they're only referred to inside
353 // intra-doc-links
354 resolver.borrow_mut().access(|resolver| {
355 for extern_name in &extern_names {
356 resolver
357 .resolve_str_path_error(DUMMY_SP, extern_name, TypeNS, CRATE_NODE_ID)
358 .unwrap_or_else(|()| {
359 panic!("Unable to resolve external crate {}", extern_name)
360 });
361 }
362 });
94b46f34 363
60c5eb7d
XL
364 // Now we're good to clone the resolver because everything should be loaded
365 resolver.clone()
94b46f34 366 };
94b46f34 367
60c5eb7d
XL
368 if sess.has_errors() {
369 sess.fatal("Compilation failed, aborting rustdoc");
370 }
94b46f34 371
60c5eb7d
XL
372 let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take();
373
374 global_ctxt.enter(|tcx| {
375 tcx.analysis(LOCAL_CRATE).ok();
376
377 // Abort if there were any errors so far
378 sess.abort_if_errors();
379
380 let access_levels = tcx.privacy_access_levels(LOCAL_CRATE);
381 // Convert from a HirId set to a DefId set since we don't always have easy access
382 // to the map from defid -> hirid
383 let access_levels = AccessLevels {
384 map: access_levels
385 .map
386 .iter()
387 .map(|(&k, &v)| (tcx.hir().local_def_id(k), v))
388 .collect(),
389 };
390
391 let mut renderinfo = RenderInfo::default();
392 renderinfo.access_levels = access_levels;
ba9703b0 393 renderinfo.output_format = output_format;
60c5eb7d
XL
394
395 let mut ctxt = DocContext {
396 tcx,
397 resolver,
398 external_traits: Default::default(),
399 active_extern_traits: Default::default(),
400 renderinfo: RefCell::new(renderinfo),
401 ty_substs: Default::default(),
402 lt_substs: Default::default(),
403 ct_substs: Default::default(),
404 impl_trait_bounds: Default::default(),
405 fake_def_ids: Default::default(),
406 all_fake_def_ids: Default::default(),
407 generated_synthetics: Default::default(),
408 auto_traits: tcx
409 .all_traits(LOCAL_CRATE)
410 .iter()
411 .cloned()
412 .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id))
413 .collect(),
414 };
415 debug!("crate: {:?}", tcx.hir().krate());
416
417 let mut krate = clean::krate(&mut ctxt);
418
ba9703b0
XL
419 if let Some(ref m) = krate.module {
420 if let None | Some("") = m.doc_value() {
421 let help = "The following guide may be of use:\n\
422 https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation\
423 .html";
424 tcx.struct_lint_node(
425 rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS,
426 ctxt.as_local_hir_id(m.def_id).unwrap(),
427 |lint| {
428 let mut diag = lint.build(
429 "no documentation found for this crate's top-level module",
430 );
431 diag.help(help);
432 diag.emit();
433 },
434 );
435 }
436 }
437
dfeec247 438 fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) {
60c5eb7d 439 let mut msg = diag.struct_warn(&format!(
ba9703b0 440 "the `#![doc({})]` attribute is considered deprecated",
60c5eb7d
XL
441 name
442 ));
74b04a01
XL
443 msg.warn(
444 "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
445 for more information",
446 );
60c5eb7d
XL
447
448 if name == "no_default_passes" {
449 msg.help("you may want to use `#![doc(document_private_items)]`");
450 }
b7449926 451
60c5eb7d 452 msg.emit();
b7449926
XL
453 }
454
60c5eb7d
XL
455 // Process all of the crate attributes, extracting plugin metadata along
456 // with the passes which we are supposed to run.
457 for attr in krate.module.as_ref().unwrap().attrs.lists(sym::doc) {
458 let diag = ctxt.sess().diagnostic();
459
460 let name = attr.name_or_empty();
461 if attr.is_word() {
462 if name == sym::no_default_passes {
463 report_deprecated_attr("no_default_passes", diag);
464 if default_passes == passes::DefaultPassOption::Default {
465 default_passes = passes::DefaultPassOption::None;
466 }
467 }
468 } else if let Some(value) = attr.value_str() {
469 let sink = match name {
470 sym::passes => {
471 report_deprecated_attr("passes = \"...\"", diag);
472 &mut manual_passes
473 }
474 sym::plugins => {
475 report_deprecated_attr("plugins = \"...\"", diag);
476 eprintln!(
477 "WARNING: `#![doc(plugins = \"...\")]` \
478 no longer functions; see CVE-2018-1000622"
479 );
480 continue;
481 }
482 _ => continue,
483 };
484 for name in value.as_str().split_whitespace() {
485 sink.push(name.to_string());
b7449926
XL
486 }
487 }
b7449926 488
60c5eb7d
XL
489 if attr.is_word() && name == sym::document_private_items {
490 document_private = true;
b7449926
XL
491 }
492 }
b7449926 493
60c5eb7d
XL
494 let passes = passes::defaults(default_passes).iter().copied().chain(
495 manual_passes.into_iter().flat_map(|name| {
496 if let Some(pass) = passes::find_pass(&name) {
497 Some(ConditionalPass::always(pass))
498 } else {
499 error!("unknown pass {}, skipping", name);
500 None
501 }
502 }),
503 );
b7449926 504
60c5eb7d 505 info!("Executing passes");
532ac7d7 506
60c5eb7d
XL
507 for p in passes {
508 let run = match p.condition {
509 Always => true,
510 WhenDocumentPrivate => document_private,
511 WhenNotDocumentPrivate => !document_private,
512 WhenNotDocumentHidden => !document_hidden,
513 };
514 if run {
515 debug!("running pass {}", p.pass.name);
516 krate = (p.pass.run)(krate, &ctxt);
517 }
518 }
b7449926 519
60c5eb7d 520 ctxt.sess().abort_if_errors();
b7449926 521
60c5eb7d
XL
522 (krate, ctxt.renderinfo.into_inner(), render_options)
523 })
532ac7d7 524 })
94b46f34 525 })
1a4d82fc 526}
e1599b0c
XL
527
528/// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter
529/// for `impl Trait` in argument position.
530#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
531pub enum ImplTraitParam {
532 DefId(DefId),
533 ParamIndex(u32),
534}
535
536impl From<DefId> for ImplTraitParam {
537 fn from(did: DefId) -> Self {
538 ImplTraitParam::DefId(did)
539 }
540}
541
542impl From<u32> for ImplTraitParam {
543 fn from(idx: u32) -> Self {
544 ImplTraitParam::ParamIndex(idx)
545 }
546}