resolve: Optimize path resolution for rustdoc
Do not construct or pass unused data
This commit is contained in:
parent
0749ec67bc
commit
15a8b981e0
3 changed files with 38 additions and 92 deletions
|
@ -71,7 +71,7 @@ use rustc_span::{Span, DUMMY_SP};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::{cmp, fmt, iter, mem, ptr};
|
use std::{cmp, fmt, mem, ptr};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding};
|
use diagnostics::{extend_span_to_previous_binding, find_span_of_binding_until_next_binding};
|
||||||
|
@ -3309,82 +3309,39 @@ impl<'a> Resolver<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rustdoc uses this to resolve things in a recoverable way. `ResolutionError<'a>`
|
/// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>`
|
||||||
/// isn't something that can be returned because it can't be made to live that long,
|
/// isn't something that can be returned because it can't be made to live that long,
|
||||||
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
|
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
|
||||||
/// just that an error occurred.
|
/// just that an error occurred.
|
||||||
// FIXME(Manishearth): intra-doc links won't get warned of epoch changes.
|
pub fn resolve_rustdoc_path(
|
||||||
pub fn resolve_str_path_error(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
|
||||||
path_str: &str,
|
path_str: &str,
|
||||||
ns: Namespace,
|
ns: Namespace,
|
||||||
module_id: DefId,
|
module_id: DefId,
|
||||||
) -> Result<(ast::Path, Res), ()> {
|
) -> Option<Res> {
|
||||||
let path = if path_str.starts_with("::") {
|
let mut segments =
|
||||||
ast::Path {
|
Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
|
||||||
span,
|
if path_str.starts_with("::") {
|
||||||
segments: iter::once(Ident::with_dummy_span(kw::PathRoot))
|
segments[0].ident.name = kw::PathRoot;
|
||||||
.chain(path_str.split("::").skip(1).map(Ident::from_str))
|
}
|
||||||
.map(|i| self.new_ast_path_segment(i))
|
|
||||||
.collect(),
|
|
||||||
tokens: None,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ast::Path {
|
|
||||||
span,
|
|
||||||
segments: path_str
|
|
||||||
.split("::")
|
|
||||||
.map(Ident::from_str)
|
|
||||||
.map(|i| self.new_ast_path_segment(i))
|
|
||||||
.collect(),
|
|
||||||
tokens: None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let module = self.expect_module(module_id);
|
|
||||||
let parent_scope = &ParentScope::module(module, self);
|
|
||||||
let res = self.resolve_ast_path(&path, ns, parent_scope).map_err(|_| ())?;
|
|
||||||
Ok((path, res))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve a path passed from rustdoc or HIR lowering.
|
let module = self.expect_module(module_id);
|
||||||
fn resolve_ast_path(
|
|
||||||
&mut self,
|
|
||||||
path: &ast::Path,
|
|
||||||
ns: Namespace,
|
|
||||||
parent_scope: &ParentScope<'a>,
|
|
||||||
) -> Result<Res, (Span, ResolutionError<'a>)> {
|
|
||||||
match self.resolve_path(
|
match self.resolve_path(
|
||||||
&Segment::from_path(path),
|
&segments,
|
||||||
Some(ns),
|
Some(ns),
|
||||||
parent_scope,
|
&ParentScope::module(module, self),
|
||||||
path.span,
|
DUMMY_SP,
|
||||||
CrateLint::No,
|
CrateLint::No,
|
||||||
) {
|
) {
|
||||||
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Ok(module.res().unwrap()),
|
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
|
||||||
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
|
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
|
||||||
Ok(path_res.base_res())
|
Some(path_res.base_res())
|
||||||
}
|
}
|
||||||
PathResult::NonModule(..) => Err((
|
PathResult::NonModule(..) | PathResult::Failed { .. } => None,
|
||||||
path.span,
|
|
||||||
ResolutionError::FailedToResolve {
|
|
||||||
label: String::from("type-relative paths are not supported in this context"),
|
|
||||||
suggestion: None,
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
|
PathResult::Module(..) | PathResult::Indeterminate => unreachable!(),
|
||||||
PathResult::Failed { span, label, suggestion, .. } => {
|
|
||||||
Err((span, ResolutionError::FailedToResolve { label, suggestion }))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_ast_path_segment(&mut self, ident: Ident) -> ast::PathSegment {
|
|
||||||
let mut seg = ast::PathSegment::from_ident(ident);
|
|
||||||
seg.id = self.next_node_id();
|
|
||||||
seg
|
|
||||||
}
|
|
||||||
|
|
||||||
// For rustdoc.
|
// For rustdoc.
|
||||||
pub fn graph_root(&self) -> Module<'a> {
|
pub fn graph_root(&self) -> Module<'a> {
|
||||||
self.graph_root
|
self.graph_root
|
||||||
|
|
|
@ -487,12 +487,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
||||||
module_id: DefId,
|
module_id: DefId,
|
||||||
) -> Result<Res, ResolutionFailure<'a>> {
|
) -> Result<Res, ResolutionFailure<'a>> {
|
||||||
self.cx.enter_resolver(|resolver| {
|
self.cx.enter_resolver(|resolver| {
|
||||||
// NOTE: this needs 2 separate lookups because `resolve_str_path_error` doesn't take
|
// NOTE: this needs 2 separate lookups because `resolve_rustdoc_path` doesn't take
|
||||||
// lexical scope into account (it ignores all macros not defined at the mod-level)
|
// lexical scope into account (it ignores all macros not defined at the mod-level)
|
||||||
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
|
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
|
||||||
if let Ok((_, res)) =
|
if let Some(res) = resolver.resolve_rustdoc_path(path_str, MacroNS, module_id) {
|
||||||
resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
|
|
||||||
{
|
|
||||||
// don't resolve builtins like `#[derive]`
|
// don't resolve builtins like `#[derive]`
|
||||||
if let Ok(res) = res.try_into() {
|
if let Ok(res) = res.try_into() {
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
|
@ -540,10 +538,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience wrapper around `resolve_str_path_error`.
|
/// Convenience wrapper around `resolve_rustdoc_path`.
|
||||||
///
|
///
|
||||||
/// This also handles resolving `true` and `false` as booleans.
|
/// This also handles resolving `true` and `false` as booleans.
|
||||||
/// NOTE: `resolve_str_path_error` knows only about paths, not about types.
|
/// NOTE: `resolve_rustdoc_path` knows only about paths, not about types.
|
||||||
/// Associated items will never be resolved by this function.
|
/// Associated items will never be resolved by this function.
|
||||||
fn resolve_path(
|
fn resolve_path(
|
||||||
&self,
|
&self,
|
||||||
|
@ -556,18 +554,14 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = self.cx.enter_resolver(|resolver| {
|
// Resolver doesn't know about true, false, and types that aren't paths (e.g. `()`).
|
||||||
resolver
|
let result = self
|
||||||
.resolve_str_path_error(DUMMY_SP, path_str, ns, module_id)
|
.cx
|
||||||
.and_then(|(_, res)| res.try_into())
|
.enter_resolver(|resolver| resolver.resolve_rustdoc_path(path_str, ns, module_id))
|
||||||
});
|
.and_then(|res| res.try_into().ok())
|
||||||
|
.or_else(|| resolve_primitive(path_str, ns));
|
||||||
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
|
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
|
||||||
match result {
|
result
|
||||||
// resolver doesn't know about true, false, and types that aren't paths (e.g. `()`)
|
|
||||||
// manually as bool
|
|
||||||
Err(()) => resolve_primitive(path_str, ns),
|
|
||||||
Ok(res) => Some(res),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves a string as a path within a particular namespace. Returns an
|
/// Resolves a string as a path within a particular namespace. Returns an
|
||||||
|
|
|
@ -13,7 +13,7 @@ use rustc_hir::TraitCandidate;
|
||||||
use rustc_middle::ty::{DefIdTree, Visibility};
|
use rustc_middle::ty::{DefIdTree, Visibility};
|
||||||
use rustc_resolve::{ParentScope, Resolver};
|
use rustc_resolve::{ParentScope, Resolver};
|
||||||
use rustc_session::config::Externs;
|
use rustc_session::config::Externs;
|
||||||
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
use rustc_span::SyntaxContext;
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -39,7 +39,7 @@ crate fn early_resolve_intra_doc_links(
|
||||||
|
|
||||||
// Overridden `visit_item` below doesn't apply to the crate root,
|
// Overridden `visit_item` below doesn't apply to the crate root,
|
||||||
// so we have to visit its attributes and reexports separately.
|
// so we have to visit its attributes and reexports separately.
|
||||||
loader.load_links_in_attrs(&krate.attrs, krate.spans.inner_span);
|
loader.load_links_in_attrs(&krate.attrs);
|
||||||
loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
|
loader.process_module_children_or_reexports(CRATE_DEF_ID.to_def_id());
|
||||||
visit::walk_crate(&mut loader, krate);
|
visit::walk_crate(&mut loader, krate);
|
||||||
loader.add_foreign_traits_in_scope();
|
loader.add_foreign_traits_in_scope();
|
||||||
|
@ -49,12 +49,7 @@ crate fn early_resolve_intra_doc_links(
|
||||||
// DO NOT REMOVE THIS without first testing on the reproducer in
|
// DO NOT REMOVE THIS without first testing on the reproducer in
|
||||||
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
|
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
|
||||||
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
|
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
|
||||||
let _ = loader.resolver.resolve_str_path_error(
|
loader.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
|
||||||
DUMMY_SP,
|
|
||||||
extern_name,
|
|
||||||
TypeNS,
|
|
||||||
CRATE_DEF_ID.to_def_id(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolverCaches {
|
ResolverCaches {
|
||||||
|
@ -151,7 +146,7 @@ impl IntraLinkCrateLoader<'_, '_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute], span: Span) {
|
fn load_links_in_attrs(&mut self, attrs: &[ast::Attribute]) {
|
||||||
// FIXME: this needs to consider reexport inlining.
|
// FIXME: this needs to consider reexport inlining.
|
||||||
let attrs = clean::Attributes::from_ast(attrs, None);
|
let attrs = clean::Attributes::from_ast(attrs, None);
|
||||||
for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
|
for (parent_module, doc) in attrs.collapsed_doc_value_by_module_level() {
|
||||||
|
@ -165,7 +160,7 @@ impl IntraLinkCrateLoader<'_, '_> {
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let _ = self.resolver.resolve_str_path_error(span, &path_str, TypeNS, module_id);
|
self.resolver.resolve_rustdoc_path(&path_str, TypeNS, module_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,7 +196,7 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
|
||||||
// loaded, even if the module itself has no doc comments.
|
// loaded, even if the module itself has no doc comments.
|
||||||
self.add_traits_in_parent_scope(self.current_mod.to_def_id());
|
self.add_traits_in_parent_scope(self.current_mod.to_def_id());
|
||||||
|
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs);
|
||||||
self.process_module_children_or_reexports(self.current_mod.to_def_id());
|
self.process_module_children_or_reexports(self.current_mod.to_def_id());
|
||||||
visit::walk_item(self, item);
|
visit::walk_item(self, item);
|
||||||
|
|
||||||
|
@ -216,28 +211,28 @@ impl Visitor<'_> for IntraLinkCrateLoader<'_, '_> {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs);
|
||||||
visit::walk_item(self, item);
|
visit::walk_item(self, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
|
fn visit_assoc_item(&mut self, item: &ast::AssocItem, ctxt: AssocCtxt) {
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs);
|
||||||
visit::walk_assoc_item(self, item, ctxt)
|
visit::walk_assoc_item(self, item, ctxt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
|
fn visit_foreign_item(&mut self, item: &ast::ForeignItem) {
|
||||||
self.load_links_in_attrs(&item.attrs, item.span);
|
self.load_links_in_attrs(&item.attrs);
|
||||||
visit::walk_foreign_item(self, item)
|
visit::walk_foreign_item(self, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_variant(&mut self, v: &ast::Variant) {
|
fn visit_variant(&mut self, v: &ast::Variant) {
|
||||||
self.load_links_in_attrs(&v.attrs, v.span);
|
self.load_links_in_attrs(&v.attrs);
|
||||||
visit::walk_variant(self, v)
|
visit::walk_variant(self, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_field_def(&mut self, field: &ast::FieldDef) {
|
fn visit_field_def(&mut self, field: &ast::FieldDef) {
|
||||||
self.load_links_in_attrs(&field.attrs, field.span);
|
self.load_links_in_attrs(&field.attrs);
|
||||||
visit::walk_field_def(self, field)
|
visit::walk_field_def(self, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue