Skip to content

Commit 1ff117e

Browse files
authored
Rollup merge of rust-lang#84101 - jyn514:early-pass, r=Manishearth
rustdoc: Move crate loader to collect_intra_doc_links::early This groups the similar code together, and also allows making most of collect_intra_doc_links private again. This builds on rust-lang#84066, but it wouldn't be too hard to base it off master if you want this to land first. Helps with rust-lang#83761. r? manishearth Fixes rust-lang#84046
2 parents 3ea5a9f + cba5073 commit 1ff117e

File tree

6 files changed

+220
-133
lines changed

6 files changed

+220
-133
lines changed

src/librustdoc/core.rs

+3-51
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use rustc_driver::abort_on_err;
55
use rustc_errors::emitter::{Emitter, EmitterWriter};
66
use rustc_errors::json::JsonEmitter;
77
use rustc_feature::UnstableFeatures;
8-
use rustc_hir::def::{Namespace::TypeNS, Res};
9-
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
8+
use rustc_hir::def::Res;
9+
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, LOCAL_CRATE};
1010
use rustc_hir::HirId;
1111
use rustc_hir::{
1212
intravisit::{self, NestedVisitorMap, Visitor},
@@ -356,55 +356,7 @@ crate fn create_resolver<'a>(
356356
let (krate, resolver, _) = &*parts;
357357
let resolver = resolver.borrow().clone();
358358

359-
// Letting the resolver escape at the end of the function leads to inconsistencies between the
360-
// crates the TyCtxt sees and the resolver sees (because the resolver could load more crates
361-
// after escaping). Hopefully `IntraLinkCrateLoader` gets all the crates we need ...
362-
struct IntraLinkCrateLoader {
363-
current_mod: DefId,
364-
resolver: Rc<RefCell<interface::BoxedResolver>>,
365-
}
366-
impl ast::visit::Visitor<'_> for IntraLinkCrateLoader {
367-
fn visit_attribute(&mut self, attr: &ast::Attribute) {
368-
use crate::html::markdown::{markdown_links, MarkdownLink};
369-
use crate::passes::collect_intra_doc_links::Disambiguator;
370-
371-
if let Some(doc) = attr.doc_str() {
372-
for MarkdownLink { link, .. } in markdown_links(&doc.as_str()) {
373-
// FIXME: this misses a *lot* of the preprocessing done in collect_intra_doc_links
374-
// I think most of it shouldn't be necessary since we only need the crate prefix?
375-
let path_str = match Disambiguator::from_str(&link) {
376-
Ok(x) => x.map_or(link.as_str(), |(_, p)| p),
377-
Err(_) => continue,
378-
};
379-
self.resolver.borrow_mut().access(|resolver| {
380-
let _ = resolver.resolve_str_path_error(
381-
attr.span,
382-
path_str,
383-
TypeNS,
384-
self.current_mod,
385-
);
386-
});
387-
}
388-
}
389-
ast::visit::walk_attribute(self, attr);
390-
}
391-
392-
fn visit_item(&mut self, item: &ast::Item) {
393-
use rustc_ast_lowering::ResolverAstLowering;
394-
395-
if let ast::ItemKind::Mod(..) = item.kind {
396-
let new_mod =
397-
self.resolver.borrow_mut().access(|resolver| resolver.local_def_id(item.id));
398-
let old_mod = mem::replace(&mut self.current_mod, new_mod.to_def_id());
399-
ast::visit::walk_item(self, item);
400-
self.current_mod = old_mod;
401-
} else {
402-
ast::visit::walk_item(self, item);
403-
}
404-
}
405-
}
406-
let crate_id = LocalDefId { local_def_index: CRATE_DEF_INDEX }.to_def_id();
407-
let mut loader = IntraLinkCrateLoader { current_mod: crate_id, resolver };
359+
let mut loader = crate::passes::collect_intra_doc_links::IntraLinkCrateLoader::new(resolver);
408360
ast::visit::walk_crate(&mut loader, krate);
409361

410362
loader.resolver

src/librustdoc/passes/collect_intra_doc_links.rs

+140-81
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,16 @@ use crate::passes::Pass;
3939

4040
use super::span_of_attrs;
4141

42+
mod early;
43+
crate use early::IntraLinkCrateLoader;
44+
4245
crate const COLLECT_INTRA_DOC_LINKS: Pass = Pass {
4346
name: "collect-intra-doc-links",
4447
run: collect_intra_doc_links,
4548
description: "resolves intra-doc links",
4649
};
4750

48-
crate fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
51+
fn collect_intra_doc_links(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
4952
LinkCollector {
5053
cx,
5154
mod_ids: Vec::new(),
@@ -892,6 +895,117 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
892895
}
893896
}
894897

898+
enum PreprocessingError<'a> {
899+
Anchor(AnchorFailure),
900+
Disambiguator(Range<usize>, String),
901+
Resolution(ResolutionFailure<'a>, String, Option<Disambiguator>),
902+
}
903+
904+
impl From<AnchorFailure> for PreprocessingError<'_> {
905+
fn from(err: AnchorFailure) -> Self {
906+
Self::Anchor(err)
907+
}
908+
}
909+
910+
struct PreprocessingInfo {
911+
path_str: String,
912+
disambiguator: Option<Disambiguator>,
913+
extra_fragment: Option<String>,
914+
link_text: String,
915+
}
916+
917+
/// Returns:
918+
/// - `None` if the link should be ignored.
919+
/// - `Some(Err)` if the link should emit an error
920+
/// - `Some(Ok)` if the link is valid
921+
///
922+
/// `link_buffer` is needed for lifetime reasons; it will always be overwritten and the contents ignored.
923+
fn preprocess_link<'a>(
924+
ori_link: &'a MarkdownLink,
925+
) -> Option<Result<PreprocessingInfo, PreprocessingError<'a>>> {
926+
// [] is mostly likely not supposed to be a link
927+
if ori_link.link.is_empty() {
928+
return None;
929+
}
930+
931+
// Bail early for real links.
932+
if ori_link.link.contains('/') {
933+
return None;
934+
}
935+
936+
let stripped = ori_link.link.replace("`", "");
937+
let mut parts = stripped.split('#');
938+
939+
let link = parts.next().unwrap();
940+
if link.trim().is_empty() {
941+
// This is an anchor to an element of the current page, nothing to do in here!
942+
return None;
943+
}
944+
let extra_fragment = parts.next();
945+
if parts.next().is_some() {
946+
// A valid link can't have multiple #'s
947+
return Some(Err(AnchorFailure::MultipleAnchors.into()));
948+
}
949+
950+
// Parse and strip the disambiguator from the link, if present.
951+
let (path_str, disambiguator) = match Disambiguator::from_str(&link) {
952+
Ok(Some((d, path))) => (path.trim(), Some(d)),
953+
Ok(None) => (link.trim(), None),
954+
Err((err_msg, relative_range)) => {
955+
// Only report error if we would not have ignored this link. See issue #83859.
956+
if !should_ignore_link_with_disambiguators(link) {
957+
let no_backticks_range = range_between_backticks(&ori_link);
958+
let disambiguator_range = (no_backticks_range.start + relative_range.start)
959+
..(no_backticks_range.start + relative_range.end);
960+
return Some(Err(PreprocessingError::Disambiguator(disambiguator_range, err_msg)));
961+
} else {
962+
return None;
963+
}
964+
}
965+
};
966+
967+
if should_ignore_link(path_str) {
968+
return None;
969+
}
970+
971+
// We stripped `()` and `!` when parsing the disambiguator.
972+
// Add them back to be displayed, but not prefix disambiguators.
973+
let link_text =
974+
disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned());
975+
976+
// Strip generics from the path.
977+
let path_str = if path_str.contains(['<', '>'].as_slice()) {
978+
match strip_generics_from_path(&path_str) {
979+
Ok(path) => path,
980+
Err(err_kind) => {
981+
debug!("link has malformed generics: {}", path_str);
982+
return Some(Err(PreprocessingError::Resolution(
983+
err_kind,
984+
path_str.to_owned(),
985+
disambiguator,
986+
)));
987+
}
988+
}
989+
} else {
990+
path_str.to_owned()
991+
};
992+
993+
// Sanity check to make sure we don't have any angle brackets after stripping generics.
994+
assert!(!path_str.contains(['<', '>'].as_slice()));
995+
996+
// The link is not an intra-doc link if it still contains spaces after stripping generics.
997+
if path_str.contains(' ') {
998+
return None;
999+
}
1000+
1001+
Some(Ok(PreprocessingInfo {
1002+
path_str,
1003+
disambiguator,
1004+
extra_fragment: extra_fragment.map(String::from),
1005+
link_text,
1006+
}))
1007+
}
1008+
8951009
impl LinkCollector<'_, '_> {
8961010
/// This is the entry point for resolving an intra-doc link.
8971011
///
@@ -907,64 +1021,36 @@ impl LinkCollector<'_, '_> {
9071021
) -> Option<ItemLink> {
9081022
trace!("considering link '{}'", ori_link.link);
9091023

910-
// Bail early for real links.
911-
if ori_link.link.contains('/') {
912-
return None;
913-
}
914-
915-
// [] is mostly likely not supposed to be a link
916-
if ori_link.link.is_empty() {
917-
return None;
918-
}
919-
9201024
let diag_info = DiagnosticInfo {
9211025
item,
9221026
dox,
9231027
ori_link: &ori_link.link,
9241028
link_range: ori_link.range.clone(),
9251029
};
9261030

927-
let link = ori_link.link.replace("`", "");
928-
let no_backticks_range = range_between_backticks(&ori_link);
929-
let parts = link.split('#').collect::<Vec<_>>();
930-
let (link, extra_fragment) = if parts.len() > 2 {
931-
// A valid link can't have multiple #'s
932-
anchor_failure(self.cx, diag_info, AnchorFailure::MultipleAnchors);
933-
return None;
934-
} else if parts.len() == 2 {
935-
if parts[0].trim().is_empty() {
936-
// This is an anchor to an element of the current page, nothing to do in here!
937-
return None;
938-
}
939-
(parts[0], Some(parts[1].to_owned()))
940-
} else {
941-
(parts[0], None)
942-
};
943-
944-
// Parse and strip the disambiguator from the link, if present.
945-
let (mut path_str, disambiguator) = match Disambiguator::from_str(&link) {
946-
Ok(Some((d, path))) => (path.trim(), Some(d)),
947-
Ok(None) => (link.trim(), None),
948-
Err((err_msg, relative_range)) => {
949-
if !should_ignore_link_with_disambiguators(link) {
950-
// Only report error if we would not have ignored this link.
951-
// See issue #83859.
952-
let disambiguator_range = (no_backticks_range.start + relative_range.start)
953-
..(no_backticks_range.start + relative_range.end);
954-
disambiguator_error(self.cx, diag_info, disambiguator_range, &err_msg);
1031+
let PreprocessingInfo { path_str, disambiguator, extra_fragment, link_text } =
1032+
match preprocess_link(&ori_link)? {
1033+
Ok(x) => x,
1034+
Err(err) => {
1035+
match err {
1036+
PreprocessingError::Anchor(err) => anchor_failure(self.cx, diag_info, err),
1037+
PreprocessingError::Disambiguator(range, msg) => {
1038+
disambiguator_error(self.cx, diag_info, range, &msg)
1039+
}
1040+
PreprocessingError::Resolution(err, path_str, disambiguator) => {
1041+
resolution_failure(
1042+
self,
1043+
diag_info,
1044+
&path_str,
1045+
disambiguator,
1046+
smallvec![err],
1047+
);
1048+
}
1049+
}
1050+
return None;
9551051
}
956-
return None;
957-
}
958-
};
959-
960-
if should_ignore_link(path_str) {
961-
return None;
962-
}
963-
964-
// We stripped `()` and `!` when parsing the disambiguator.
965-
// Add them back to be displayed, but not prefix disambiguators.
966-
let link_text =
967-
disambiguator.map(|d| d.display_for(path_str)).unwrap_or_else(|| path_str.to_owned());
1052+
};
1053+
let mut path_str = &*path_str;
9681054

9691055
// In order to correctly resolve intra-doc links we need to
9701056
// pick a base AST node to work from. If the documentation for
@@ -1029,39 +1115,12 @@ impl LinkCollector<'_, '_> {
10291115
module_id = DefId { krate, index: CRATE_DEF_INDEX };
10301116
}
10311117

1032-
// Strip generics from the path.
1033-
let stripped_path_string;
1034-
if path_str.contains(['<', '>'].as_slice()) {
1035-
stripped_path_string = match strip_generics_from_path(path_str) {
1036-
Ok(path) => path,
1037-
Err(err_kind) => {
1038-
debug!("link has malformed generics: {}", path_str);
1039-
resolution_failure(
1040-
self,
1041-
diag_info,
1042-
path_str,
1043-
disambiguator,
1044-
smallvec![err_kind],
1045-
);
1046-
return None;
1047-
}
1048-
};
1049-
path_str = &stripped_path_string;
1050-
}
1051-
// Sanity check to make sure we don't have any angle brackets after stripping generics.
1052-
assert!(!path_str.contains(['<', '>'].as_slice()));
1053-
1054-
// The link is not an intra-doc link if it still contains spaces after stripping generics.
1055-
if path_str.contains(' ') {
1056-
return None;
1057-
}
1058-
10591118
let (mut res, mut fragment) = self.resolve_with_disambiguator_cached(
10601119
ResolutionInfo {
10611120
module_id,
10621121
dis: disambiguator,
10631122
path_str: path_str.to_owned(),
1064-
extra_fragment,
1123+
extra_fragment: extra_fragment.map(String::from),
10651124
},
10661125
diag_info.clone(), // this struct should really be Copy, but Range is not :(
10671126
matches!(ori_link.kind, LinkType::Reference | LinkType::Shortcut),
@@ -1438,7 +1497,7 @@ fn should_ignore_link(path_str: &str) -> bool {
14381497

14391498
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
14401499
/// Disambiguators for a link.
1441-
crate enum Disambiguator {
1500+
enum Disambiguator {
14421501
/// `prim@`
14431502
///
14441503
/// This is buggy, see <https://github.com/rust-lang/rust/pull/77875#discussion_r503583103>
@@ -1467,7 +1526,7 @@ impl Disambiguator {
14671526
/// This returns `Ok(Some(...))` if a disambiguator was found,
14681527
/// `Ok(None)` if no disambiguator was found, or `Err(...)`
14691528
/// if there was a problem with the disambiguator.
1470-
crate fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> {
1529+
fn from_str(link: &str) -> Result<Option<(Self, &str)>, (String, Range<usize>)> {
14711530
use Disambiguator::{Kind, Namespace as NS, Primitive};
14721531

14731532
if let Some(idx) = link.find('@') {

0 commit comments

Comments
 (0)