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