]> git.lizzy.rs Git - rust.git/commitdiff
rustdoc: Only resolve traits in scope
authorJoshua Nelson <jyn514@gmail.com>
Sat, 8 Aug 2020 18:57:35 +0000 (14:57 -0400)
committerJoshua Nelson <jyn514@gmail.com>
Sat, 22 Aug 2020 04:25:29 +0000 (00:25 -0400)
src/librustdoc/core.rs
src/librustdoc/passes/collect_intra_doc_links.rs
src/test/rustdoc-ui/assoc-item-not-in-scope.rs [new file with mode: 0644]
src/test/rustdoc-ui/assoc-item-not-in-scope.stderr [new file with mode: 0644]

index 51d3b92b3e17a4131ea780258e083455872b40ac..27debfc174339d818baef7b39327015fa19ed698 100644 (file)
@@ -69,11 +69,11 @@ pub struct DocContext<'tcx> {
     pub auto_traits: Vec<DefId>,
     /// The options given to rustdoc that could be relevant to a pass.
     pub render_options: RenderOptions,
-    /// The traits implemented by a given type.
+    /// The traits in scope for a given module.
     ///
     /// See `collect_intra_doc_links::traits_implemented_by` for more details.
-    /// `map<type, set<trait>>`
-    pub type_trait_cache: RefCell<FxHashMap<DefId, FxHashSet<DefId>>>,
+    /// `map<module, set<trait>>`
+    pub module_trait_cache: RefCell<FxHashMap<DefId, FxHashSet<DefId>>>,
 }
 
 impl<'tcx> DocContext<'tcx> {
@@ -515,7 +515,7 @@ pub fn run_core(options: RustdocOptions) -> (clean::Crate, RenderInfo, RenderOpt
                         .filter(|trait_def_id| tcx.trait_is_auto(*trait_def_id))
                         .collect(),
                     render_options,
-                    type_trait_cache: RefCell::new(FxHashMap::default()),
+                    module_trait_cache: RefCell::new(FxHashMap::default()),
                 };
                 debug!("crate: {:?}", tcx.hir().krate());
 
index d6417ec2e1542feab17740eee5d33d75381ec065..2ed3d07974fcf74fbcee0d82bf19a5f01650e35e 100644 (file)
@@ -10,7 +10,7 @@
     PerNS, Res,
 };
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty;
 use rustc_resolve::ParentScope;
 use rustc_session::lint;
 use rustc_span::hygiene::MacroKind;
@@ -327,7 +327,9 @@ fn resolve(
                         // To handle that properly resolve() would have to support
                         // something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
                         .or_else(|| {
-                            let kind = resolve_associated_trait_item(did, item_name, ns, &self.cx);
+                            let kind = resolve_associated_trait_item(
+                                did, module_id, item_name, ns, &self.cx,
+                            );
                             debug!("got associated item kind {:?}", kind);
                             kind
                         });
@@ -440,6 +442,7 @@ fn resolve(
 
 fn resolve_associated_trait_item(
     did: DefId,
+    module: DefId,
     item_name: Symbol,
     ns: Namespace,
     cx: &DocContext<'_>,
@@ -504,8 +507,7 @@ fn resolve_associated_trait_item(
     // Next consider explicit impls: `impl MyTrait for MyType`
     // Give precedence to inherent impls.
     if candidates.is_empty() {
-        let mut cache = cx.type_trait_cache.borrow_mut();
-        let traits = cache.entry(did).or_insert_with(|| traits_implemented_by(cx.tcx, did));
+        let traits = traits_implemented_by(cx, did, module);
         debug!("considering traits {:?}", traits);
         candidates.extend(traits.iter().filter_map(|&trait_| {
             cx.tcx
@@ -519,27 +521,30 @@ fn resolve_associated_trait_item(
     candidates.pop().map(|(_, kind)| kind)
 }
 
-/// Given a type, return all traits implemented by that type.
+/// Given a type, return all traits in scope in `module` implemented by that type.
 ///
 /// NOTE: this cannot be a query because more traits could be available when more crates are compiled!
 /// So it is not stable to serialize cross-crate.
-/// FIXME: this should only search traits in scope
-fn traits_implemented_by<'a>(tcx: TyCtxt<'a>, type_: DefId) -> FxHashSet<DefId> {
-    use rustc_hir::def_id::LOCAL_CRATE;
+fn traits_implemented_by(cx: &DocContext<'_>, type_: DefId, module: DefId) -> FxHashSet<DefId> {
+    let mut cache = cx.module_trait_cache.borrow_mut();
+    let in_scope_traits = cache.entry(module).or_insert_with(|| {
+        cx.enter_resolver(|resolver| {
+            resolver.traits_in_scope(module).into_iter().map(|candidate| candidate.def_id).collect()
+        })
+    });
 
-    let all_traits = tcx.all_traits(LOCAL_CRATE).iter().copied();
-    let ty = tcx.type_of(type_);
-    let iter = all_traits.flat_map(|trait_| {
+    let ty = cx.tcx.type_of(type_);
+    let iter = in_scope_traits.iter().flat_map(|&trait_| {
         trace!("considering explicit impl for trait {:?}", trait_);
         let mut saw_impl = false;
         // Look at each trait implementation to see if it's an impl for `did`
-        tcx.for_each_relevant_impl(trait_, ty, |impl_| {
+        cx.tcx.for_each_relevant_impl(trait_, ty, |impl_| {
             // FIXME: this is inefficient, find a way to short-circuit for_each_* so this doesn't take as long
             if saw_impl {
                 return;
             }
 
-            let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
+            let trait_ref = cx.tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
             // Check if these are the same type.
             let impl_type = trait_ref.self_ty();
             debug!(
diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.rs b/src/test/rustdoc-ui/assoc-item-not-in-scope.rs
new file mode 100644 (file)
index 0000000..c5bb430
--- /dev/null
@@ -0,0 +1,22 @@
+#![deny(broken_intra_doc_links)]
+
+#[derive(Debug)]
+/// Link to [`S::fmt`]
+//~^ ERROR unresolved link
+pub struct S;
+
+pub mod inner {
+    use std::fmt::Debug;
+    use super::S;
+
+    /// Link to [`S::fmt`]
+    pub fn f() {}
+}
+
+pub mod ambiguous {
+    use std::fmt::{Display, Debug};
+    use super::S;
+
+    /// Link to [`S::fmt`]
+    pub fn f() {}
+}
diff --git a/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr b/src/test/rustdoc-ui/assoc-item-not-in-scope.stderr
new file mode 100644 (file)
index 0000000..8827c93
--- /dev/null
@@ -0,0 +1,15 @@
+error: unresolved link to `S::fmt`
+  --> $DIR/assoc-item-not-in-scope.rs:4:14
+   |
+LL | /// Link to [`S::fmt`]
+   |              ^^^^^^^^ unresolved link
+   |
+note: the lint level is defined here
+  --> $DIR/assoc-item-not-in-scope.rs:1:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   = help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
+
+error: aborting due to previous error
+