]>
Commit | Line | Data |
---|---|---|
1 | use rustc_ast::NodeId; | |
2 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
3 | use rustc_data_structures::sync::{self, Lrc}; | |
4 | use rustc_errors::emitter::{Emitter, EmitterWriter}; | |
5 | use rustc_errors::json::JsonEmitter; | |
6 | use rustc_feature::UnstableFeatures; | |
7 | use rustc_hir::def::{Namespace, Res}; | |
8 | use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; | |
9 | use rustc_hir::intravisit::{self, Visitor}; | |
10 | use rustc_hir::{HirId, Path, TraitCandidate}; | |
11 | use rustc_interface::interface; | |
12 | use rustc_middle::hir::nested_filter; | |
13 | use rustc_middle::middle::privacy::AccessLevels; | |
14 | use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; | |
15 | use rustc_resolve as resolve; | |
16 | use rustc_session::config::{self, CrateType, ErrorOutputType}; | |
17 | use rustc_session::lint; | |
18 | use rustc_session::DiagnosticOutput; | |
19 | use rustc_session::Session; | |
20 | use rustc_span::symbol::sym; | |
21 | use rustc_span::{source_map, Span, Symbol}; | |
22 | ||
23 | use std::cell::RefCell; | |
24 | use std::mem; | |
25 | use std::rc::Rc; | |
26 | use std::sync::LazyLock; | |
27 | ||
28 | use crate::clean::inline::build_external_trait; | |
29 | use crate::clean::{self, ItemId, TraitWithExtraInfo}; | |
30 | use crate::config::{Options as RustdocOptions, OutputFormat, RenderOptions}; | |
31 | use crate::formats::cache::Cache; | |
32 | use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink; | |
33 | use crate::passes::{self, Condition::*}; | |
34 | ||
35 | pub(crate) use rustc_session::config::{DebuggingOptions, Input, Options}; | |
36 | ||
37 | pub(crate) struct ResolverCaches { | |
38 | pub(crate) markdown_links: Option<FxHashMap<String, Vec<PreprocessedMarkdownLink>>>, | |
39 | pub(crate) doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<NodeId>>>, | |
40 | /// Traits in scope for a given module. | |
41 | /// See `collect_intra_doc_links::traits_implemented_by` for more details. | |
42 | pub(crate) traits_in_scope: DefIdMap<Vec<TraitCandidate>>, | |
43 | pub(crate) all_traits: Option<Vec<DefId>>, | |
44 | pub(crate) all_trait_impls: Option<Vec<DefId>>, | |
45 | pub(crate) all_macro_rules: FxHashMap<Symbol, Res<NodeId>>, | |
46 | } | |
47 | ||
48 | pub(crate) struct DocContext<'tcx> { | |
49 | pub(crate) tcx: TyCtxt<'tcx>, | |
50 | /// Name resolver. Used for intra-doc links. | |
51 | /// | |
52 | /// The `Rc<RefCell<...>>` wrapping is needed because that is what's returned by | |
53 | /// [`rustc_interface::Queries::expansion()`]. | |
54 | // FIXME: see if we can get rid of this RefCell somehow | |
55 | pub(crate) resolver: Rc<RefCell<interface::BoxedResolver>>, | |
56 | pub(crate) resolver_caches: ResolverCaches, | |
57 | /// Used for normalization. | |
58 | /// | |
59 | /// Most of this logic is copied from rustc_lint::late. | |
60 | pub(crate) param_env: ParamEnv<'tcx>, | |
61 | /// Later on moved through `clean::Crate` into `cache` | |
62 | pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, clean::TraitWithExtraInfo>>>, | |
63 | /// Used while populating `external_traits` to ensure we don't process the same trait twice at | |
64 | /// the same time. | |
65 | pub(crate) active_extern_traits: FxHashSet<DefId>, | |
66 | // The current set of parameter substitutions, | |
67 | // for expanding type aliases at the HIR level: | |
68 | /// Table `DefId` of type, lifetime, or const parameter -> substituted type, lifetime, or const | |
69 | pub(crate) substs: FxHashMap<DefId, clean::SubstParam>, | |
70 | /// Table synthetic type parameter for `impl Trait` in argument position -> bounds | |
71 | pub(crate) impl_trait_bounds: FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>, | |
72 | /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. | |
73 | // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. | |
74 | pub(crate) generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>, | |
75 | pub(crate) auto_traits: Vec<DefId>, | |
76 | /// The options given to rustdoc that could be relevant to a pass. | |
77 | pub(crate) render_options: RenderOptions, | |
78 | /// This same cache is used throughout rustdoc, including in [`crate::html::render`]. | |
79 | pub(crate) cache: Cache, | |
80 | /// Used by [`clean::inline`] to tell if an item has already been inlined. | |
81 | pub(crate) inlined: FxHashSet<ItemId>, | |
82 | /// Used by `calculate_doc_coverage`. | |
83 | pub(crate) output_format: OutputFormat, | |
84 | } | |
85 | ||
86 | impl<'tcx> DocContext<'tcx> { | |
87 | pub(crate) fn sess(&self) -> &'tcx Session { | |
88 | self.tcx.sess | |
89 | } | |
90 | ||
91 | pub(crate) fn with_param_env<T, F: FnOnce(&mut Self) -> T>( | |
92 | &mut self, | |
93 | def_id: DefId, | |
94 | f: F, | |
95 | ) -> T { | |
96 | let old_param_env = mem::replace(&mut self.param_env, self.tcx.param_env(def_id)); | |
97 | let ret = f(self); | |
98 | self.param_env = old_param_env; | |
99 | ret | |
100 | } | |
101 | ||
102 | pub(crate) fn enter_resolver<F, R>(&self, f: F) -> R | |
103 | where | |
104 | F: FnOnce(&mut resolve::Resolver<'_>) -> R, | |
105 | { | |
106 | self.resolver.borrow_mut().access(f) | |
107 | } | |
108 | ||
109 | /// Call the closure with the given parameters set as | |
110 | /// the substitutions for a type alias' RHS. | |
111 | pub(crate) fn enter_alias<F, R>( | |
112 | &mut self, | |
113 | substs: FxHashMap<DefId, clean::SubstParam>, | |
114 | f: F, | |
115 | ) -> R | |
116 | where | |
117 | F: FnOnce(&mut Self) -> R, | |
118 | { | |
119 | let old_substs = mem::replace(&mut self.substs, substs); | |
120 | let r = f(self); | |
121 | self.substs = old_substs; | |
122 | r | |
123 | } | |
124 | ||
125 | /// Like `hir().local_def_id_to_hir_id()`, but skips calling it on fake DefIds. | |
126 | /// (This avoids a slice-index-out-of-bounds panic.) | |
127 | pub(crate) fn as_local_hir_id(tcx: TyCtxt<'_>, item_id: ItemId) -> Option<HirId> { | |
128 | match item_id { | |
129 | ItemId::DefId(real_id) => { | |
130 | real_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id)) | |
131 | } | |
132 | // FIXME: Can this be `Some` for `Auto` or `Blanket`? | |
133 | _ => None, | |
134 | } | |
135 | } | |
136 | ||
137 | pub(crate) fn with_all_traits(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { | |
138 | let all_traits = self.resolver_caches.all_traits.take(); | |
139 | f(self, all_traits.as_ref().expect("`all_traits` are already borrowed")); | |
140 | self.resolver_caches.all_traits = all_traits; | |
141 | } | |
142 | ||
143 | pub(crate) fn with_all_trait_impls(&mut self, f: impl FnOnce(&mut Self, &[DefId])) { | |
144 | let all_trait_impls = self.resolver_caches.all_trait_impls.take(); | |
145 | f(self, all_trait_impls.as_ref().expect("`all_trait_impls` are already borrowed")); | |
146 | self.resolver_caches.all_trait_impls = all_trait_impls; | |
147 | } | |
148 | } | |
149 | ||
150 | /// Creates a new diagnostic `Handler` that can be used to emit warnings and errors. | |
151 | /// | |
152 | /// If the given `error_format` is `ErrorOutputType::Json` and no `SourceMap` is given, a new one | |
153 | /// will be created for the handler. | |
154 | pub(crate) fn new_handler( | |
155 | error_format: ErrorOutputType, | |
156 | source_map: Option<Lrc<source_map::SourceMap>>, | |
157 | debugging_opts: &DebuggingOptions, | |
158 | ) -> rustc_errors::Handler { | |
159 | let fallback_bundle = | |
160 | rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); | |
161 | let emitter: Box<dyn Emitter + sync::Send> = match error_format { | |
162 | ErrorOutputType::HumanReadable(kind) => { | |
163 | let (short, color_config) = kind.unzip(); | |
164 | Box::new( | |
165 | EmitterWriter::stderr( | |
166 | color_config, | |
167 | source_map.map(|sm| sm as _), | |
168 | None, | |
169 | fallback_bundle, | |
170 | short, | |
171 | debugging_opts.teach, | |
172 | debugging_opts.terminal_width, | |
173 | false, | |
174 | ) | |
175 | .ui_testing(debugging_opts.ui_testing), | |
176 | ) | |
177 | } | |
178 | ErrorOutputType::Json { pretty, json_rendered } => { | |
179 | let source_map = source_map.unwrap_or_else(|| { | |
180 | Lrc::new(source_map::SourceMap::new(source_map::FilePathMapping::empty())) | |
181 | }); | |
182 | Box::new( | |
183 | JsonEmitter::stderr( | |
184 | None, | |
185 | source_map, | |
186 | None, | |
187 | fallback_bundle, | |
188 | pretty, | |
189 | json_rendered, | |
190 | debugging_opts.terminal_width, | |
191 | false, | |
192 | ) | |
193 | .ui_testing(debugging_opts.ui_testing), | |
194 | ) | |
195 | } | |
196 | }; | |
197 | ||
198 | rustc_errors::Handler::with_emitter_and_flags( | |
199 | emitter, | |
200 | debugging_opts.diagnostic_handler_flags(true), | |
201 | ) | |
202 | } | |
203 | ||
204 | /// Parse, resolve, and typecheck the given crate. | |
205 | pub(crate) fn create_config( | |
206 | RustdocOptions { | |
207 | input, | |
208 | crate_name, | |
209 | proc_macro_crate, | |
210 | error_format, | |
211 | libs, | |
212 | externs, | |
213 | mut cfgs, | |
214 | check_cfgs, | |
215 | codegen_options, | |
216 | debugging_opts, | |
217 | target, | |
218 | edition, | |
219 | maybe_sysroot, | |
220 | lint_opts, | |
221 | describe_lints, | |
222 | lint_cap, | |
223 | scrape_examples_options, | |
224 | .. | |
225 | }: RustdocOptions, | |
226 | ) -> rustc_interface::Config { | |
227 | // Add the doc cfg into the doc build. | |
228 | cfgs.push("doc".to_string()); | |
229 | ||
230 | let cpath = Some(input.clone()); | |
231 | let input = Input::File(input); | |
232 | ||
233 | // By default, rustdoc ignores all lints. | |
234 | // Specifically unblock lints relevant to documentation or the lint machinery itself. | |
235 | let mut lints_to_show = vec![ | |
236 | // it's unclear whether these should be part of rustdoc directly (#77364) | |
237 | rustc_lint::builtin::MISSING_DOCS.name.to_string(), | |
238 | rustc_lint::builtin::INVALID_DOC_ATTRIBUTES.name.to_string(), | |
239 | // these are definitely not part of rustdoc, but we want to warn on them anyway. | |
240 | rustc_lint::builtin::RENAMED_AND_REMOVED_LINTS.name.to_string(), | |
241 | rustc_lint::builtin::UNKNOWN_LINTS.name.to_string(), | |
242 | rustc_lint::builtin::UNEXPECTED_CFGS.name.to_string(), | |
243 | // this lint is needed to support `#[expect]` attributes | |
244 | rustc_lint::builtin::UNFULFILLED_LINT_EXPECTATIONS.name.to_string(), | |
245 | ]; | |
246 | lints_to_show.extend(crate::lint::RUSTDOC_LINTS.iter().map(|lint| lint.name.to_string())); | |
247 | ||
248 | let (lint_opts, lint_caps) = crate::lint::init_lints(lints_to_show, lint_opts, |lint| { | |
249 | Some((lint.name_lower(), lint::Allow)) | |
250 | }); | |
251 | ||
252 | let crate_types = | |
253 | if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] }; | |
254 | let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false); | |
255 | // plays with error output here! | |
256 | let sessopts = config::Options { | |
257 | maybe_sysroot, | |
258 | search_paths: libs, | |
259 | crate_types, | |
260 | lint_opts, | |
261 | lint_cap, | |
262 | cg: codegen_options, | |
263 | externs, | |
264 | target_triple: target, | |
265 | unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()), | |
266 | actually_rustdoc: true, | |
267 | debugging_opts, | |
268 | error_format, | |
269 | edition, | |
270 | describe_lints, | |
271 | crate_name, | |
272 | test, | |
273 | ..Options::default() | |
274 | }; | |
275 | ||
276 | interface::Config { | |
277 | opts: sessopts, | |
278 | crate_cfg: interface::parse_cfgspecs(cfgs), | |
279 | crate_check_cfg: interface::parse_check_cfg(check_cfgs), | |
280 | input, | |
281 | input_path: cpath, | |
282 | output_file: None, | |
283 | output_dir: None, | |
284 | file_loader: None, | |
285 | diagnostic_output: DiagnosticOutput::Default, | |
286 | lint_caps, | |
287 | parse_sess_created: None, | |
288 | register_lints: Some(box crate::lint::register_lints), | |
289 | override_queries: Some(|_sess, providers, _external_providers| { | |
290 | // Most lints will require typechecking, so just don't run them. | |
291 | providers.lint_mod = |_, _| {}; | |
292 | // Prevent `rustc_typeck::check_crate` from calling `typeck` on all bodies. | |
293 | providers.typeck_item_bodies = |_, _| {}; | |
294 | // hack so that `used_trait_imports` won't try to call typeck | |
295 | providers.used_trait_imports = |_, _| { | |
296 | static EMPTY_SET: LazyLock<FxHashSet<LocalDefId>> = | |
297 | LazyLock::new(FxHashSet::default); | |
298 | &EMPTY_SET | |
299 | }; | |
300 | // In case typeck does end up being called, don't ICE in case there were name resolution errors | |
301 | providers.typeck = move |tcx, def_id| { | |
302 | // Closures' tables come from their outermost function, | |
303 | // as they are part of the same "inference environment". | |
304 | // This avoids emitting errors for the parent twice (see similar code in `typeck_with_fallback`) | |
305 | let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); | |
306 | if typeck_root_def_id != def_id { | |
307 | return tcx.typeck(typeck_root_def_id); | |
308 | } | |
309 | ||
310 | let hir = tcx.hir(); | |
311 | let body = hir.body(hir.body_owned_by(hir.local_def_id_to_hir_id(def_id))); | |
312 | debug!("visiting body for {:?}", def_id); | |
313 | EmitIgnoredResolutionErrors::new(tcx).visit_body(body); | |
314 | (rustc_interface::DEFAULT_QUERY_PROVIDERS.typeck)(tcx, def_id) | |
315 | }; | |
316 | }), | |
317 | make_codegen_backend: None, | |
318 | registry: rustc_driver::diagnostics_registry(), | |
319 | } | |
320 | } | |
321 | ||
322 | pub(crate) fn run_global_ctxt( | |
323 | tcx: TyCtxt<'_>, | |
324 | resolver: Rc<RefCell<interface::BoxedResolver>>, | |
325 | resolver_caches: ResolverCaches, | |
326 | show_coverage: bool, | |
327 | render_options: RenderOptions, | |
328 | output_format: OutputFormat, | |
329 | ) -> (clean::Crate, RenderOptions, Cache) { | |
330 | // Certain queries assume that some checks were run elsewhere | |
331 | // (see https://github.com/rust-lang/rust/pull/73566#issuecomment-656954425), | |
332 | // so type-check everything other than function bodies in this crate before running lints. | |
333 | ||
334 | // NOTE: this does not call `tcx.analysis()` so that we won't | |
335 | // typeck function bodies or run the default rustc lints. | |
336 | // (see `override_queries` in the `config`) | |
337 | ||
338 | // HACK(jynelson) this calls an _extremely_ limited subset of `typeck` | |
339 | // and might break if queries change their assumptions in the future. | |
340 | ||
341 | // NOTE: This is copy/pasted from typeck/lib.rs and should be kept in sync with those changes. | |
342 | tcx.sess.time("item_types_checking", || { | |
343 | tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module)) | |
344 | }); | |
345 | tcx.sess.abort_if_errors(); | |
346 | tcx.sess.time("missing_docs", || { | |
347 | rustc_lint::check_crate(tcx, rustc_lint::builtin::MissingDoc::new); | |
348 | }); | |
349 | tcx.sess.time("check_mod_attrs", || { | |
350 | tcx.hir().for_each_module(|module| tcx.ensure().check_mod_attrs(module)) | |
351 | }); | |
352 | rustc_passes::stability::check_unused_or_stable_features(tcx); | |
353 | ||
354 | let auto_traits = resolver_caches | |
355 | .all_traits | |
356 | .as_ref() | |
357 | .expect("`all_traits` are already borrowed") | |
358 | .iter() | |
359 | .copied() | |
360 | .filter(|&trait_def_id| tcx.trait_is_auto(trait_def_id)) | |
361 | .collect(); | |
362 | let access_levels = AccessLevels { | |
363 | map: tcx.privacy_access_levels(()).map.iter().map(|(k, v)| (k.to_def_id(), *v)).collect(), | |
364 | }; | |
365 | ||
366 | let mut ctxt = DocContext { | |
367 | tcx, | |
368 | resolver, | |
369 | resolver_caches, | |
370 | param_env: ParamEnv::empty(), | |
371 | external_traits: Default::default(), | |
372 | active_extern_traits: Default::default(), | |
373 | substs: Default::default(), | |
374 | impl_trait_bounds: Default::default(), | |
375 | generated_synthetics: Default::default(), | |
376 | auto_traits, | |
377 | cache: Cache::new(access_levels, render_options.document_private), | |
378 | inlined: FxHashSet::default(), | |
379 | output_format, | |
380 | render_options, | |
381 | }; | |
382 | ||
383 | // Small hack to force the Sized trait to be present. | |
384 | // | |
385 | // Note that in case of `#![no_core]`, the trait is not available. | |
386 | if let Some(sized_trait_did) = ctxt.tcx.lang_items().sized_trait() { | |
387 | let mut sized_trait = build_external_trait(&mut ctxt, sized_trait_did); | |
388 | sized_trait.is_auto = true; | |
389 | ctxt.external_traits | |
390 | .borrow_mut() | |
391 | .insert(sized_trait_did, TraitWithExtraInfo { trait_: sized_trait, is_notable: false }); | |
392 | } | |
393 | ||
394 | debug!("crate: {:?}", tcx.hir().krate()); | |
395 | ||
396 | let mut krate = tcx.sess.time("clean_crate", || clean::krate(&mut ctxt)); | |
397 | ||
398 | if krate.module.doc_value().map(|d| d.is_empty()).unwrap_or(true) { | |
399 | let help = format!( | |
400 | "The following guide may be of use:\n\ | |
401 | {}/rustdoc/how-to-write-documentation.html", | |
402 | crate::DOC_RUST_LANG_ORG_CHANNEL | |
403 | ); | |
404 | tcx.struct_lint_node( | |
405 | crate::lint::MISSING_CRATE_LEVEL_DOCS, | |
406 | DocContext::as_local_hir_id(tcx, krate.module.item_id).unwrap(), | |
407 | |lint| { | |
408 | let mut diag = | |
409 | lint.build("no documentation found for this crate's top-level module"); | |
410 | diag.help(&help); | |
411 | diag.emit(); | |
412 | }, | |
413 | ); | |
414 | } | |
415 | ||
416 | fn report_deprecated_attr(name: &str, diag: &rustc_errors::Handler, sp: Span) { | |
417 | let mut msg = | |
418 | diag.struct_span_warn(sp, &format!("the `#![doc({})]` attribute is deprecated", name)); | |
419 | msg.note( | |
420 | "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \ | |
421 | for more information", | |
422 | ); | |
423 | ||
424 | if name == "no_default_passes" { | |
425 | msg.help("`#![doc(no_default_passes)]` no longer functions; you may want to use `#![doc(document_private_items)]`"); | |
426 | } else if name.starts_with("passes") { | |
427 | msg.help("`#![doc(passes = \"...\")]` no longer functions; you may want to use `#![doc(document_private_items)]`"); | |
428 | } else if name.starts_with("plugins") { | |
429 | msg.warn("`#![doc(plugins = \"...\")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>"); | |
430 | } | |
431 | ||
432 | msg.emit(); | |
433 | } | |
434 | ||
435 | // Process all of the crate attributes, extracting plugin metadata along | |
436 | // with the passes which we are supposed to run. | |
437 | for attr in krate.module.attrs.lists(sym::doc) { | |
438 | let diag = ctxt.sess().diagnostic(); | |
439 | ||
440 | let name = attr.name_or_empty(); | |
441 | // `plugins = "..."`, `no_default_passes`, and `passes = "..."` have no effect | |
442 | if attr.is_word() && name == sym::no_default_passes { | |
443 | report_deprecated_attr("no_default_passes", diag, attr.span()); | |
444 | } else if attr.value_str().is_some() { | |
445 | match name { | |
446 | sym::passes => { | |
447 | report_deprecated_attr("passes = \"...\"", diag, attr.span()); | |
448 | } | |
449 | sym::plugins => { | |
450 | report_deprecated_attr("plugins = \"...\"", diag, attr.span()); | |
451 | } | |
452 | _ => (), | |
453 | } | |
454 | } | |
455 | ||
456 | if attr.is_word() && name == sym::document_private_items { | |
457 | ctxt.render_options.document_private = true; | |
458 | } | |
459 | } | |
460 | ||
461 | info!("Executing passes"); | |
462 | ||
463 | for p in passes::defaults(show_coverage) { | |
464 | let run = match p.condition { | |
465 | Always => true, | |
466 | WhenDocumentPrivate => ctxt.render_options.document_private, | |
467 | WhenNotDocumentPrivate => !ctxt.render_options.document_private, | |
468 | WhenNotDocumentHidden => !ctxt.render_options.document_hidden, | |
469 | }; | |
470 | if run { | |
471 | debug!("running pass {}", p.pass.name); | |
472 | krate = tcx.sess.time(p.pass.name, || (p.pass.run)(krate, &mut ctxt)); | |
473 | } | |
474 | } | |
475 | ||
476 | tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc))); | |
477 | ||
478 | if tcx.sess.diagnostic().has_errors_or_lint_errors().is_some() { | |
479 | rustc_errors::FatalError.raise(); | |
480 | } | |
481 | ||
482 | krate = tcx.sess.time("create_format_cache", || Cache::populate(&mut ctxt, krate)); | |
483 | ||
484 | (krate, ctxt.render_options, ctxt.cache) | |
485 | } | |
486 | ||
487 | /// Due to <https://github.com/rust-lang/rust/pull/73566>, | |
488 | /// the name resolution pass may find errors that are never emitted. | |
489 | /// If typeck is called after this happens, then we'll get an ICE: | |
490 | /// 'Res::Error found but not reported'. To avoid this, emit the errors now. | |
491 | struct EmitIgnoredResolutionErrors<'tcx> { | |
492 | tcx: TyCtxt<'tcx>, | |
493 | } | |
494 | ||
495 | impl<'tcx> EmitIgnoredResolutionErrors<'tcx> { | |
496 | fn new(tcx: TyCtxt<'tcx>) -> Self { | |
497 | Self { tcx } | |
498 | } | |
499 | } | |
500 | ||
501 | impl<'tcx> Visitor<'tcx> for EmitIgnoredResolutionErrors<'tcx> { | |
502 | type NestedFilter = nested_filter::OnlyBodies; | |
503 | ||
504 | fn nested_visit_map(&mut self) -> Self::Map { | |
505 | // We need to recurse into nested closures, | |
506 | // since those will fallback to the parent for type checking. | |
507 | self.tcx.hir() | |
508 | } | |
509 | ||
510 | fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) { | |
511 | debug!("visiting path {:?}", path); | |
512 | if path.res == Res::Err { | |
513 | // We have less context here than in rustc_resolve, | |
514 | // so we can only emit the name and span. | |
515 | // However we can give a hint that rustc_resolve will have more info. | |
516 | let label = format!( | |
517 | "could not resolve path `{}`", | |
518 | path.segments | |
519 | .iter() | |
520 | .map(|segment| segment.ident.as_str()) | |
521 | .intersperse("::") | |
522 | .collect::<String>() | |
523 | ); | |
524 | let mut err = rustc_errors::struct_span_err!( | |
525 | self.tcx.sess, | |
526 | path.span, | |
527 | E0433, | |
528 | "failed to resolve: {}", | |
529 | label | |
530 | ); | |
531 | err.span_label(path.span, label); | |
532 | err.note("this error was originally ignored because you are running `rustdoc`"); | |
533 | err.note("try running again with `rustc` or `cargo check` and you may get a more detailed error"); | |
534 | err.emit(); | |
535 | } | |
536 | // We could have an outer resolution that succeeded, | |
537 | // but with generic parameters that failed. | |
538 | // Recurse into the segments so we catch those too. | |
539 | intravisit::walk_path(self, path); | |
540 | } | |
541 | } | |
542 | ||
543 | /// `DefId` or parameter index (`ty::ParamTy.index`) of a synthetic type parameter | |
544 | /// for `impl Trait` in argument position. | |
545 | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | |
546 | pub(crate) enum ImplTraitParam { | |
547 | DefId(DefId), | |
548 | ParamIndex(u32), | |
549 | } | |
550 | ||
551 | impl From<DefId> for ImplTraitParam { | |
552 | fn from(did: DefId) -> Self { | |
553 | ImplTraitParam::DefId(did) | |
554 | } | |
555 | } | |
556 | ||
557 | impl From<u32> for ImplTraitParam { | |
558 | fn from(idx: u32) -> Self { | |
559 | ImplTraitParam::ParamIndex(idx) | |
560 | } | |
561 | } |