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