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