Skip doc link resolution for some crate types and non-exported items

This commit is contained in:
Vadim Petrochenkov 2023-02-06 21:57:45 +04:00
parent 3b0866272a
commit da4ce6b41e
5 changed files with 106 additions and 19 deletions

View File

@ -24,13 +24,13 @@ use rustc_hir::{BindingAnnotation, PrimTy, TraitCandidate};
use rustc_middle::middle::resolve_lifetime::Set1;
use rustc_middle::ty::DefIdTree;
use rustc_middle::{bug, span_bug};
use rustc_session::config::CrateType;
use rustc_session::config::{CrateType, ResolveDocLinks};
use rustc_session::lint;
use rustc_span::source_map::{respan, Spanned};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{BytePos, Span, SyntaxContext};
use smallvec::{smallvec, SmallVec};
use rustc_span::source_map::{respan, Spanned};
use std::assert_matches::debug_assert_matches;
use std::borrow::Cow;
use std::collections::{hash_map::Entry, BTreeSet};
@ -494,6 +494,30 @@ impl<'a> PathSource<'a> {
}
}
/// At this point for most items we can answer whether that item is exported or not,
/// but some items like impls require type information to determine exported-ness, so we make a
/// conservative estimate for them (e.g. based on nominal visibility).
#[derive(Clone, Copy)]
enum MaybeExported<'a> {
Ok(NodeId),
Impl(Option<DefId>),
ImplItem(Result<DefId, &'a Visibility>),
}
impl MaybeExported<'_> {
fn eval(self, r: &Resolver<'_>) -> bool {
let def_id = match self {
MaybeExported::Ok(node_id) => Some(r.local_def_id(node_id)),
MaybeExported::Impl(Some(trait_def_id)) | MaybeExported::ImplItem(Ok(trait_def_id)) => {
trait_def_id.as_local()
}
MaybeExported::Impl(None) => return true,
MaybeExported::ImplItem(Err(vis)) => return vis.kind.is_pub(),
};
def_id.map_or(true, |def_id| r.effective_visibilities.is_exported(def_id))
}
}
#[derive(Default)]
struct DiagnosticMetadata<'ast> {
/// The current trait's associated items' ident, used for diagnostic suggestions.
@ -774,7 +798,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
);
}
fn visit_foreign_item(&mut self, foreign_item: &'ast ForeignItem) {
self.resolve_doc_links(&foreign_item.attrs);
self.resolve_doc_links(&foreign_item.attrs, MaybeExported::Ok(foreign_item.id));
match foreign_item.kind {
ForeignItemKind::TyAlias(box TyAlias { ref generics, .. }) => {
self.with_generic_param_rib(
@ -1165,12 +1189,12 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
}
fn visit_variant(&mut self, v: &'ast Variant) {
self.resolve_doc_links(&v.attrs);
self.resolve_doc_links(&v.attrs, MaybeExported::Ok(v.id));
visit::walk_variant(self, v)
}
fn visit_field_def(&mut self, f: &'ast FieldDef) {
self.resolve_doc_links(&f.attrs);
self.resolve_doc_links(&f.attrs, MaybeExported::Ok(f.id));
visit::walk_field_def(self, f)
}
}
@ -2201,8 +2225,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn resolve_item(&mut self, item: &'ast Item) {
let mod_inner_docs =
matches!(item.kind, ItemKind::Mod(..)) && rustdoc::inner_docs(&item.attrs);
if !mod_inner_docs {
self.resolve_doc_links(&item.attrs);
if !mod_inner_docs && !matches!(item.kind, ItemKind::Impl(..)) {
self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
}
let name = item.ident.name;
@ -2249,7 +2273,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
..
}) => {
self.diagnostic_metadata.current_impl_items = Some(impl_items);
self.resolve_implementation(generics, of_trait, &self_ty, item.id, impl_items);
self.resolve_implementation(
&item.attrs,
generics,
of_trait,
&self_ty,
item.id,
impl_items,
);
self.diagnostic_metadata.current_impl_items = None;
}
@ -2297,7 +2328,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
ItemKind::Mod(..) => {
self.with_scope(item.id, |this| {
if mod_inner_docs {
this.resolve_doc_links(&item.attrs);
this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
}
let old_macro_rules = this.parent_scope.macro_rules;
visit::walk_item(this, item);
@ -2583,7 +2614,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
};
for item in trait_items {
self.resolve_doc_links(&item.attrs);
self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
match &item.kind {
AssocItemKind::Const(_, ty, default) => {
self.visit_ty(ty);
@ -2671,6 +2702,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
fn resolve_implementation(
&mut self,
attrs: &[ast::Attribute],
generics: &'ast Generics,
opt_trait_reference: &'ast Option<TraitRef>,
self_type: &'ast Ty,
@ -2701,6 +2733,8 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
opt_trait_reference.as_ref(),
self_type,
|this, trait_id| {
this.resolve_doc_links(attrs, MaybeExported::Impl(trait_id));
let item_def_id = this.r.local_def_id(item_id);
// Register the trait definitions from here.
@ -2734,7 +2768,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
debug!("resolve_implementation with_self_rib_ns(ValueNS, ...)");
let mut seen_trait_items = Default::default();
for item in impl_items {
this.resolve_impl_item(&**item, &mut seen_trait_items);
this.resolve_impl_item(&**item, &mut seen_trait_items, trait_id);
}
});
});
@ -2752,9 +2786,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
&mut self,
item: &'ast AssocItem,
seen_trait_items: &mut FxHashMap<DefId, Span>,
trait_id: Option<DefId>,
) {
use crate::ResolutionError::*;
self.resolve_doc_links(&item.attrs);
self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
match &item.kind {
AssocItemKind::Const(_, ty, default) => {
debug!("resolve_implementation AssocItemKind::Const");
@ -4183,7 +4218,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
res
}
fn resolve_doc_links(&mut self, attrs: &[Attribute]) {
fn resolve_doc_links(&mut self, attrs: &[Attribute], maybe_exported: MaybeExported<'_>) {
match self.r.session.opts.resolve_doc_links {
ResolveDocLinks::None => return,
ResolveDocLinks::ExportedMetadata
if !self.r.session.crate_types().iter().copied().any(CrateType::has_metadata)
|| !maybe_exported.eval(self.r) =>
{
return;
}
ResolveDocLinks::Exported if !maybe_exported.eval(self.r) => {
return;
}
ResolveDocLinks::ExportedMetadata
| ResolveDocLinks::Exported
| ResolveDocLinks::All => {}
}
if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
return;
}
@ -4283,7 +4334,7 @@ impl<'a> Resolver<'a> {
pub(crate) fn late_resolve_crate(&mut self, krate: &Crate) {
visit::walk_crate(&mut LifetimeCountVisitor { r: self }, krate);
let mut late_resolution_visitor = LateResolutionVisitor::new(self);
late_resolution_visitor.resolve_doc_links(&krate.attrs);
late_resolution_visitor.resolve_doc_links(&krate.attrs, MaybeExported::Ok(CRATE_NODE_ID));
visit::walk_crate(&mut late_resolution_visitor, krate);
for (id, span) in late_resolution_visitor.diagnostic_metadata.unused_labels.iter() {
self.lint_buffer.buffer_lint(lint::builtin::UNUSED_LABELS, *id, *span, "unused label");

View File

@ -419,6 +419,18 @@ pub enum TrimmedDefPaths {
GoodPath,
}
#[derive(Clone, Hash)]
pub enum ResolveDocLinks {
/// Do not resolve doc links.
None,
/// Resolve doc links on exported items only for crate types that have metadata.
ExportedMetadata,
/// Resolve doc links on exported items.
Exported,
/// Resolve doc links on all items.
All,
}
/// Use tree-based collections to cheaply get a deterministic `Hash` implementation.
/// *Do not* switch `BTreeMap` out for an unsorted container type! That would break
/// dependency tracking for command-line arguments. Also only hash keys, since tracking
@ -788,6 +800,7 @@ impl Default for Options {
unstable_features: UnstableFeatures::Disallow,
debug_assertions: true,
actually_rustdoc: false,
resolve_doc_links: ResolveDocLinks::None,
trimmed_def_paths: TrimmedDefPaths::default(),
cli_forced_codegen_units: None,
cli_forced_local_thinlto_off: false,
@ -883,6 +896,15 @@ pub enum CrateType {
ProcMacro,
}
impl CrateType {
pub fn has_metadata(self) -> bool {
match self {
CrateType::Rlib | CrateType::Dylib | CrateType::ProcMacro => true,
CrateType::Executable | CrateType::Cdylib | CrateType::Staticlib => false,
}
}
}
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum Passes {
Some(Vec<String>),
@ -2562,6 +2584,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
libs,
debug_assertions,
actually_rustdoc: false,
resolve_doc_links: ResolveDocLinks::ExportedMetadata,
trimmed_def_paths: TrimmedDefPaths::default(),
cli_forced_codegen_units: codegen_units,
cli_forced_local_thinlto_off: disable_local_thinlto,
@ -2825,8 +2848,9 @@ pub(crate) mod dep_tracking {
use super::{
BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
InstrumentCoverage, InstrumentXRay, LdImpl, LinkerPluginLto, LocationDetail, LtoCli,
OomStrategy, OptLevel, OutputType, OutputTypes, Passes, SourceFileHashAlgorithm,
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, TraitSolver, TrimmedDefPaths,
OomStrategy, OptLevel, OutputType, OutputTypes, Passes, ResolveDocLinks,
SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
TraitSolver, TrimmedDefPaths,
};
use crate::lint;
use crate::options::WasiExecModel;
@ -2913,6 +2937,7 @@ pub(crate) mod dep_tracking {
TargetTriple,
Edition,
LinkerPluginLto,
ResolveDocLinks,
SplitDebuginfo,
SplitDwarfKind,
StackProtector,

View File

@ -169,6 +169,8 @@ top_level_options!(
/// is currently just a hack and will be removed eventually, so please
/// try to not rely on this too much.
actually_rustdoc: bool [TRACKED],
/// Whether name resolver should resolve documentation links.
resolve_doc_links: ResolveDocLinks [TRACKED],
/// Control path trimming.
trimmed_def_paths: TrimmedDefPaths [TRACKED],

View File

@ -12,7 +12,7 @@ use rustc_hir::{HirId, Path};
use rustc_interface::interface;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_session::config::{self, CrateType, ErrorOutputType};
use rustc_session::config::{self, CrateType, ErrorOutputType, ResolveDocLinks};
use rustc_session::lint;
use rustc_session::Session;
use rustc_span::symbol::sym;
@ -200,6 +200,7 @@ pub(crate) fn create_config(
scrape_examples_options,
..
}: RustdocOptions,
RenderOptions { document_private, .. }: &RenderOptions,
) -> rustc_interface::Config {
// Add the doc cfg into the doc build.
cfgs.push("doc".to_string());
@ -227,6 +228,13 @@ pub(crate) fn create_config(
let crate_types =
if proc_macro_crate { vec![CrateType::ProcMacro] } else { vec![CrateType::Rlib] };
let resolve_doc_links = if *document_private {
ResolveDocLinks::All
} else {
// Should be `ResolveDocLinks::Exported` in theory, but for some reason rustdoc
// still tries to request resolutions for links on private items.
ResolveDocLinks::All
};
let test = scrape_examples_options.map(|opts| opts.scrape_tests).unwrap_or(false);
// plays with error output here!
let sessopts = config::Options {
@ -240,6 +248,7 @@ pub(crate) fn create_config(
target_triple: target,
unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
actually_rustdoc: true,
resolve_doc_links,
unstable_opts,
error_format,
diagnostic_width,

View File

@ -742,7 +742,7 @@ fn main_args(at_args: &[String]) -> MainResult {
(false, true) => {
let input = options.input.clone();
let edition = options.edition;
let config = core::create_config(options);
let config = core::create_config(options, &render_options);
// `markdown::render` can invoke `doctest::make_test`, which
// requires session globals and a thread pool, so we use
@ -775,7 +775,7 @@ fn main_args(at_args: &[String]) -> MainResult {
let scrape_examples_options = options.scrape_examples_options.clone();
let bin_crate = options.bin_crate;
let config = core::create_config(options);
let config = core::create_config(options, &render_options);
interface::run_compiler(config, |compiler| {
let sess = compiler.session();