]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/core.rs
Auto merge of #57542 - Centril:rollup, r=Centril
[rust.git] / src / librustdoc / core.rs
1 use rustc_lint;
2 use rustc_driver::{self, driver, target_features, abort_on_err};
3 use rustc::session::{self, config};
4 use rustc::hir::def_id::{DefId, DefIndex, DefIndexAddressSpace, CrateNum, LOCAL_CRATE};
5 use rustc::hir::def::Def;
6 use rustc::hir::{self, HirVec};
7 use rustc::middle::cstore::CrateStore;
8 use rustc::middle::privacy::AccessLevels;
9 use rustc::ty::{self, TyCtxt, AllArenas};
10 use rustc::hir::map as hir_map;
11 use rustc::lint::{self, LintPass};
12 use rustc::session::config::ErrorOutputType;
13 use rustc::util::nodemap::{FxHashMap, FxHashSet};
14 use rustc_resolve as resolve;
15 use rustc_metadata::creader::CrateLoader;
16 use rustc_metadata::cstore::CStore;
17 use rustc_target::spec::TargetTriple;
18
19 use syntax::ast::{self, Ident, NodeId};
20 use syntax::source_map;
21 use syntax::feature_gate::UnstableFeatures;
22 use syntax::json::JsonEmitter;
23 use syntax::ptr::P;
24 use syntax::symbol::keywords;
25 use syntax_pos::DUMMY_SP;
26 use errors::{self, FatalError};
27 use errors::emitter::{Emitter, EmitterWriter};
28 use parking_lot::ReentrantMutex;
29
30 use std::cell::RefCell;
31 use std::mem;
32 use rustc_data_structures::sync::{self, Lrc};
33 use std::rc::Rc;
34 use std::sync::Arc;
35
36 use visit_ast::RustdocVisitor;
37 use config::{Options as RustdocOptions, RenderOptions};
38 use clean;
39 use clean::{get_path_for_type, Clean, MAX_DEF_ID, AttributesExt};
40 use html::render::RenderInfo;
41 use passes;
42
43 pub use rustc::session::config::{Input, Options, CodegenOptions};
44 pub use rustc::session::search_paths::SearchPath;
45
46 pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>;
47
48 pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> {
49     pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
50     pub resolver: &'a RefCell<resolve::Resolver<'rcx>>,
51     /// The stack of module NodeIds up till this point
52     pub crate_name: Option<String>,
53     pub cstore: Rc<CStore>,
54     // Note that external items for which `doc(hidden)` applies to are shown as
55     // non-reachable while local items aren't. This is because we're reusing
56     // the access levels from crateanalysis.
57     /// Later on moved into `html::render::CACHE_KEY`
58     pub renderinfo: RefCell<RenderInfo>,
59     /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY`
60     pub external_traits: Arc<ReentrantMutex<RefCell<FxHashMap<DefId, clean::Trait>>>>,
61     /// Used while populating `external_traits` to ensure we don't process the same trait twice at
62     /// the same time.
63     pub active_extern_traits: RefCell<Vec<DefId>>,
64     // The current set of type and lifetime substitutions,
65     // for expanding type aliases at the HIR level:
66
67     /// Table type parameter definition -> substituted type
68     pub ty_substs: RefCell<FxHashMap<Def, clean::Type>>,
69     /// Table node id of lifetime parameter definition -> substituted lifetime
70     pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>,
71     /// Table DefId of `impl Trait` in argument position -> bounds
72     pub impl_trait_bounds: RefCell<FxHashMap<DefId, Vec<clean::GenericBound>>>,
73     pub send_trait: Option<DefId>,
74     pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>,
75     pub all_fake_def_ids: RefCell<FxHashSet<DefId>>,
76     /// Maps (type_id, trait_id) -> auto trait impl
77     pub generated_synthetics: RefCell<FxHashSet<(DefId, DefId)>>,
78     pub all_traits: Vec<DefId>,
79 }
80
81 impl<'a, 'tcx, 'rcx> DocContext<'a, 'tcx, 'rcx> {
82     pub fn sess(&self) -> &session::Session {
83         &self.tcx.sess
84     }
85
86     /// Call the closure with the given parameters set as
87     /// the substitutions for a type alias' RHS.
88     pub fn enter_alias<F, R>(&self,
89                              ty_substs: FxHashMap<Def, clean::Type>,
90                              lt_substs: FxHashMap<DefId, clean::Lifetime>,
91                              f: F) -> R
92     where F: FnOnce() -> R {
93         let (old_tys, old_lts) =
94             (mem::replace(&mut *self.ty_substs.borrow_mut(), ty_substs),
95              mem::replace(&mut *self.lt_substs.borrow_mut(), lt_substs));
96         let r = f();
97         *self.ty_substs.borrow_mut() = old_tys;
98         *self.lt_substs.borrow_mut() = old_lts;
99         r
100     }
101
102     // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly
103     // refactoring either librustdoc or librustc. In particular, allowing new DefIds to be
104     // registered after the AST is constructed would require storing the defid mapping in a
105     // RefCell, decreasing the performance for normal compilation for very little gain.
106     //
107     // Instead, we construct 'fake' def ids, which start immediately after the last DefId in
108     // DefIndexAddressSpace::Low. In the Debug impl for clean::Item, we explicitly check for fake
109     // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds
110     pub fn next_def_id(&self, crate_num: CrateNum) -> DefId {
111         let start_def_id = {
112             let next_id = if crate_num == LOCAL_CRATE {
113                 self.tcx
114                     .hir()
115                     .definitions()
116                     .def_path_table()
117                     .next_id(DefIndexAddressSpace::Low)
118             } else {
119                 self.cstore
120                     .def_path_table(crate_num)
121                     .next_id(DefIndexAddressSpace::Low)
122             };
123
124             DefId {
125                 krate: crate_num,
126                 index: next_id,
127             }
128         };
129
130         let mut fake_ids = self.fake_def_ids.borrow_mut();
131
132         let def_id = fake_ids.entry(crate_num).or_insert(start_def_id).clone();
133         fake_ids.insert(
134             crate_num,
135             DefId {
136                 krate: crate_num,
137                 index: DefIndex::from_array_index(
138                     def_id.index.as_array_index() + 1,
139                     def_id.index.address_space(),
140                 ),
141             },
142         );
143
144         MAX_DEF_ID.with(|m| {
145             m.borrow_mut()
146                 .entry(def_id.krate.clone())
147                 .or_insert(start_def_id);
148         });
149
150         self.all_fake_def_ids.borrow_mut().insert(def_id);
151
152         def_id.clone()
153     }
154
155     /// Like the function of the same name on the HIR map, but skips calling it on fake DefIds.
156     /// (This avoids a slice-index-out-of-bounds panic.)
157     pub fn as_local_node_id(&self, def_id: DefId) -> Option<NodeId> {
158         if self.all_fake_def_ids.borrow().contains(&def_id) {
159             None
160         } else {
161             self.tcx.hir().as_local_node_id(def_id)
162         }
163     }
164
165     pub fn get_real_ty<F>(&self,
166                           def_id: DefId,
167                           def_ctor: &F,
168                           real_name: &Option<Ident>,
169                           generics: &ty::Generics,
170     ) -> hir::Ty
171     where F: Fn(DefId) -> Def {
172         let path = get_path_for_type(self.tcx, def_id, def_ctor);
173         let mut segments = path.segments.into_vec();
174         let last = segments.pop().expect("segments were empty");
175
176         segments.push(hir::PathSegment::new(
177             real_name.unwrap_or(last.ident),
178             None,
179             None,
180             self.generics_to_path_params(generics.clone()),
181             false,
182         ));
183
184         let new_path = hir::Path {
185             span: path.span,
186             def: path.def,
187             segments: HirVec::from_vec(segments),
188         };
189
190         hir::Ty {
191             id: ast::DUMMY_NODE_ID,
192             node: hir::TyKind::Path(hir::QPath::Resolved(None, P(new_path))),
193             span: DUMMY_SP,
194             hir_id: hir::DUMMY_HIR_ID,
195         }
196     }
197
198     pub fn generics_to_path_params(&self, generics: ty::Generics) -> hir::GenericArgs {
199         let mut args = vec![];
200
201         for param in generics.params.iter() {
202             match param.kind {
203                 ty::GenericParamDefKind::Lifetime => {
204                     let name = if param.name == "" {
205                         hir::ParamName::Plain(keywords::StaticLifetime.ident())
206                     } else {
207                         hir::ParamName::Plain(ast::Ident::from_interned_str(param.name))
208                     };
209
210                     args.push(hir::GenericArg::Lifetime(hir::Lifetime {
211                         id: ast::DUMMY_NODE_ID,
212                         span: DUMMY_SP,
213                         name: hir::LifetimeName::Param(name),
214                     }));
215                 }
216                 ty::GenericParamDefKind::Type {..} => {
217                     args.push(hir::GenericArg::Type(self.ty_param_to_ty(param.clone())));
218                 }
219             }
220         }
221
222         hir::GenericArgs {
223             args: HirVec::from_vec(args),
224             bindings: HirVec::new(),
225             parenthesized: false,
226         }
227     }
228
229     pub fn ty_param_to_ty(&self, param: ty::GenericParamDef) -> hir::Ty {
230         debug!("ty_param_to_ty({:?}) {:?}", param, param.def_id);
231         hir::Ty {
232             id: ast::DUMMY_NODE_ID,
233             node: hir::TyKind::Path(hir::QPath::Resolved(
234                 None,
235                 P(hir::Path {
236                     span: DUMMY_SP,
237                     def: Def::TyParam(param.def_id),
238                     segments: HirVec::from_vec(vec![
239                         hir::PathSegment::from_ident(Ident::from_interned_str(param.name))
240                     ]),
241                 }),
242             )),
243             span: DUMMY_SP,
244             hir_id: hir::DUMMY_HIR_ID,
245         }
246     }
247 }
248
249 pub trait DocAccessLevels {
250     fn is_doc_reachable(&self, did: DefId) -> bool;
251 }
252
253 impl DocAccessLevels for AccessLevels<DefId> {
254     fn is_doc_reachable(&self, did: DefId) -> bool {
255         self.is_public(did)
256     }
257 }
258
259 /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors.
260 ///
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,
264                    source_map: Option<Lrc<source_map::SourceMap>>,
265                    treat_err_as_bug: bool,
266                    ui_testing: bool,
267 ) -> errors::Handler {
268     // rustdoc doesn't override (or allow to override) anything from this that is relevant here, so
269     // stick to the defaults
270     let sessopts = Options::default();
271     let emitter: Box<dyn Emitter + sync::Send> = match error_format {
272         ErrorOutputType::HumanReadable(color_config) => Box::new(
273             EmitterWriter::stderr(
274                 color_config,
275                 source_map.map(|cm| cm as _),
276                 false,
277                 sessopts.debugging_opts.teach,
278             ).ui_testing(ui_testing)
279         ),
280         ErrorOutputType::Json(pretty) => {
281             let source_map = source_map.unwrap_or_else(
282                 || Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping())));
283             Box::new(
284                 JsonEmitter::stderr(
285                     None,
286                     source_map,
287                     pretty,
288                 ).ui_testing(ui_testing)
289             )
290         },
291         ErrorOutputType::Short(color_config) => Box::new(
292             EmitterWriter::stderr(
293                 color_config,
294                 source_map.map(|cm| cm as _),
295                 true,
296                 false)
297         ),
298     };
299
300     errors::Handler::with_emitter_and_flags(
301         emitter,
302         errors::HandlerFlags {
303             can_emit_warnings: true,
304             treat_err_as_bug,
305             report_delayed_bugs: false,
306             external_macro_backtrace: false,
307             ..Default::default()
308         },
309     )
310 }
311
312 pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOptions, Vec<String>) {
313     // Parse, resolve, and typecheck the given crate.
314
315     let RustdocOptions {
316         input,
317         crate_name,
318         error_format,
319         libs,
320         externs,
321         cfgs,
322         codegen_options,
323         debugging_options,
324         target,
325         edition,
326         maybe_sysroot,
327         lint_opts,
328         describe_lints,
329         lint_cap,
330         mut default_passes,
331         mut manual_passes,
332         display_warnings,
333         render_options,
334         ..
335     } = options;
336
337     let cpath = Some(input.clone());
338     let input = Input::File(input);
339
340     let intra_link_resolution_failure_name = lint::builtin::INTRA_DOC_LINK_RESOLUTION_FAILURE.name;
341     let warnings_lint_name = lint::builtin::WARNINGS.name;
342     let missing_docs = rustc_lint::builtin::MISSING_DOCS.name;
343     let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name;
344     let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name;
345
346     // In addition to those specific lints, we also need to whitelist those given through
347     // command line, otherwise they'll get ignored and we don't want that.
348     let mut whitelisted_lints = vec![warnings_lint_name.to_owned(),
349                                      intra_link_resolution_failure_name.to_owned(),
350                                      missing_docs.to_owned(),
351                                      missing_doc_example.to_owned(),
352                                      private_doc_tests.to_owned()];
353
354     whitelisted_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
355
356     let lints = lint::builtin::HardwiredLints.get_lints()
357                     .into_iter()
358                     .chain(rustc_lint::SoftLints.get_lints().into_iter())
359                     .filter_map(|lint| {
360                         if lint.name == warnings_lint_name ||
361                            lint.name == intra_link_resolution_failure_name {
362                             None
363                         } else {
364                             Some((lint.name_lower(), lint::Allow))
365                         }
366                     })
367                     .chain(lint_opts.into_iter())
368                     .collect::<Vec<_>>();
369
370     let host_triple = TargetTriple::from_triple(config::host_triple());
371     // plays with error output here!
372     let sessopts = config::Options {
373         maybe_sysroot,
374         search_paths: libs,
375         crate_types: vec![config::CrateType::Rlib],
376         lint_opts: if !display_warnings {
377             lints
378         } else {
379             vec![]
380         },
381         lint_cap: Some(lint_cap.unwrap_or_else(|| lint::Forbid)),
382         cg: codegen_options,
383         externs,
384         target_triple: target.unwrap_or(host_triple),
385         // Ensure that rustdoc works even if rustc is feature-staged
386         unstable_features: UnstableFeatures::Allow,
387         actually_rustdoc: true,
388         debugging_opts: debugging_options.clone(),
389         error_format,
390         edition,
391         describe_lints,
392         ..Options::default()
393     };
394     driver::spawn_thread_pool(sessopts, move |sessopts| {
395         let source_map = Lrc::new(source_map::SourceMap::new(sessopts.file_path_mapping()));
396         let diagnostic_handler = new_handler(error_format,
397                                              Some(source_map.clone()),
398                                              debugging_options.treat_err_as_bug,
399                                              debugging_options.ui_testing);
400
401         let mut sess = session::build_session_(
402             sessopts, cpath, diagnostic_handler, source_map,
403         );
404
405         lint::builtin::HardwiredLints.get_lints()
406                                      .into_iter()
407                                      .chain(rustc_lint::SoftLints.get_lints().into_iter())
408                                      .filter_map(|lint| {
409                                          // We don't want to whitelist *all* lints so let's
410                                          // ignore those ones.
411                                          if whitelisted_lints.iter().any(|l| &lint.name == l) {
412                                              None
413                                          } else {
414                                              Some(lint)
415                                          }
416                                      })
417                                      .for_each(|l| {
418                                          sess.driver_lint_caps.insert(lint::LintId::of(l),
419                                                                       lint::Allow);
420                                      });
421
422         let codegen_backend = rustc_driver::get_codegen_backend(&sess);
423         let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader()));
424         rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
425
426         let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs));
427         target_features::add_configuration(&mut cfg, &sess, &*codegen_backend);
428         sess.parse_sess.config = cfg;
429
430         let control = &driver::CompileController::basic();
431
432         let krate = match driver::phase_1_parse_input(control, &sess, &input) {
433             Ok(krate) => krate,
434             Err(mut e) => {
435                 e.emit();
436                 FatalError.raise();
437             }
438         };
439
440         let name = match crate_name {
441             Some(ref crate_name) => crate_name.clone(),
442             None => ::rustc_codegen_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input),
443         };
444
445         let mut crate_loader = CrateLoader::new(&sess, &cstore, &name);
446
447         let resolver_arenas = resolve::Resolver::arenas();
448         let result = driver::phase_2_configure_and_expand_inner(&sess,
449                                                         &cstore,
450                                                         krate,
451                                                         None,
452                                                         &name,
453                                                         None,
454                                                         resolve::MakeGlobMap::No,
455                                                         &resolver_arenas,
456                                                         &mut crate_loader,
457                                                         |_| Ok(()));
458         let driver::InnerExpansionResult {
459             mut hir_forest,
460             resolver,
461             ..
462         } = abort_on_err(result, &sess);
463
464         // We need to hold on to the complete resolver, so we clone everything
465         // for the analysis passes to use. Suboptimal, but necessary in the
466         // current architecture.
467         let defs = resolver.definitions.clone();
468         let resolutions = ty::Resolutions {
469             freevars: resolver.freevars.clone(),
470             export_map: resolver.export_map.clone(),
471             trait_map: resolver.trait_map.clone(),
472             maybe_unused_trait_imports: resolver.maybe_unused_trait_imports.clone(),
473             maybe_unused_extern_crates: resolver.maybe_unused_extern_crates.clone(),
474             extern_prelude: resolver.extern_prelude.iter().map(|(ident, entry)| {
475                 (ident.name, entry.introduced_by_item)
476             }).collect(),
477         };
478         let analysis = ty::CrateAnalysis {
479             glob_map: if resolver.make_glob_map { Some(resolver.glob_map.clone()) } else { None },
480         };
481
482         let mut arenas = AllArenas::new();
483         let hir_map = hir_map::map_crate(&sess, &*cstore, &mut hir_forest, &defs);
484         let output_filenames = driver::build_output_filenames(&input,
485                                                             &None,
486                                                             &None,
487                                                             &[],
488                                                             &sess);
489
490         let resolver = RefCell::new(resolver);
491         abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend,
492                                                         control,
493                                                         &sess,
494                                                         &*cstore,
495                                                         hir_map,
496                                                         analysis,
497                                                         resolutions,
498                                                         &mut arenas,
499                                                         &name,
500                                                         &output_filenames,
501                                                         |tcx, _, _, result| {
502             if result.is_err() {
503                 sess.fatal("Compilation failed, aborting rustdoc");
504             }
505
506             let access_levels = tcx.privacy_access_levels(LOCAL_CRATE);
507
508             // Convert from a NodeId set to a DefId set since we don't always have easy access
509             // to the map from defid -> nodeid
510             let access_levels = AccessLevels {
511                 map: access_levels.map.iter()
512                                     .map(|(&k, &v)| (tcx.hir().local_def_id(k), v))
513                                     .collect()
514             };
515
516             let send_trait = if crate_name == Some("core".to_string()) {
517                 clean::path_to_def_local(&tcx, &["marker", "Send"])
518             } else {
519                 clean::path_to_def(&tcx, &["core", "marker", "Send"])
520             };
521
522             let mut renderinfo = RenderInfo::default();
523             renderinfo.access_levels = access_levels;
524
525             let ctxt = DocContext {
526                 tcx,
527                 resolver: &resolver,
528                 crate_name,
529                 cstore: cstore.clone(),
530                 external_traits: Default::default(),
531                 active_extern_traits: Default::default(),
532                 renderinfo: RefCell::new(renderinfo),
533                 ty_substs: Default::default(),
534                 lt_substs: Default::default(),
535                 impl_trait_bounds: Default::default(),
536                 send_trait: send_trait,
537                 fake_def_ids: Default::default(),
538                 all_fake_def_ids: Default::default(),
539                 generated_synthetics: Default::default(),
540                 all_traits: tcx.all_traits(LOCAL_CRATE).to_vec(),
541             };
542             debug!("crate: {:?}", tcx.hir().krate());
543
544             let mut krate = {
545                 let mut v = RustdocVisitor::new(&ctxt);
546                 v.visit(tcx.hir().krate());
547                 v.clean(&ctxt)
548             };
549
550             fn report_deprecated_attr(name: &str, diag: &errors::Handler) {
551                 let mut msg = diag.struct_warn(&format!("the `#![doc({})]` attribute is \
552                                                          considered deprecated", name));
553                 msg.warn("please see https://github.com/rust-lang/rust/issues/44136");
554
555                 if name == "no_default_passes" {
556                     msg.help("you may want to use `#![doc(document_private_items)]`");
557                 }
558
559                 msg.emit();
560             }
561
562             // Process all of the crate attributes, extracting plugin metadata along
563             // with the passes which we are supposed to run.
564             for attr in krate.module.as_ref().unwrap().attrs.lists("doc") {
565                 let diag = ctxt.sess().diagnostic();
566
567                 let name = attr.name().map(|s| s.as_str());
568                 let name = name.as_ref().map(|s| &s[..]);
569                 if attr.is_word() {
570                     if name == Some("no_default_passes") {
571                         report_deprecated_attr("no_default_passes", diag);
572                         if default_passes == passes::DefaultPassOption::Default {
573                             default_passes = passes::DefaultPassOption::None;
574                         }
575                     }
576                 } else if let Some(value) = attr.value_str() {
577                     let sink = match name {
578                         Some("passes") => {
579                             report_deprecated_attr("passes = \"...\"", diag);
580                             &mut manual_passes
581                         },
582                         Some("plugins") => {
583                             report_deprecated_attr("plugins = \"...\"", diag);
584                             eprintln!("WARNING: #![doc(plugins = \"...\")] no longer functions; \
585                                       see CVE-2018-1000622");
586                             continue
587                         },
588                         _ => continue,
589                     };
590                     for p in value.as_str().split_whitespace() {
591                         sink.push(p.to_string());
592                     }
593                 }
594
595                 if attr.is_word() && name == Some("document_private_items") {
596                     if default_passes == passes::DefaultPassOption::Default {
597                         default_passes = passes::DefaultPassOption::Private;
598                     }
599                 }
600             }
601
602             let mut passes: Vec<String> =
603                 passes::defaults(default_passes).iter().map(|p| p.to_string()).collect();
604             passes.extend(manual_passes);
605
606             for pass in &passes {
607                 // the "unknown pass" error will be reported when late passes are run
608                 if let Some(pass) = passes::find_pass(pass).and_then(|p| p.early_fn()) {
609                     krate = pass(krate, &ctxt);
610                 }
611             }
612
613             ctxt.sess().abort_if_errors();
614
615             (krate, ctxt.renderinfo.into_inner(), render_options, passes)
616         }), &sess)
617     })
618 }