]>
Commit | Line | Data |
---|---|---|
ba9703b0 | 1 | use rustc_attr as attr; |
dfeec247 | 2 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
f9f354fc | 3 | use rustc_data_structures::sync::{self, Lrc}; |
532ac7d7 | 4 | use rustc_driver::abort_on_err; |
ba9703b0 XL |
5 | use rustc_errors::emitter::{Emitter, EmitterWriter}; |
6 | use rustc_errors::json::JsonEmitter; | |
60c5eb7d | 7 | use rustc_feature::UnstableFeatures; |
3dfed10e | 8 | use rustc_hir::def::{Namespace::TypeNS, Res}; |
f9f354fc | 9 | use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE}; |
dfeec247 | 10 | use rustc_hir::HirId; |
3dfed10e XL |
11 | use rustc_hir::{ |
12 | intravisit::{self, NestedVisitorMap, Visitor}, | |
13 | Path, | |
14 | }; | |
60c5eb7d | 15 | use rustc_interface::interface; |
3dfed10e | 16 | use rustc_middle::hir::map::Map; |
ba9703b0 XL |
17 | use rustc_middle::middle::cstore::CrateStore; |
18 | use rustc_middle::middle::privacy::AccessLevels; | |
19 | use rustc_middle::ty::{Ty, TyCtxt}; | |
85aaf69f | 20 | use rustc_resolve as resolve; |
f9f354fc | 21 | use rustc_session::config::{self, CrateType, ErrorOutputType}; |
dfeec247 | 22 | use rustc_session::lint; |
ba9703b0 | 23 | use rustc_session::DiagnosticOutput; |
f9f354fc | 24 | use rustc_session::Session; |
dfeec247 XL |
25 | use rustc_span::source_map; |
26 | use rustc_span::symbol::sym; | |
27 | use rustc_span::DUMMY_SP; | |
1a4d82fc | 28 | |
0bf4aa26 | 29 | use std::cell::RefCell; |
9e0c209e | 30 | use std::mem; |
532ac7d7 | 31 | use std::rc::Rc; |
1a4d82fc | 32 | |
9fa01778 | 33 | use crate::clean; |
60c5eb7d XL |
34 | use crate::clean::{AttributesExt, MAX_DEF_ID}; |
35 | use crate::config::{Options as RustdocOptions, RenderOptions}; | |
3dfed10e | 36 | use crate::config::{OutputFormat, RenderInfo}; |
60c5eb7d | 37 | use crate::passes::{self, Condition::*, ConditionalPass}; |
1a4d82fc | 38 | |
ba9703b0 XL |
39 | pub use rustc_session::config::{CodegenOptions, DebuggingOptions, Input, Options}; |
40 | pub use rustc_session::search_paths::SearchPath; | |
85aaf69f | 41 | |
476ff2be | 42 | pub type ExternalPaths = FxHashMap<DefId, (Vec<String>, clean::TypeKind)>; |
1a4d82fc | 43 | |
532ac7d7 | 44 | pub struct DocContext<'tcx> { |
dc9dc135 | 45 | pub tcx: TyCtxt<'tcx>, |
416331ca | 46 | pub resolver: Rc<RefCell<interface::BoxedResolver>>, |
3dfed10e | 47 | /// Later on moved into `CACHE_KEY` |
a7813a04 | 48 | pub renderinfo: RefCell<RenderInfo>, |
3dfed10e | 49 | /// Later on moved through `clean::Crate` into `CACHE_KEY` |
416331ca | 50 | pub external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>, |
0531ce1d XL |
51 | /// Used while populating `external_traits` to ensure we don't process the same trait twice at |
52 | /// the same time. | |
416331ca | 53 | pub active_extern_traits: RefCell<FxHashSet<DefId>>, |
9e0c209e SL |
54 | // The current set of type and lifetime substitutions, |
55 | // for expanding type aliases at the HIR level: | |
48663c56 XL |
56 | /// Table `DefId` of type parameter -> substituted type |
57 | pub ty_substs: RefCell<FxHashMap<DefId, clean::Type>>, | |
58 | /// Table `DefId` of lifetime parameter -> substituted lifetime | |
ea8adc8c | 59 | pub lt_substs: RefCell<FxHashMap<DefId, clean::Lifetime>>, |
48663c56 XL |
60 | /// Table `DefId` of const parameter -> substituted const |
61 | pub ct_substs: RefCell<FxHashMap<DefId, clean::Constant>>, | |
e1599b0c XL |
62 | /// Table synthetic type parameter for `impl Trait` in argument position -> bounds |
63 | pub impl_trait_bounds: RefCell<FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>>, | |
0531ce1d XL |
64 | pub fake_def_ids: RefCell<FxHashMap<CrateNum, DefId>>, |
65 | pub all_fake_def_ids: RefCell<FxHashSet<DefId>>, | |
48663c56 XL |
66 | /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. |
67 | // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. | |
68 | pub generated_synthetics: RefCell<FxHashSet<(Ty<'tcx>, DefId)>>, | |
dc9dc135 | 69 | pub auto_traits: Vec<DefId>, |
f035d41b XL |
70 | /// The options given to rustdoc that could be relevant to a pass. |
71 | pub render_options: RenderOptions, | |
3dfed10e XL |
72 | /// The traits in scope for a given module. |
73 | /// | |
74 | /// See `collect_intra_doc_links::traits_implemented_by` for more details. | |
75 | /// `map<module, set<trait>>` | |
76 | pub module_trait_cache: RefCell<FxHashMap<DefId, FxHashSet<DefId>>>, | |
476ff2be | 77 | } |
1a4d82fc | 78 | |
532ac7d7 | 79 | impl<'tcx> DocContext<'tcx> { |
ba9703b0 | 80 | pub fn sess(&self) -> &Session { |
476ff2be | 81 | &self.tcx.sess |
1a4d82fc | 82 | } |
9e0c209e | 83 | |
532ac7d7 | 84 | pub fn enter_resolver<F, R>(&self, f: F) -> R |
60c5eb7d XL |
85 | where |
86 | F: FnOnce(&mut resolve::Resolver<'_>) -> R, | |
87 | { | |
416331ca | 88 | self.resolver.borrow_mut().access(f) |
532ac7d7 XL |
89 | } |
90 | ||
9e0c209e SL |
91 | /// Call the closure with the given parameters set as |
92 | /// the substitutions for a type alias' RHS. | |
60c5eb7d XL |
93 | pub fn enter_alias<F, R>( |
94 | &self, | |
95 | ty_substs: FxHashMap<DefId, clean::Type>, | |
96 | lt_substs: FxHashMap<DefId, clean::Lifetime>, | |
97 | ct_substs: FxHashMap<DefId, clean::Constant>, | |
98 | f: F, | |
99 | ) -> R | |
100 | where | |
101 | F: FnOnce() -> R, | |
102 | { | |
9fa01778 XL |
103 | let (old_tys, old_lts, old_cts) = ( |
104 | mem::replace(&mut *self.ty_substs.borrow_mut(), ty_substs), | |
105 | mem::replace(&mut *self.lt_substs.borrow_mut(), lt_substs), | |
106 | mem::replace(&mut *self.ct_substs.borrow_mut(), ct_substs), | |
107 | ); | |
9e0c209e SL |
108 | let r = f(); |
109 | *self.ty_substs.borrow_mut() = old_tys; | |
110 | *self.lt_substs.borrow_mut() = old_lts; | |
9fa01778 | 111 | *self.ct_substs.borrow_mut() = old_cts; |
9e0c209e SL |
112 | r |
113 | } | |
b7449926 XL |
114 | |
115 | // This is an ugly hack, but it's the simplest way to handle synthetic impls without greatly | |
ba9703b0 | 116 | // refactoring either librustdoc or librustc_middle. In particular, allowing new DefIds to be |
b7449926 XL |
117 | // registered after the AST is constructed would require storing the defid mapping in a |
118 | // RefCell, decreasing the performance for normal compilation for very little gain. | |
119 | // | |
48663c56 XL |
120 | // Instead, we construct 'fake' def ids, which start immediately after the last DefId. |
121 | // In the Debug impl for clean::Item, we explicitly check for fake | |
b7449926 XL |
122 | // def ids, as we'll end up with a panic if we use the DefId Debug impl for fake DefIds |
123 | pub fn next_def_id(&self, crate_num: CrateNum) -> DefId { | |
124 | let start_def_id = { | |
3dfed10e XL |
125 | let num_def_ids = if crate_num == LOCAL_CRATE { |
126 | self.tcx.hir().definitions().def_path_table().num_def_ids() | |
b7449926 | 127 | } else { |
3dfed10e | 128 | self.enter_resolver(|r| r.cstore().num_def_ids(crate_num)) |
b7449926 XL |
129 | }; |
130 | ||
3dfed10e | 131 | DefId { krate: crate_num, index: DefIndex::from_usize(num_def_ids) } |
b7449926 XL |
132 | }; |
133 | ||
134 | let mut fake_ids = self.fake_def_ids.borrow_mut(); | |
135 | ||
dfeec247 | 136 | let def_id = *fake_ids.entry(crate_num).or_insert(start_def_id); |
b7449926 XL |
137 | fake_ids.insert( |
138 | crate_num, | |
60c5eb7d | 139 | DefId { krate: crate_num, index: DefIndex::from(def_id.index.index() + 1) }, |
b7449926 XL |
140 | ); |
141 | ||
142 | MAX_DEF_ID.with(|m| { | |
f9f354fc | 143 | m.borrow_mut().entry(def_id.krate).or_insert(start_def_id); |
b7449926 XL |
144 | }); |
145 | ||
146 | self.all_fake_def_ids.borrow_mut().insert(def_id); | |
147 | ||
dfeec247 | 148 | def_id |
b7449926 XL |
149 | } |
150 | ||
3dfed10e | 151 | /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds. |
0bf4aa26 | 152 | /// (This avoids a slice-index-out-of-bounds panic.) |
532ac7d7 XL |
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 { | |
3dfed10e | 157 | def_id.as_local().map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)) |
532ac7d7 XL |
158 | } |
159 | } | |
1a4d82fc | 160 | |
416331ca | 161 | pub fn stability(&self, id: HirId) -> Option<attr::Stability> { |
60c5eb7d XL |
162 | self.tcx |
163 | .hir() | |
164 | .opt_local_def_id(id) | |
ba9703b0 | 165 | .and_then(|def_id| self.tcx.lookup_stability(def_id.to_def_id())) |
60c5eb7d | 166 | .cloned() |
416331ca | 167 | } |
1a4d82fc | 168 | |
416331ca | 169 | pub fn deprecation(&self, id: HirId) -> Option<attr::Deprecation> { |
ba9703b0 XL |
170 | self.tcx |
171 | .hir() | |
172 | .opt_local_def_id(id) | |
173 | .and_then(|def_id| self.tcx.lookup_deprecation(def_id.to_def_id())) | |
a7813a04 XL |
174 | } |
175 | } | |
1a4d82fc | 176 | |
94b46f34 XL |
177 | /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. |
178 | /// | |
b7449926 | 179 | /// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one |
94b46f34 | 180 | /// will be created for the handler. |
60c5eb7d XL |
181 | pub fn new_handler( |
182 | error_format: ErrorOutputType, | |
183 | source_map: Option<Lrc<source_map::SourceMap>>, | |
dfeec247 XL |
184 | debugging_opts: &DebuggingOptions, |
185 | ) -> rustc_errors::Handler { | |
94b46f34 | 186 | let emitter: Box<dyn Emitter + sync::Send> = match error_format { |
48663c56 XL |
187 | ErrorOutputType::HumanReadable(kind) => { |
188 | let (short, color_config) = kind.unzip(); | |
189 | Box::new( | |
190 | EmitterWriter::stderr( | |
191 | color_config, | |
74b04a01 | 192 | source_map.map(|sm| sm as _), |
48663c56 | 193 | short, |
dfeec247 XL |
194 | debugging_opts.teach, |
195 | debugging_opts.terminal_width, | |
e1599b0c | 196 | false, |
60c5eb7d | 197 | ) |
ba9703b0 | 198 | .ui_testing(debugging_opts.ui_testing), |
48663c56 | 199 | ) |
60c5eb7d | 200 | } |
48663c56 | 201 | ErrorOutputType::Json { pretty, json_rendered } => { |
60c5eb7d | 202 | let source_map = source_map.unwrap_or_else(|| { |
dfeec247 | 203 | Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) |
60c5eb7d | 204 | }); |
94b46f34 | 205 | Box::new( |
f035d41b XL |
206 | JsonEmitter::stderr( |
207 | None, | |
208 | source_map, | |
209 | pretty, | |
210 | json_rendered, | |
211 | debugging_opts.terminal_width, | |
212 | false, | |
213 | ) | |
214 | .ui_testing(debugging_opts.ui_testing), | |
94b46f34 | 215 | ) |
60c5eb7d | 216 | } |
94b46f34 XL |
217 | }; |
218 | ||
dfeec247 | 219 | rustc_errors::Handler::with_emitter_and_flags( |
94b46f34 | 220 | emitter, |
dfeec247 | 221 | debugging_opts.diagnostic_handler_flags(true), |
94b46f34 XL |
222 | ) |
223 | } | |
224 | ||
f9f354fc XL |
225 | /// This function is used to setup the lint initialization. By default, in rustdoc, everything |
226 | /// is "allowed". Depending if we run in test mode or not, we want some of them to be at their | |
227 | /// default level. For example, the "INVALID_CODEBLOCK_ATTRIBUTES" lint is activated in both | |
228 | /// modes. | |
229 | /// | |
230 | /// A little detail easy to forget is that there is a way to set the lint level for all lints | |
231 | /// through the "WARNINGS" lint. To prevent this to happen, we set it back to its "normal" level | |
232 | /// inside this function. | |
233 | /// | |
234 | /// It returns a tuple containing: | |
235 | /// * Vector of tuples of lints' name and their associated "max" level | |
236 | /// * HashMap of lint id with their associated "max" level | |
1b1a35ee | 237 | pub(crate) fn init_lints<F>( |
f035d41b | 238 | mut allowed_lints: Vec<String>, |
f9f354fc XL |
239 | lint_opts: Vec<(String, lint::Level)>, |
240 | filter_call: F, | |
241 | ) -> (Vec<(String, lint::Level)>, FxHashMap<lint::LintId, lint::Level>) | |
242 | where | |
243 | F: Fn(&lint::Lint) -> Option<(String, lint::Level)>, | |
244 | { | |
245 | let warnings_lint_name = lint::builtin::WARNINGS.name; | |
246 | ||
f035d41b XL |
247 | allowed_lints.push(warnings_lint_name.to_owned()); |
248 | allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); | |
f9f354fc XL |
249 | |
250 | let lints = || { | |
251 | lint::builtin::HardwiredLints::get_lints() | |
252 | .into_iter() | |
253 | .chain(rustc_lint::SoftLints::get_lints().into_iter()) | |
254 | }; | |
255 | ||
256 | let lint_opts = lints() | |
257 | .filter_map(|lint| { | |
f035d41b XL |
258 | // Permit feature-gated lints to avoid feature errors when trying to |
259 | // allow all lints. | |
1b1a35ee | 260 | if lint.feature_gate.is_some() || allowed_lints.iter().any(|l| lint.name == l) { |
f9f354fc XL |
261 | None |
262 | } else { | |
263 | filter_call(lint) | |
264 | } | |
265 | }) | |
266 | .chain(lint_opts.into_iter()) | |
267 | .collect::<Vec<_>>(); | |
268 | ||
269 | let lint_caps = lints() | |
270 | .filter_map(|lint| { | |
f035d41b XL |
271 | // We don't want to allow *all* lints so let's ignore |
272 | // those ones. | |
273 | if allowed_lints.iter().any(|l| lint.name == l) { | |
f9f354fc XL |
274 | None |
275 | } else { | |
276 | Some((lint::LintId::of(lint), lint::Allow)) | |
277 | } | |
278 | }) | |
279 | .collect(); | |
280 | (lint_opts, lint_caps) | |
281 | } | |
282 | ||
3dfed10e XL |
283 | pub fn run_core( |
284 | options: RustdocOptions, | |
285 | ) -> (clean::Crate, RenderInfo, RenderOptions, Lrc<Session>) { | |
1a4d82fc JJ |
286 | // Parse, resolve, and typecheck the given crate. |
287 | ||
a1dfa0c6 XL |
288 | let RustdocOptions { |
289 | input, | |
290 | crate_name, | |
e1599b0c | 291 | proc_macro_crate, |
a1dfa0c6 XL |
292 | error_format, |
293 | libs, | |
294 | externs, | |
e74abb32 | 295 | mut cfgs, |
a1dfa0c6 | 296 | codegen_options, |
1b1a35ee | 297 | debugging_opts, |
a1dfa0c6 XL |
298 | target, |
299 | edition, | |
300 | maybe_sysroot, | |
301 | lint_opts, | |
302 | describe_lints, | |
303 | lint_cap, | |
3dfed10e XL |
304 | default_passes, |
305 | manual_passes, | |
a1dfa0c6 XL |
306 | display_warnings, |
307 | render_options, | |
ba9703b0 | 308 | output_format, |
a1dfa0c6 XL |
309 | .. |
310 | } = options; | |
311 | ||
60c5eb7d XL |
312 | let extern_names: Vec<String> = externs |
313 | .iter() | |
314 | .filter(|(_, entry)| entry.add_prelude) | |
315 | .map(|(name, _)| name) | |
316 | .cloned() | |
317 | .collect(); | |
318 | ||
319 | // Add the doc cfg into the doc build. | |
320 | cfgs.push("doc".to_string()); | |
e74abb32 | 321 | |
a1dfa0c6 XL |
322 | let cpath = Some(input.clone()); |
323 | let input = Input::File(input); | |
1a4d82fc | 324 | |
29967ef6 XL |
325 | let broken_intra_doc_links = lint::builtin::BROKEN_INTRA_DOC_LINKS.name; |
326 | let private_intra_doc_links = lint::builtin::PRIVATE_INTRA_DOC_LINKS.name; | |
94b46f34 | 327 | let missing_docs = rustc_lint::builtin::MISSING_DOCS.name; |
0bf4aa26 | 328 | let missing_doc_example = rustc_lint::builtin::MISSING_DOC_CODE_EXAMPLES.name; |
a1dfa0c6 | 329 | let private_doc_tests = rustc_lint::builtin::PRIVATE_DOC_TESTS.name; |
ba9703b0 | 330 | let no_crate_level_docs = rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS.name; |
3dfed10e | 331 | let invalid_codeblock_attributes_name = rustc_lint::builtin::INVALID_CODEBLOCK_ATTRIBUTES.name; |
29967ef6 | 332 | let invalid_html_tags = rustc_lint::builtin::INVALID_HTML_TAGS.name; |
1b1a35ee | 333 | let renamed_and_removed_lints = rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name; |
29967ef6 | 334 | let non_autolinks = rustc_lint::builtin::NON_AUTOLINKS.name; |
1b1a35ee | 335 | let unknown_lints = rustc_lint::builtin::UNKNOWN_LINTS.name; |
8faf50e0 | 336 | |
f035d41b | 337 | // In addition to those specific lints, we also need to allow those given through |
8faf50e0 | 338 | // command line, otherwise they'll get ignored and we don't want that. |
1b1a35ee | 339 | let lints_to_show = vec![ |
29967ef6 XL |
340 | broken_intra_doc_links.to_owned(), |
341 | private_intra_doc_links.to_owned(), | |
60c5eb7d XL |
342 | missing_docs.to_owned(), |
343 | missing_doc_example.to_owned(), | |
344 | private_doc_tests.to_owned(), | |
ba9703b0 | 345 | no_crate_level_docs.to_owned(), |
3dfed10e | 346 | invalid_codeblock_attributes_name.to_owned(), |
29967ef6 | 347 | invalid_html_tags.to_owned(), |
1b1a35ee XL |
348 | renamed_and_removed_lints.to_owned(), |
349 | unknown_lints.to_owned(), | |
29967ef6 | 350 | non_autolinks.to_owned(), |
60c5eb7d | 351 | ]; |
8faf50e0 | 352 | |
1b1a35ee | 353 | let (lint_opts, lint_caps) = init_lints(lints_to_show, lint_opts, |lint| { |
29967ef6 XL |
354 | // FIXME: why is this necessary? |
355 | if lint.name == broken_intra_doc_links || lint.name == invalid_codeblock_attributes_name { | |
f9f354fc XL |
356 | None |
357 | } else { | |
358 | Some((lint.name_lower(), lint::Allow)) | |
359 | } | |
360 | }); | |
1a4d82fc | 361 | |
f9f354fc XL |
362 | let crate_types = |
363 | if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; | |
83c7162d | 364 | // plays with error output here! |
1a4d82fc | 365 | let sessopts = config::Options { |
3b2f2976 | 366 | maybe_sysroot, |
a1dfa0c6 | 367 | search_paths: libs, |
e1599b0c | 368 | crate_types, |
60c5eb7d | 369 | lint_opts: if !display_warnings { lint_opts } else { vec![] }, |
1b1a35ee | 370 | lint_cap, |
a1dfa0c6 | 371 | cg: codegen_options, |
3b2f2976 | 372 | externs, |
e1599b0c | 373 | target_triple: target, |
60c5eb7d | 374 | unstable_features: UnstableFeatures::from_environment(), |
c30ab7b3 | 375 | actually_rustdoc: true, |
1b1a35ee | 376 | debugging_opts, |
83c7162d XL |
377 | error_format, |
378 | edition, | |
8faf50e0 | 379 | describe_lints, |
b7449926 | 380 | ..Options::default() |
a7813a04 | 381 | }; |
94b46f34 | 382 | |
532ac7d7 XL |
383 | let config = interface::Config { |
384 | opts: sessopts, | |
e74abb32 | 385 | crate_cfg: interface::parse_cfgspecs(cfgs), |
532ac7d7 XL |
386 | input, |
387 | input_path: cpath, | |
388 | output_file: None, | |
389 | output_dir: None, | |
390 | file_loader: None, | |
391 | diagnostic_output: DiagnosticOutput::Default, | |
392 | stderr: None, | |
416331ca | 393 | crate_name, |
532ac7d7 | 394 | lint_caps, |
e74abb32 | 395 | register_lints: None, |
3dfed10e XL |
396 | override_queries: Some(|_sess, providers, _external_providers| { |
397 | // Most lints will require typechecking, so just don't run them. | |
398 | providers.lint_mod = |_, _| {}; | |
399 | // Prevent `rustc_typeck::check_crate` from calling `typeck` on all bodies. | |
400 | providers.typeck_item_bodies = |_, _| {}; | |
401 | // hack so that `used_trait_imports` won't try to call typeck | |
402 | providers.used_trait_imports = |_, _| { | |
403 | lazy_static! { | |
404 | static ref EMPTY_SET: FxHashSet<LocalDefId> = FxHashSet::default(); | |
405 | } | |
406 | &EMPTY_SET | |
407 | }; | |
408 | // In case typeck does end up being called, don't ICE in case there were name resolution errors | |
409 | providers.typeck = move |tcx, def_id| { | |
410 | // Closures' tables come from their outermost function, | |
411 | // as they are part of the same "inference environment". | |
412 | // This avoids emitting errors for the parent twice (see similar code in `typeck_with_fallback`) | |
413 | let outer_def_id = tcx.closure_base_def_id(def_id.to_def_id()).expect_local(); | |
414 | if outer_def_id != def_id { | |
415 | return tcx.typeck(outer_def_id); | |
416 | } | |
417 | ||
418 | let hir = tcx.hir(); | |
419 | let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(def_id))); | |
420 | debug!("visiting body for {:?}", def_id); | |
421 | tcx.sess.time("emit_ignored_resolution_errors", || { | |
422 | EmitIgnoredResolutionErrors::new(tcx).visit_body(body); | |
423 | }); | |
424 | (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) | |
425 | }; | |
426 | }), | |
1b1a35ee | 427 | make_codegen_backend: None, |
60c5eb7d | 428 | registry: rustc_driver::diagnostics_registry(), |
532ac7d7 | 429 | }; |
94b46f34 | 430 | |
f035d41b | 431 | interface::create_compiler_and_run(config, |compiler| { |
60c5eb7d XL |
432 | compiler.enter(|queries| { |
433 | let sess = compiler.session(); | |
434 | ||
435 | // We need to hold on to the complete resolver, so we cause everything to be | |
436 | // cloned for the analysis passes to use. Suboptimal, but necessary in the | |
437 | // current architecture. | |
438 | let resolver = { | |
439 | let parts = abort_on_err(queries.expansion(), sess).peek(); | |
440 | let resolver = parts.1.borrow(); | |
441 | ||
442 | // Before we actually clone it, let's force all the extern'd crates to | |
443 | // actually be loaded, just in case they're only referred to inside | |
444 | // intra-doc-links | |
445 | resolver.borrow_mut().access(|resolver| { | |
3dfed10e XL |
446 | sess.time("load_extern_crates", || { |
447 | for extern_name in &extern_names { | |
1b1a35ee | 448 | debug!("loading extern crate {}", extern_name); |
3dfed10e XL |
449 | resolver |
450 | .resolve_str_path_error( | |
451 | DUMMY_SP, | |
452 | extern_name, | |
453 | TypeNS, | |
454 | LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id(), | |
455 | ) | |
456 | .unwrap_or_else(|()| { | |
457 | panic!("Unable to resolve external crate {}", extern_name) | |
458 | }); | |
459 | } | |
460 | }); | |
60c5eb7d | 461 | }); |
94b46f34 | 462 | |
60c5eb7d XL |
463 | // Now we're good to clone the resolver because everything should be loaded |
464 | resolver.clone() | |
94b46f34 | 465 | }; |
94b46f34 | 466 | |
60c5eb7d XL |
467 | if sess.has_errors() { |
468 | sess.fatal("Compilation failed, aborting rustdoc"); | |
469 | } | |
94b46f34 | 470 | |
60c5eb7d XL |
471 | let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess).take(); |
472 | ||
3dfed10e XL |
473 | let (krate, render_info, opts) = sess.time("run_global_ctxt", || { |
474 | global_ctxt.enter(|tcx| { | |
475 | run_global_ctxt( | |
476 | tcx, | |
477 | resolver, | |
478 | default_passes, | |
479 | manual_passes, | |
480 | render_options, | |
481 | output_format, | |
482 | ) | |
483 | }) | |
484 | }); | |
485 | (krate, render_info, opts, Lrc::clone(sess)) | |
486 | }) | |
487 | }) | |
488 | } | |
ba9703b0 | 489 | |
3dfed10e XL |
490 | fn run_global_ctxt( |
491 | tcx: TyCtxt<'_>, | |
492 | resolver: Rc<RefCell<interface::BoxedResolver>>, | |
493 | mut default_passes: passes::DefaultPassOption, | |
494 | mut manual_passes: Vec<String>, | |
495 | render_options: RenderOptions, | |
496 | output_format: Option<OutputFormat>, | |
497 | ) -> (clean::Crate, RenderInfo, RenderOptions) { | |
498 | // Certain queries assume that some checks were run elsewhere | |
499 | // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), | |
500 | // so type-check everything other than function bodies in this crate before running lints. | |
501 | ||
502 | // NOTE: this does not call `tcx.analysis()` so that we won't | |
503 | // typeck function bodies or run the default rustc lints. | |
504 | // (see `override_queries` in the `config`) | |
505 | ||
506 | // HACK(jynelson) this calls an _extremely_ limited subset of `typeck` | |
507 | // and might break if queries change their assumptions in the future. | |
508 | ||
509 | // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes. | |
510 | tcx.sess.time("item_types_checking", || { | |
511 | for &module in tcx.hir().krate().modules.keys() { | |
512 | tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module)); | |
513 | } | |
514 | }); | |
515 | tcx.sess.abort_if_errors(); | |
516 | tcx.sess.time("missing_docs", || { | |
517 | rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); | |
518 | }); | |
519 | tcx.sess.time("check_mod_attrs", || { | |
520 | for &module in tcx.hir().krate().modules.keys() { | |
521 | let local_def_id = tcx.hir().local_def_id(module); | |
522 | tcx.ensure().check_mod_attrs(local_def_id); | |
523 | } | |
524 | }); | |
60c5eb7d | 525 | |
3dfed10e XL |
526 | let access_levels = tcx.privacy_access_levels(LOCAL_CRATE); |
527 | // Convert from a HirId set to a DefId set since we don't always have easy access | |
528 | // to the map from defid -> hirid | |
529 | let access_levels = AccessLevels { | |
530 | map: access_levels | |
531 | .map | |
532 | .iter() | |
533 | .map(|(&k, &v)| (tcx.hir().local_def_id(k).to_def_id(), v)) | |
534 | .collect(), | |
535 | }; | |
b7449926 | 536 | |
3dfed10e XL |
537 | let mut renderinfo = RenderInfo::default(); |
538 | renderinfo.access_levels = access_levels; | |
539 | renderinfo.output_format = output_format; | |
540 | ||
541 | let mut ctxt = DocContext { | |
542 | tcx, | |
543 | resolver, | |
544 | external_traits: Default::default(), | |
545 | active_extern_traits: Default::default(), | |
546 | renderinfo: RefCell::new(renderinfo), | |
547 | ty_substs: Default::default(), | |
548 | lt_substs: Default::default(), | |
549 | ct_substs: Default::default(), | |
550 | impl_trait_bounds: Default::default(), | |
551 | fake_def_ids: Default::default(), | |
552 | all_fake_def_ids: Default::default(), | |
553 | generated_synthetics: Default::default(), | |
554 | auto_traits: tcx | |
555 | .all_traits(LOCAL_CRATE) | |
556 | .iter() | |
557 | .cloned() | |
558 | .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id)) | |
559 | .collect(), | |
560 | render_options, | |
561 | module_trait_cache: RefCell::new(FxHashMap::default()), | |
562 | }; | |
563 | debug!("crate: {:?}", tcx.hir().krate()); | |
564 | ||
565 | let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); | |
566 | ||
567 | if let Some(ref m) = krate.module { | |
568 | if let None | Some("") = m.doc_value() { | |
569 | let help = "The following guide may be of use:\n\ | |
1b1a35ee | 570 | https://doc.rust-lang.org/nightly/rustdoc/how-to-write-documentation.html"; |
3dfed10e XL |
571 | tcx.struct_lint_node( |
572 | rustc_lint::builtin::MISSING_CRATE_LEVEL_DOCS, | |
573 | ctxt.as_local_hir_id(m.def_id).unwrap(), | |
574 | |lint| { | |
575 | let mut diag = | |
576 | lint.build("no documentation found for this crate's top-level module"); | |
577 | diag.help(help); | |
578 | diag.emit(); | |
579 | }, | |
580 | ); | |
581 | } | |
582 | } | |
b7449926 | 583 | |
3dfed10e XL |
584 | fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler) { |
585 | let mut msg = diag | |
586 | .struct_warn(&format!("the `#![doc({})]` attribute is considered deprecated", name)); | |
587 | msg.warn( | |
588 | "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ | |
1b1a35ee | 589 | for more information", |
3dfed10e | 590 | ); |
b7449926 | 591 | |
3dfed10e XL |
592 | if name == "no_default_passes" { |
593 | msg.help("you may want to use `#![doc(document_private_items)]`"); | |
594 | } | |
b7449926 | 595 | |
3dfed10e XL |
596 | msg.emit(); |
597 | } | |
598 | ||
599 | // Process all of the crate attributes, extracting plugin metadata along | |
600 | // with the passes which we are supposed to run. | |
601 | for attr in krate.module.as_ref().unwrap().attrs.lists(sym::doc) { | |
602 | let diag = ctxt.sess().diagnostic(); | |
603 | ||
604 | let name = attr.name_or_empty(); | |
605 | if attr.is_word() { | |
606 | if name == sym::no_default_passes { | |
607 | report_deprecated_attr("no_default_passes", diag); | |
608 | if default_passes == passes::DefaultPassOption::Default { | |
609 | default_passes = passes::DefaultPassOption::None; | |
60c5eb7d | 610 | } |
3dfed10e XL |
611 | } |
612 | } else if let Some(value) = attr.value_str() { | |
613 | let sink = match name { | |
614 | sym::passes => { | |
615 | report_deprecated_attr("passes = \"...\"", diag); | |
616 | &mut manual_passes | |
617 | } | |
618 | sym::plugins => { | |
619 | report_deprecated_attr("plugins = \"...\"", diag); | |
620 | eprintln!( | |
621 | "WARNING: `#![doc(plugins = \"...\")]` \ | |
1b1a35ee | 622 | no longer functions; see CVE-2018-1000622" |
3dfed10e XL |
623 | ); |
624 | continue; | |
625 | } | |
626 | _ => continue, | |
627 | }; | |
628 | for name in value.as_str().split_whitespace() { | |
629 | sink.push(name.to_string()); | |
630 | } | |
631 | } | |
b7449926 | 632 | |
3dfed10e XL |
633 | if attr.is_word() && name == sym::document_private_items { |
634 | ctxt.render_options.document_private = true; | |
635 | } | |
636 | } | |
b7449926 | 637 | |
3dfed10e XL |
638 | let passes = passes::defaults(default_passes).iter().copied().chain( |
639 | manual_passes.into_iter().flat_map(|name| { | |
640 | if let Some(pass) = passes::find_pass(&name) { | |
641 | Some(ConditionalPass::always(pass)) | |
642 | } else { | |
643 | error!("unknown pass {}, skipping", name); | |
644 | None | |
645 | } | |
646 | }), | |
647 | ); | |
648 | ||
649 | info!("Executing passes"); | |
650 | ||
651 | for p in passes { | |
652 | let run = match p.condition { | |
653 | Always => true, | |
654 | WhenDocumentPrivate => ctxt.render_options.document_private, | |
655 | WhenNotDocumentPrivate => !ctxt.render_options.document_private, | |
656 | WhenNotDocumentHidden => !ctxt.render_options.document_hidden, | |
657 | }; | |
658 | if run { | |
659 | debug!("running pass {}", p.pass.name); | |
660 | krate = ctxt.tcx.sess.time(p.pass.name, || (p.pass.run)(krate, &ctxt)); | |
661 | } | |
662 | } | |
663 | ||
664 | ctxt.sess().abort_if_errors(); | |
665 | ||
666 | (krate, ctxt.renderinfo.into_inner(), ctxt.render_options) | |
667 | } | |
668 | ||
29967ef6 | 669 | /// Due to <https://github.com/rust-lang/rust/pull/73566>, |
3dfed10e XL |
670 | /// the name resolution pass may find errors that are never emitted. |
671 | /// If typeck is called after this happens, then we'll get an ICE: | |
672 | /// 'Res::Error found but not reported'. To avoid this, emit the errors now. | |
673 | struct EmitIgnoredResolutionErrors<'tcx> { | |
674 | tcx: TyCtxt<'tcx>, | |
675 | } | |
676 | ||
677 | impl<'tcx> EmitIgnoredResolutionErrors<'tcx> { | |
678 | fn new(tcx: TyCtxt<'tcx>) -> Self { | |
679 | Self { tcx } | |
680 | } | |
681 | } | |
682 | ||
683 | impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { | |
684 | type Map = Map<'tcx>; | |
685 | ||
686 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { | |
687 | // We need to recurse into nested closures, | |
688 | // since those will fallback to the parent for type checking. | |
689 | NestedVisitorMap::OnlyBodies(self.tcx.hir()) | |
690 | } | |
691 | ||
692 | fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { | |
693 | debug!("visiting path {:?}", path); | |
694 | if path.res == Res::Err { | |
695 | // We have less context here than in rustc_resolve, | |
696 | // so we can only emit the name and span. | |
697 | // However we can give a hint that rustc_resolve will have more info. | |
698 | let label = format!( | |
699 | "could not resolve path `{}`", | |
700 | path.segments | |
701 | .iter() | |
702 | .map(|segment| segment.ident.as_str().to_string()) | |
703 | .collect::<Vec<_>>() | |
704 | .join("::") | |
705 | ); | |
706 | let mut err = rustc_errors::struct_span_err!( | |
707 | self.tcx.sess, | |
708 | path.span, | |
709 | E0433, | |
710 | "failed to resolve: {}", | |
711 | label | |
712 | ); | |
713 | err.span_label(path.span, label); | |
714 | err.note("this error was originally ignored because you are running `rustdoc`"); | |
715 | err.note("try running again with `rustc` or `cargo check` and you may get a more detailed error"); | |
716 | err.emit(); | |
717 | } | |
718 | // We could have an outer resolution that succeeded, | |
719 | // but with generic parameters that failed. | |
720 | // Recurse into the segments so we catch those too. | |
721 | intravisit::walk_path(self, path); | |
722 | } | |
1a4d82fc | 723 | } |
e1599b0c XL |
724 | |
725 | /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter | |
726 | /// for `impl Trait` in argument position. | |
727 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | |
728 | pub enum ImplTraitParam { | |
729 | DefId(DefId), | |
730 | ParamIndex(u32), | |
731 | } | |
732 | ||
733 | impl From<DefId> for ImplTraitParam { | |
734 | fn from(did: DefId) -> Self { | |
735 | ImplTraitParam::DefId(did) | |
736 | } | |
737 | } | |
738 | ||
739 | impl From<u32> for ImplTraitParam { | |
740 | fn from(idx: u32) -> Self { | |
741 | ImplTraitParam::ParamIndex(idx) | |
742 | } | |
743 | } |