]> git.lizzy.rs Git - rust.git/blobdiff - src/librustdoc/passes/collect_intra_doc_links.rs
Pass on the DefId so rustdoc can name it in suggestions
[rust.git] / src / librustdoc / passes / collect_intra_doc_links.rs
index 9bf30c71892aa94c1a9ad26164d883ddf830abdf..287f5fcf805efa7c6ca527f6792053d78019ece4 100644 (file)
@@ -81,6 +81,7 @@ enum ResolutionFailure<'a> {
 }
 
 impl ResolutionFailure<'a> {
+    // A partial or full resolution
     fn res(&self) -> Option<Res> {
         use ResolutionFailure::*;
         match self {
@@ -93,6 +94,14 @@ fn res(&self) -> Option<Res> {
             NotInScope(_) | NoParentItem | Dummy => None,
         }
     }
+
+    // This resolved fully (not just partially) but is erroneous for some other reason
+    fn full_res(&self) -> Option<Res> {
+        match self {
+            Self::WrongNamespace(res, _) => Some(*res),
+            _ => None,
+        }
+    }
 }
 
 enum AnchorFailure {
@@ -112,7 +121,7 @@ struct LinkCollector<'a, 'tcx> {
     /// This is used to store the kind of associated items,
     /// because `clean` and the disambiguator code expect them to be different.
     /// See the code for associated items on inherent impls for details.
-    kind_side_channel: Cell<Option<DefKind>>,
+    kind_side_channel: Cell<Option<(DefKind, DefId)>>,
 }
 
 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
@@ -128,6 +137,7 @@ fn variant_field(
     ) -> Result<(Res, Option<String>), ErrorKind<'path>> {
         let cx = self.cx;
 
+        debug!("looking for enum variant {}", path_str);
         let mut split = path_str.rsplitn(3, "::");
         let variant_field_name = split
             .next()
@@ -260,7 +270,7 @@ fn resolve<'path>(
             debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
             let result = match result {
                 Ok((_, Res::Err)) => Err(()),
-                _ => result.map_err(|_| ()),
+                x => x,
             };
 
             if let Ok((_, res)) = result {
@@ -371,7 +381,7 @@ fn resolve<'path>(
                 ) => {
                     debug!("looking for associated item named {} for item {:?}", item_name, did);
                     // Checks if item_name belongs to `impl SomeItem`
-                    let kind = cx
+                    let assoc_item = cx
                         .tcx
                         .inherent_impls(did)
                         .iter()
@@ -383,7 +393,7 @@ fn resolve<'path>(
                                 imp,
                             )
                         })
-                        .map(|item| item.kind)
+                        .map(|item| (item.kind, item.def_id))
                         // There should only ever be one associated item that matches from any inherent impl
                         .next()
                         // Check if item_name belongs to `impl SomeTrait for SomeItem`
@@ -399,7 +409,7 @@ fn resolve<'path>(
                             kind
                         });
 
-                    if let Some(kind) = kind {
+                    if let Some((kind, id)) = assoc_item {
                         let out = match kind {
                             ty::AssocKind::Fn => "method",
                             ty::AssocKind::Const => "associatedconstant",
@@ -415,10 +425,11 @@ fn resolve<'path>(
                             // HACK(jynelson): `clean` expects the type, not the associated item.
                             // but the disambiguator logic expects the associated item.
                             // Store the kind in a side channel so that only the disambiguator logic looks at it.
-                            self.kind_side_channel.set(Some(kind.as_def_kind()));
+                            self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
                             Ok((ty_res, Some(format!("{}.{}", out, item_name))))
                         })
                     } else if ns == Namespace::ValueNS {
+                        debug!("looking for variants or fields named {} for {:?}", item_name, did);
                         match cx.tcx.type_of(did).kind() {
                             ty::Adt(def, _) => {
                                 let field = if def.is_enum() {
@@ -514,7 +525,7 @@ fn resolve_associated_trait_item(
     item_name: Symbol,
     ns: Namespace,
     cx: &DocContext<'_>,
-) -> Option<ty::AssocKind> {
+) -> Option<(ty::AssocKind, DefId)> {
     let ty = cx.tcx.type_of(did);
     // First consider automatic impls: `impl From<T> for T`
     let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
@@ -542,7 +553,7 @@ fn resolve_associated_trait_item(
                             // but provided methods come directly from `tcx`.
                             // Fortunately, we don't need the whole method, we just need to know
                             // what kind of associated item it is.
-                            Some((assoc.def_id, kind))
+                            Some((kind, assoc.def_id))
                         });
                         let assoc = items.next();
                         debug_assert_eq!(items.count(), 0);
@@ -564,7 +575,7 @@ fn resolve_associated_trait_item(
                                 ns,
                                 trait_,
                             )
-                            .map(|assoc| (assoc.def_id, assoc.kind))
+                            .map(|assoc| (assoc.kind, assoc.def_id))
                     }
                 }
                 _ => panic!("get_impls returned something that wasn't an impl"),
@@ -581,12 +592,12 @@ fn resolve_associated_trait_item(
             cx.tcx
                 .associated_items(trait_)
                 .find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, trait_)
-                .map(|assoc| (assoc.def_id, assoc.kind))
+                .map(|assoc| (assoc.kind, assoc.def_id))
         }));
     }
     // FIXME: warn about ambiguity
     debug!("the candidates were {:?}", candidates);
-    candidates.pop().map(|(_, kind)| kind)
+    candidates.pop()
 }
 
 /// Given a type, return all traits in scope in `module` implemented by that type.
@@ -838,12 +849,40 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                     }
                 }
 
+                // used for reporting better errors
+                let check_full_res = |this: &mut Self, ns| {
+                    let res =
+                        match this.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
+                        {
+                            Ok(res) => {
+                                debug!(
+                                    "check_full_res: saw res for {} in {:?} ns: {:?}",
+                                    path_str, ns, res.0
+                                );
+                                Some(res.0)
+                            }
+                            Err(ErrorKind::Resolve(kind)) => kind.full_res(),
+                            // TODO: add `Res` to AnchorFailure
+                            Err(ErrorKind::AnchorFailure(_)) => None,
+                        };
+                    this.kind_side_channel.take().map(|(kind, id)| Res::Def(kind, id)).or(res)
+                };
+
                 match disambiguator.map(Disambiguator::ns) {
                     Some(ns @ (ValueNS | TypeNS)) => {
                         match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
                         {
                             Ok(res) => res,
-                            Err(ErrorKind::Resolve(kind)) => {
+                            Err(ErrorKind::Resolve(mut kind)) => {
+                                // We only looked in one namespace. Try to give a better error if possible.
+                                // TODO: handle MacroNS too
+                                if kind.full_res().is_none() {
+                                    let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
+                                    if let Some(res) = check_full_res(self, other_ns) {
+                                        // recall that this stores the _expected_ namespace
+                                        kind = ResolutionFailure::WrongNamespace(res, ns);
+                                    }
+                                }
                                 resolution_failure(
                                     cx,
                                     &item,
@@ -965,30 +1004,14 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                             Ok(res) => (res, extra_fragment),
                             Err(mut kind) => {
                                 // `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
+                                //if kind.res().is_none() {
                                 for &ns in &[TypeNS, ValueNS] {
-                                    match self.resolve(
-                                        path_str,
-                                        ns,
-                                        &current_item,
-                                        base_node,
-                                        &extra_fragment,
-                                    ) {
-                                        Ok(res) => {
-                                            kind = ResolutionFailure::WrongNamespace(res.0, MacroNS)
-                                        }
-                                        // This will show up in the other namespace, no need to handle it here
-                                        Err(ErrorKind::Resolve(
-                                            ResolutionFailure::WrongNamespace(..),
-                                        )) => {}
-                                        Err(ErrorKind::AnchorFailure(_)) => {}
-                                        Err(ErrorKind::Resolve(inner_kind)) => {
-                                            if let Some(res) = inner_kind.res() {
-                                                kind =
-                                                    ResolutionFailure::WrongNamespace(res, MacroNS);
-                                            }
-                                        }
+                                    if let Some(res) = check_full_res(self, ns) {
+                                        kind = ResolutionFailure::WrongNamespace(res, MacroNS);
+                                        break;
                                     }
                                 }
+                                //}
                                 resolution_failure(
                                     cx,
                                     &item,
@@ -1073,7 +1096,7 @@ fn fold_item(&mut self, mut item: Item) -> Option<Item> {
                 // Disallow e.g. linking to enums with `struct@`
                 if let Res::Def(kind, _) = res {
                     debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
-                    match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
+                    match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
                         | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
                         // NOTE: this allows 'method' to mean both normal functions and associated functions
                         // This can't cause ambiguity because both are in the same namespace.