about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJoshua Nelson <jyn514@gmail.com>2020-08-28 01:31:33 -0400
committerJoshua Nelson <jyn514@gmail.com>2020-09-05 13:56:57 -0400
commitefdc3facdfe931108146587b2f9d22b7dfd217c5 (patch)
treea54e0fbd163b40a8188b336746030ba8c95dbcf8 /src
parentee683ef8532034a4bee01e9aa8fd92dbe38ac6f1 (diff)
downloadrust-efdc3facdfe931108146587b2f9d22b7dfd217c5.tar.gz
rust-efdc3facdfe931108146587b2f9d22b7dfd217c5.zip
Give a much better error message when an item has a macro disambiguator
Previously, this called `check_full_res` for values and types, but not
macros. This would result in not showing when there was a partial
resolution for a parent of the item.

This now calls `check_full_res`. Additionally, it checks if there was a
disambiguator, and if so, says that specific kind wasn't found instead
of saying generically 'associated item'.
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs87
-rw-r--r--src/test/rustdoc-ui/intra-link-errors.rs2
-rw-r--r--src/test/rustdoc-ui/intra-link-errors.stderr2
3 files changed, 59 insertions, 32 deletions
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index a19387ad992..df8ecf26a41 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -262,11 +262,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                 false,
             ) {
                 if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
-                    return Ok(res.map_id(|_| panic!("unexpected id")));
+                    return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
                 }
             }
             if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
-                return Ok(res.map_id(|_| panic!("unexpected id")));
+                return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
             }
             if let Some(module_id) = parent_id {
                 debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
@@ -276,14 +276,32 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                     // don't resolve builtins like `#[derive]`
                     if let Res::Def(..) = res {
                         let res = res.map_id(|_| panic!("unexpected node_id"));
-                        return Ok(res);
+                        return Some(Ok(res));
                     }
                 }
             } else {
                 debug!("attempting to resolve item without parent module: {}", path_str);
-                return Err(ResolutionFailure::NoParentItem);
+                return Some(Err(ResolutionFailure::NoParentItem));
             }
-            return Err(ResolutionFailure::NotInScope(path_str.into()));
+            None
+        })
+        // This weird control flow is so we don't borrow the resolver more than once at a time
+        .unwrap_or_else(|| {
+            let mut split = path_str.rsplitn(2, "::");
+            if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
+                if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) {
+                    return Err(if matches!(res, Res::PrimTy(_)) {
+                        ResolutionFailure::NoPrimitiveAssocItem {
+                            res,
+                            prim_name: parent,
+                            assoc_item: Symbol::intern(base),
+                        }
+                    } else {
+                        ResolutionFailure::NoAssocItem(res, Symbol::intern(base))
+                    });
+                }
+            }
+            Err(ResolutionFailure::NotInScope(path_str.into()))
         })
     }
     /// Resolves a string as a path within a particular namespace. Also returns an optional
@@ -981,6 +999,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                                     cx,
                                     &item,
                                     path_str,
+                                    disambiguator,
                                     &dox,
                                     link_range,
                                     smallvec![kind],
@@ -1060,6 +1079,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                                 cx,
                                 &item,
                                 path_str,
+                                disambiguator,
                                 &dox,
                                 link_range,
                                 candidates.into_iter().filter_map(|res| res.err()).collect(),
@@ -1114,6 +1134,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                                     cx,
                                     &item,
                                     path_str,
+                                    disambiguator,
                                     &dox,
                                     link_range,
                                     smallvec![kind],
@@ -1489,6 +1510,7 @@ fn resolution_failure(
     cx: &DocContext<'_>,
     item: &Item,
     path_str: &str,
+    disambiguator: Option<Disambiguator>,
     dox: &str,
     link_range: Option<Range<usize>>,
     kinds: SmallVec<[ResolutionFailure<'_>; 3]>,
@@ -1581,34 +1603,39 @@ fn resolution_failure(
 
                         let (kind, def_id) = match res {
                             Res::Def(kind, def_id) => (kind, def_id),
-                            _ => unreachable!(
-                                "primitives are covered above and other `Res` variants aren't possible at module scope"
+                            x => unreachable!(
+                                "primitives are covered above and other `Res` variants aren't possible at module scope: {:?}",
+                                x,
                             ),
                         };
                         let name = cx.tcx.item_name(def_id);
-                        let path_description = match kind {
-                            Mod | ForeignMod => "inner item",
-                            Struct => "field or associated item",
-                            Enum | Union => "variant or associated item",
-                            Variant
-                            | Field
-                            | Closure
-                            | Generator
-                            | AssocTy
-                            | AssocConst
-                            | AssocFn
-                            | Fn
-                            | Macro(_)
-                            | Const
-                            | ConstParam
-                            | ExternCrate
-                            | Use
-                            | LifetimeParam
-                            | Ctor(_, _)
-                            | AnonConst => return assoc_item_not_allowed(res, diag),
-                            Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
-                            | Static => "associated item",
-                            Impl | GlobalAsm => unreachable!("not a path"),
+                        let path_description = if let Some(disambiguator) = disambiguator {
+                            disambiguator.descr()
+                        } else {
+                            match kind {
+                                Mod | ForeignMod => "inner item",
+                                Struct => "field or associated item",
+                                Enum | Union => "variant or associated item",
+                                Variant
+                                | Field
+                                | Closure
+                                | Generator
+                                | AssocTy
+                                | AssocConst
+                                | AssocFn
+                                | Fn
+                                | Macro(_)
+                                | Const
+                                | ConstParam
+                                | ExternCrate
+                                | Use
+                                | LifetimeParam
+                                | Ctor(_, _)
+                                | AnonConst => return assoc_item_not_allowed(res, diag),
+                                Trait | TyAlias | ForeignTy | OpaqueTy | TraitAlias | TyParam
+                                | Static => "associated item",
+                                Impl | GlobalAsm => unreachable!("not a path"),
+                            }
                         };
                         let note = format!(
                             "the {} `{}` has no {} named `{}`",
diff --git a/src/test/rustdoc-ui/intra-link-errors.rs b/src/test/rustdoc-ui/intra-link-errors.rs
index 919c46fe1da..ae89f418984 100644
--- a/src/test/rustdoc-ui/intra-link-errors.rs
+++ b/src/test/rustdoc-ui/intra-link-errors.rs
@@ -65,7 +65,7 @@ impl S {
 
 /// [T::h!]
 //~^ ERROR unresolved link
-//~| NOTE no item named `T::h`
+//~| NOTE `T` has no macro named `h`
 pub trait T {
     fn g() {}
 }
diff --git a/src/test/rustdoc-ui/intra-link-errors.stderr b/src/test/rustdoc-ui/intra-link-errors.stderr
index 7318193964f..68941d64c7a 100644
--- a/src/test/rustdoc-ui/intra-link-errors.stderr
+++ b/src/test/rustdoc-ui/intra-link-errors.stderr
@@ -97,7 +97,7 @@ error: unresolved link to `T::h`
 LL | /// [T::h!]
    |      ^^^^^
    |
-   = note: no item named `T::h` is in scope
+   = note: the trait `T` has no macro named `h`
 
 error: unresolved link to `S::h`
   --> $DIR/intra-link-errors.rs:54:6