Skip to content

Commit 375c7fb

Browse files
committed
Auto merge of #47046 - Manishearth:intra-doc-links, r=eddyb,GuillaumeGomez,QuietMisdreavus,Manishearth
Implement RFC 1946 - intra-rustdoc links rust-lang/rfcs#1946 #43466 Note for reviewers: The plain line counts are a little inflated because of how the markdown link parsing was done. [Read the file diff with "whitespace only" changes removed](https://github.com/rust-lang/rust/pull/47046/files?w=1) to get a better view of what actually changed there. This pulls the name/path resolution mechanisms out of the compiler and runs it on the markdown in a crate's docs, so that links can be made to `SomeStruct` directly rather than finding the folder path to `struct.SomeStruct.html`. Check the `src/test/rustdoc/intra-paths.rs` test in this PR for a demo. The change was... a little invasive, but unlocks a really powerful mechanism for writing documentation that doesn't care about where an item was written to on the hard disk. Items included: - [x] Make work with the hoedown renderer - [x] Handle relative paths - [x] Parse out the "path ambiguities" qualifiers (`[crate foo]`, `[struct Foo]`, `[foo()]`, `[static FOO]`, `[foo!]`, etc) - [x] Resolve foreign macros - [x] Resolve local macros - [x] Handle the use of inner/outer attributes giving different resolution scopes (handling for non-modules pushed to different PR) Items not included: - [ ] Make sure cross-crate inlining works (blocked on refactor described in #47046 (comment)) - [ ] Implied Shortcut Reference Links (where just doing `[::std::iter::Iterator][]` without a reference anchor will resolve using the reference name rather than the link target) (requires modifying the markdown parser - blocked on Hoedown/Pulldown switch and pulldown-cmark/pulldown-cmark#121) - [ ] Handle enum variants and UFCS methods (Enum variants link to the enum page, associated methods don't link at all) - [ ] Emit more warnings/errors when things fail to resolve (linking to a value-namespaced item without a qualifier will emit an error, otherwise the link is just treated as a url, not a rust path) - [ ] Give better spans for resolution errors (currently the span for the first doc comment is used) - [ ] Check for inner doc comments on things that aren't modules I'm making the PR, but it should be noted that most of the work was done by Misdreavus 😄 (Editor's note: This has become a lie, check that commit log, Manish did a ton of work after this PR was opened `>_>`)
2 parents fdc18b3 + fe93ada commit 375c7fb

File tree

17 files changed

+944
-264
lines changed

17 files changed

+944
-264
lines changed

src/librustc/hir/lowering.rs

+6-10
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ pub trait Resolver {
151151
/// We must keep the set of definitions up to date as we add nodes that weren't in the AST.
152152
/// This should only return `None` during testing.
153153
fn definitions(&mut self) -> &mut Definitions;
154+
155+
/// Given suffix ["b","c","d"], creates a HIR path for `[::crate_root]::b::c::d` and resolves
156+
/// it based on `is_value`.
157+
fn resolve_str_path(&mut self, span: Span, crate_root: Option<&str>,
158+
components: &[&str], is_value: bool) -> hir::Path;
154159
}
155160

156161
#[derive(Clone, Copy, Debug)]
@@ -3625,16 +3630,7 @@ impl<'a> LoweringContext<'a> {
36253630
/// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
36263631
/// The path is also resolved according to `is_value`.
36273632
fn std_path(&mut self, span: Span, components: &[&str], is_value: bool) -> hir::Path {
3628-
let mut path = hir::Path {
3629-
span,
3630-
def: Def::Err,
3631-
segments: iter::once(keywords::CrateRoot.name()).chain({
3632-
self.crate_root.into_iter().chain(components.iter().cloned()).map(Symbol::intern)
3633-
}).map(hir::PathSegment::from_name).collect(),
3634-
};
3635-
3636-
self.resolver.resolve_hir_path(&mut path, is_value);
3637-
path
3633+
self.resolver.resolve_str_path(span, self.crate_root, components, is_value)
36383634
}
36393635

36403636
fn signal_block_expr(&mut self,

src/librustc_driver/driver.rs

+60-23
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use rustc::util::common::{ErrorReported, time};
2828
use rustc_allocator as allocator;
2929
use rustc_borrowck as borrowck;
3030
use rustc_incremental;
31-
use rustc_resolve::{MakeGlobMap, Resolver};
31+
use rustc_resolve::{MakeGlobMap, Resolver, ResolverArenas};
3232
use rustc_metadata::creader::CrateLoader;
3333
use rustc_metadata::cstore::{self, CStore};
3434
use rustc_trans_utils::trans_crate::TransCrate;
@@ -139,6 +139,7 @@ pub fn compile_input(trans: Box<TransCrate>,
139139

140140
let crate_name =
141141
::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input);
142+
142143
let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
143144
phase_2_configure_and_expand(
144145
sess,
@@ -562,6 +563,12 @@ pub struct ExpansionResult {
562563
pub hir_forest: hir_map::Forest,
563564
}
564565

566+
pub struct InnerExpansionResult<'a> {
567+
pub expanded_crate: ast::Crate,
568+
pub resolver: Resolver<'a>,
569+
pub hir_forest: hir_map::Forest,
570+
}
571+
565572
/// Run the "early phases" of the compiler: initial `cfg` processing,
566573
/// loading compiler plugins (including those from `addl_plugins`),
567574
/// syntax expansion, secondary `cfg` expansion, synthesis of a test
@@ -578,6 +585,55 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
578585
make_glob_map: MakeGlobMap,
579586
after_expand: F)
580587
-> Result<ExpansionResult, CompileIncomplete>
588+
where F: FnOnce(&ast::Crate) -> CompileResult {
589+
// Currently, we ignore the name resolution data structures for the purposes of dependency
590+
// tracking. Instead we will run name resolution and include its output in the hash of each
591+
// item, much like we do for macro expansion. In other words, the hash reflects not just
592+
// its contents but the results of name resolution on those contents. Hopefully we'll push
593+
// this back at some point.
594+
let mut crate_loader = CrateLoader::new(sess, &cstore, &crate_name);
595+
let resolver_arenas = Resolver::arenas();
596+
let result = phase_2_configure_and_expand_inner(sess, cstore, krate, registry, crate_name,
597+
addl_plugins, make_glob_map, &resolver_arenas,
598+
&mut crate_loader, after_expand);
599+
match result {
600+
Ok(InnerExpansionResult {expanded_crate, resolver, hir_forest}) => {
601+
Ok(ExpansionResult {
602+
expanded_crate,
603+
defs: resolver.definitions,
604+
hir_forest,
605+
resolutions: Resolutions {
606+
freevars: resolver.freevars,
607+
export_map: resolver.export_map,
608+
trait_map: resolver.trait_map,
609+
maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
610+
maybe_unused_extern_crates: resolver.maybe_unused_extern_crates,
611+
},
612+
613+
analysis: ty::CrateAnalysis {
614+
access_levels: Rc::new(AccessLevels::default()),
615+
name: crate_name.to_string(),
616+
glob_map: if resolver.make_glob_map { Some(resolver.glob_map) } else { None },
617+
},
618+
})
619+
}
620+
Err(x) => Err(x)
621+
}
622+
}
623+
624+
/// Same as phase_2_configure_and_expand, but doesn't let you keep the resolver
625+
/// around
626+
pub fn phase_2_configure_and_expand_inner<'a, F>(sess: &'a Session,
627+
cstore: &'a CStore,
628+
krate: ast::Crate,
629+
registry: Option<Registry>,
630+
crate_name: &str,
631+
addl_plugins: Option<Vec<String>>,
632+
make_glob_map: MakeGlobMap,
633+
resolver_arenas: &'a ResolverArenas<'a>,
634+
crate_loader: &'a mut CrateLoader,
635+
after_expand: F)
636+
-> Result<InnerExpansionResult<'a>, CompileIncomplete>
581637
where F: FnOnce(&ast::Crate) -> CompileResult,
582638
{
583639
let time_passes = sess.time_passes();
@@ -666,19 +722,12 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
666722
return Err(CompileIncomplete::Stopped);
667723
}
668724

669-
// Currently, we ignore the name resolution data structures for the purposes of dependency
670-
// tracking. Instead we will run name resolution and include its output in the hash of each
671-
// item, much like we do for macro expansion. In other words, the hash reflects not just
672-
// its contents but the results of name resolution on those contents. Hopefully we'll push
673-
// this back at some point.
674-
let mut crate_loader = CrateLoader::new(sess, &cstore, crate_name);
675-
let resolver_arenas = Resolver::arenas();
676725
let mut resolver = Resolver::new(sess,
677726
cstore,
678727
&krate,
679728
crate_name,
680729
make_glob_map,
681-
&mut crate_loader,
730+
crate_loader,
682731
&resolver_arenas);
683732
resolver.whitelisted_legacy_custom_derives = whitelisted_legacy_custom_derives;
684733
syntax_ext::register_builtins(&mut resolver, syntax_exts, sess.features.borrow().quote);
@@ -855,21 +904,9 @@ pub fn phase_2_configure_and_expand<F>(sess: &Session,
855904
syntax::ext::hygiene::clear_markings();
856905
}
857906

858-
Ok(ExpansionResult {
907+
Ok(InnerExpansionResult {
859908
expanded_crate: krate,
860-
defs: resolver.definitions,
861-
analysis: ty::CrateAnalysis {
862-
access_levels: Rc::new(AccessLevels::default()),
863-
name: crate_name.to_string(),
864-
glob_map: if resolver.make_glob_map { Some(resolver.glob_map) } else { None },
865-
},
866-
resolutions: Resolutions {
867-
freevars: resolver.freevars,
868-
export_map: resolver.export_map,
869-
trait_map: resolver.trait_map,
870-
maybe_unused_trait_imports: resolver.maybe_unused_trait_imports,
871-
maybe_unused_extern_crates: resolver.maybe_unused_extern_crates,
872-
},
909+
resolver,
873910
hir_forest,
874911
})
875912
}

src/librustc_resolve/lib.rs

+75-14
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ use std::cell::{Cell, RefCell};
6767
use std::cmp;
6868
use std::collections::BTreeSet;
6969
use std::fmt;
70+
use std::iter;
7071
use std::mem::replace;
7172
use std::rc::Rc;
7273

@@ -1320,6 +1321,7 @@ pub struct Resolver<'a> {
13201321
crate_loader: &'a mut CrateLoader,
13211322
macro_names: FxHashSet<Ident>,
13221323
global_macros: FxHashMap<Name, &'a NameBinding<'a>>,
1324+
pub all_macros: FxHashMap<Name, Def>,
13231325
lexical_macro_resolutions: Vec<(Ident, &'a Cell<LegacyScope<'a>>)>,
13241326
macro_map: FxHashMap<DefId, Rc<SyntaxExtension>>,
13251327
macro_defs: FxHashMap<Mark, DefId>,
@@ -1407,6 +1409,71 @@ impl<'a, 'b: 'a> ty::DefIdTree for &'a Resolver<'b> {
14071409

14081410
impl<'a> hir::lowering::Resolver for Resolver<'a> {
14091411
fn resolve_hir_path(&mut self, path: &mut hir::Path, is_value: bool) {
1412+
self.resolve_hir_path_cb(path, is_value,
1413+
|resolver, span, error| resolve_error(resolver, span, error))
1414+
}
1415+
1416+
fn resolve_str_path(&mut self, span: Span, crate_root: Option<&str>,
1417+
components: &[&str], is_value: bool) -> hir::Path {
1418+
let mut path = hir::Path {
1419+
span,
1420+
def: Def::Err,
1421+
segments: iter::once(keywords::CrateRoot.name()).chain({
1422+
crate_root.into_iter().chain(components.iter().cloned()).map(Symbol::intern)
1423+
}).map(hir::PathSegment::from_name).collect(),
1424+
};
1425+
1426+
self.resolve_hir_path(&mut path, is_value);
1427+
path
1428+
}
1429+
1430+
fn get_resolution(&mut self, id: NodeId) -> Option<PathResolution> {
1431+
self.def_map.get(&id).cloned()
1432+
}
1433+
1434+
fn definitions(&mut self) -> &mut Definitions {
1435+
&mut self.definitions
1436+
}
1437+
}
1438+
1439+
impl<'a> Resolver<'a> {
1440+
/// Rustdoc uses this to resolve things in a recoverable way. ResolutionError<'a>
1441+
/// isn't something that can be returned because it can't be made to live that long,
1442+
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
1443+
/// just that an error occured.
1444+
pub fn resolve_str_path_error(&mut self, span: Span, path_str: &str, is_value: bool)
1445+
-> Result<hir::Path, ()> {
1446+
use std::iter;
1447+
let mut errored = false;
1448+
1449+
let mut path = if path_str.starts_with("::") {
1450+
hir::Path {
1451+
span,
1452+
def: Def::Err,
1453+
segments: iter::once(keywords::CrateRoot.name()).chain({
1454+
path_str.split("::").skip(1).map(Symbol::intern)
1455+
}).map(hir::PathSegment::from_name).collect(),
1456+
}
1457+
} else {
1458+
hir::Path {
1459+
span,
1460+
def: Def::Err,
1461+
segments: path_str.split("::").map(Symbol::intern)
1462+
.map(hir::PathSegment::from_name).collect(),
1463+
}
1464+
};
1465+
self.resolve_hir_path_cb(&mut path, is_value, |_, _, _| errored = true);
1466+
if errored || path.def == Def::Err {
1467+
Err(())
1468+
} else {
1469+
Ok(path)
1470+
}
1471+
}
1472+
1473+
/// resolve_hir_path, but takes a callback in case there was an error
1474+
fn resolve_hir_path_cb<F>(&mut self, path: &mut hir::Path, is_value: bool, error_callback: F)
1475+
where F: for<'c, 'b> FnOnce(&'c mut Resolver, Span, ResolutionError<'b>)
1476+
{
14101477
let namespace = if is_value { ValueNS } else { TypeNS };
14111478
let hir::Path { ref segments, span, ref mut def } = *path;
14121479
let path: Vec<SpannedIdent> = segments.iter()
@@ -1418,24 +1485,16 @@ impl<'a> hir::lowering::Resolver for Resolver<'a> {
14181485
*def = path_res.base_def(),
14191486
PathResult::NonModule(..) => match self.resolve_path(&path, None, true, span) {
14201487
PathResult::Failed(span, msg, _) => {
1421-
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
1488+
error_callback(self, span, ResolutionError::FailedToResolve(&msg));
14221489
}
14231490
_ => {}
14241491
},
14251492
PathResult::Indeterminate => unreachable!(),
14261493
PathResult::Failed(span, msg, _) => {
1427-
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
1494+
error_callback(self, span, ResolutionError::FailedToResolve(&msg));
14281495
}
14291496
}
14301497
}
1431-
1432-
fn get_resolution(&mut self, id: NodeId) -> Option<PathResolution> {
1433-
self.def_map.get(&id).cloned()
1434-
}
1435-
1436-
fn definitions(&mut self) -> &mut Definitions {
1437-
&mut self.definitions
1438-
}
14391498
}
14401499

14411500
impl<'a> Resolver<'a> {
@@ -1538,6 +1597,7 @@ impl<'a> Resolver<'a> {
15381597
crate_loader,
15391598
macro_names: FxHashSet(),
15401599
global_macros: FxHashMap(),
1600+
all_macros: FxHashMap(),
15411601
lexical_macro_resolutions: Vec::new(),
15421602
macro_map: FxHashMap(),
15431603
macro_exports: Vec::new(),
@@ -1833,8 +1893,8 @@ impl<'a> Resolver<'a> {
18331893
// generate a fake "implementation scope" containing all the
18341894
// implementations thus found, for compatibility with old resolve pass.
18351895

1836-
fn with_scope<F>(&mut self, id: NodeId, f: F)
1837-
where F: FnOnce(&mut Resolver)
1896+
pub fn with_scope<F, T>(&mut self, id: NodeId, f: F) -> T
1897+
where F: FnOnce(&mut Resolver) -> T
18381898
{
18391899
let id = self.definitions.local_def_id(id);
18401900
let module = self.module_map.get(&id).cloned(); // clones a reference
@@ -1845,13 +1905,14 @@ impl<'a> Resolver<'a> {
18451905
self.ribs[TypeNS].push(Rib::new(ModuleRibKind(module)));
18461906

18471907
self.finalize_current_module_macro_resolutions();
1848-
f(self);
1908+
let ret = f(self);
18491909

18501910
self.current_module = orig_module;
18511911
self.ribs[ValueNS].pop();
18521912
self.ribs[TypeNS].pop();
1913+
ret
18531914
} else {
1854-
f(self);
1915+
f(self)
18551916
}
18561917
}
18571918

src/librustc_resolve/macros.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ impl<'a> Resolver<'a> {
409409
def
410410
}
411411

412-
fn resolve_macro_to_def_inner(&mut self, scope: Mark, path: &ast::Path,
412+
pub fn resolve_macro_to_def_inner(&mut self, scope: Mark, path: &ast::Path,
413413
kind: MacroKind, force: bool)
414414
-> Result<Def, Determinacy> {
415415
let ast::Path { ref segments, span } = *path;
@@ -755,8 +755,9 @@ impl<'a> Resolver<'a> {
755755
*legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding {
756756
parent: Cell::new(*legacy_scope), ident: ident, def_id: def_id, span: item.span,
757757
}));
758+
let def = Def::Macro(def_id, MacroKind::Bang);
759+
self.all_macros.insert(ident.name, def);
758760
if attr::contains_name(&item.attrs, "macro_export") {
759-
let def = Def::Macro(def_id, MacroKind::Bang);
760761
self.macro_exports.push(Export {
761762
ident: ident.modern(),
762763
def: def,

src/librustdoc/clean/inline.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,11 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
135135
None
136136
}
137137
});
138-
let fqn = once(crate_name).chain(relative).collect();
138+
let fqn = if let clean::TypeKind::Macro = kind {
139+
vec![crate_name, relative.last().unwrap()]
140+
} else {
141+
once(crate_name).chain(relative).collect()
142+
};
139143
cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
140144
}
141145

0 commit comments

Comments
 (0)