Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c1589cc

Browse files
committedSep 15, 2020
Auto merge of #76684 - jyn514:refactor-intra-links, r=manishearth
Refactor intra doc link code I got tired of `fold_item` being 500 lines long. This is best reviewed one commit at a time with whitespace changes hidden. There are no logic changes other than the last commit making a parameter checked by the caller instead of the callee. r? `@Manishearth`
2 parents 6cae281 + 8a13fc4 commit c1589cc

File tree

1 file changed

+659
-631
lines changed

1 file changed

+659
-631
lines changed
 

‎src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 659 additions & 631 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
217217
let kind = if let Some(intermediate) = self.check_full_res(
218218
TypeNS,
219219
&intermediate_path,
220-
Some(module_id),
220+
module_id,
221221
current_item,
222222
extra_fragment,
223223
) {
@@ -235,7 +235,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
235235
fn macro_resolve(
236236
&self,
237237
path_str: &'a str,
238-
parent_id: Option<DefId>,
238+
module_id: DefId,
239239
) -> Result<Res, ResolutionFailure<'a>> {
240240
let cx = self.cx;
241241
let path = ast::Path::from_ident(Ident::from_str(path_str));
@@ -254,28 +254,23 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
254254
if let Some(res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
255255
return Some(Ok(res.map_id(|_| panic!("unexpected id"))));
256256
}
257-
if let Some(module_id) = parent_id {
258-
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
259-
if let Ok((_, res)) =
260-
resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
261-
{
262-
// don't resolve builtins like `#[derive]`
263-
if let Res::Def(..) = res {
264-
let res = res.map_id(|_| panic!("unexpected node_id"));
265-
return Some(Ok(res));
266-
}
257+
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
258+
if let Ok((_, res)) =
259+
resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
260+
{
261+
// don't resolve builtins like `#[derive]`
262+
if let Res::Def(..) = res {
263+
let res = res.map_id(|_| panic!("unexpected node_id"));
264+
return Some(Ok(res));
267265
}
268-
} else {
269-
debug!("attempting to resolve item without parent module: {}", path_str);
270-
return Some(Err(ResolutionFailure::NoParentItem));
271266
}
272267
None
273268
})
274269
// This weird control flow is so we don't borrow the resolver more than once at a time
275270
.unwrap_or_else(|| {
276271
let mut split = path_str.rsplitn(2, "::");
277272
if let Some((parent, base)) = split.next().and_then(|x| Some((split.next()?, x))) {
278-
if let Some(res) = self.check_full_res(TypeNS, parent, parent_id, &None, &None) {
273+
if let Some(res) = self.check_full_res(TypeNS, parent, module_id, &None, &None) {
279274
return Err(if matches!(res, Res::PrimTy(_)) {
280275
ResolutionFailure::NoPrimitiveAssocItem {
281276
res,
@@ -287,306 +282,282 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
287282
});
288283
}
289284
}
290-
Err(ResolutionFailure::NotInScope {
291-
module_id: parent_id.expect("already saw `Some` when resolving as a macro"),
292-
name: path_str.into(),
293-
})
285+
Err(ResolutionFailure::NotInScope { module_id, name: path_str.into() })
294286
})
295287
}
288+
296289
/// Resolves a string as a path within a particular namespace. Also returns an optional
297290
/// URL fragment in the case of variants and methods.
298291
fn resolve<'path>(
299292
&self,
300293
path_str: &'path str,
301294
ns: Namespace,
302295
current_item: &Option<String>,
303-
parent_id: Option<DefId>,
296+
module_id: DefId,
304297
extra_fragment: &Option<String>,
305298
) -> Result<(Res, Option<String>), ErrorKind<'path>> {
306299
let cx = self.cx;
307300

308-
// In case we're in a module, try to resolve the relative path.
309-
if let Some(module_id) = parent_id {
310-
let result = cx.enter_resolver(|resolver| {
311-
resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
312-
});
313-
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
314-
let result = match result {
315-
Ok((_, Res::Err)) => Err(()),
316-
x => x,
317-
};
301+
let result = cx.enter_resolver(|resolver| {
302+
resolver.resolve_str_path_error(DUMMY_SP, &path_str, ns, module_id)
303+
});
304+
debug!("{} resolved to {:?} in namespace {:?}", path_str, result, ns);
305+
let result = match result {
306+
Ok((_, Res::Err)) => Err(()),
307+
x => x,
308+
};
318309

319-
if let Ok((_, res)) = result {
320-
let res = res.map_id(|_| panic!("unexpected node_id"));
321-
// In case this is a trait item, skip the
322-
// early return and try looking for the trait.
323-
let value = match res {
324-
Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
325-
Res::Def(DefKind::AssocTy, _) => false,
326-
Res::Def(DefKind::Variant, _) => {
327-
return handle_variant(cx, res, extra_fragment);
328-
}
329-
// Not a trait item; just return what we found.
330-
Res::PrimTy(..) => {
331-
if extra_fragment.is_some() {
332-
return Err(ErrorKind::AnchorFailure(
333-
AnchorFailure::RustdocAnchorConflict(res),
334-
));
335-
}
336-
return Ok((res, Some(path_str.to_owned())));
337-
}
338-
Res::Def(DefKind::Mod, _) => {
339-
return Ok((res, extra_fragment.clone()));
340-
}
341-
_ => {
342-
return Ok((res, extra_fragment.clone()));
310+
if let Ok((_, res)) = result {
311+
let res = res.map_id(|_| panic!("unexpected node_id"));
312+
// In case this is a trait item, skip the
313+
// early return and try looking for the trait.
314+
let value = match res {
315+
Res::Def(DefKind::AssocFn | DefKind::AssocConst, _) => true,
316+
Res::Def(DefKind::AssocTy, _) => false,
317+
Res::Def(DefKind::Variant, _) => {
318+
return handle_variant(cx, res, extra_fragment);
319+
}
320+
// Not a trait item; just return what we found.
321+
Res::PrimTy(..) => {
322+
if extra_fragment.is_some() {
323+
return Err(ErrorKind::AnchorFailure(
324+
AnchorFailure::RustdocAnchorConflict(res),
325+
));
343326
}
344-
};
345-
346-
if value != (ns == ValueNS) {
347-
return Err(ResolutionFailure::WrongNamespace(res, ns).into());
327+
return Ok((res, Some(path_str.to_owned())));
328+
}
329+
Res::Def(DefKind::Mod, _) => {
330+
return Ok((res, extra_fragment.clone()));
348331
}
349-
} else if let Some((path, prim)) = is_primitive(path_str, ns) {
350-
if extra_fragment.is_some() {
351-
return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
352-
prim,
353-
)));
332+
_ => {
333+
return Ok((res, extra_fragment.clone()));
354334
}
355-
return Ok((prim, Some(path.to_owned())));
335+
};
336+
337+
if value != (ns == ValueNS) {
338+
return Err(ResolutionFailure::WrongNamespace(res, ns).into());
356339
}
340+
} else if let Some((path, prim)) = is_primitive(path_str, ns) {
341+
if extra_fragment.is_some() {
342+
return Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(prim)));
343+
}
344+
return Ok((prim, Some(path.to_owned())));
345+
}
357346

358-
// Try looking for methods and associated items.
359-
let mut split = path_str.rsplitn(2, "::");
360-
// this can be an `unwrap()` because we ensure the link is never empty
361-
let item_name = Symbol::intern(split.next().unwrap());
362-
let path_root = split
363-
.next()
364-
.map(|f| {
365-
if f == "self" || f == "Self" {
366-
if let Some(name) = current_item.as_ref() {
367-
return name.clone();
368-
}
369-
}
370-
f.to_owned()
371-
})
372-
// If there's no `::`, it's not an associated item.
373-
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
374-
.ok_or_else(|| {
375-
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
376-
ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() }
377-
})?;
378-
379-
if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
380-
let impls = primitive_impl(cx, &path)
381-
.ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?;
382-
for &impl_ in impls {
383-
let link = cx
384-
.tcx
385-
.associated_items(impl_)
386-
.find_by_name_and_namespace(
387-
cx.tcx,
388-
Ident::with_dummy_span(item_name),
389-
ns,
390-
impl_,
391-
)
392-
.map(|item| match item.kind {
393-
ty::AssocKind::Fn => "method",
394-
ty::AssocKind::Const => "associatedconstant",
395-
ty::AssocKind::Type => "associatedtype",
396-
})
397-
.map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))));
398-
if let Some(link) = link {
399-
return Ok(link);
347+
// Try looking for methods and associated items.
348+
let mut split = path_str.rsplitn(2, "::");
349+
// this can be an `unwrap()` because we ensure the link is never empty
350+
let item_name = Symbol::intern(split.next().unwrap());
351+
let path_root = split
352+
.next()
353+
.map(|f| {
354+
if f == "self" || f == "Self" {
355+
if let Some(name) = current_item.as_ref() {
356+
return name.clone();
400357
}
401358
}
402-
debug!(
403-
"returning primitive error for {}::{} in {} namespace",
404-
path,
405-
item_name,
406-
ns.descr()
407-
);
408-
return Err(ResolutionFailure::NoPrimitiveAssocItem {
409-
res: prim,
410-
prim_name: path,
411-
assoc_item: item_name,
359+
f.to_owned()
360+
})
361+
// If there's no `::`, it's not an associated item.
362+
// So we can be sure that `rustc_resolve` was accurate when it said it wasn't resolved.
363+
.ok_or_else(|| {
364+
debug!("found no `::`, assumming {} was correctly not in scope", item_name);
365+
ResolutionFailure::NotInScope { module_id, name: item_name.to_string().into() }
366+
})?;
367+
368+
if let Some((path, prim)) = is_primitive(&path_root, TypeNS) {
369+
let impls = primitive_impl(cx, &path)
370+
.ok_or_else(|| ResolutionFailure::NoPrimitiveImpl(prim, path_root.into()))?;
371+
for &impl_ in impls {
372+
let link = cx
373+
.tcx
374+
.associated_items(impl_)
375+
.find_by_name_and_namespace(
376+
cx.tcx,
377+
Ident::with_dummy_span(item_name),
378+
ns,
379+
impl_,
380+
)
381+
.map(|item| match item.kind {
382+
ty::AssocKind::Fn => "method",
383+
ty::AssocKind::Const => "associatedconstant",
384+
ty::AssocKind::Type => "associatedtype",
385+
})
386+
.map(|out| (prim, Some(format!("{}#{}.{}", path, out, item_name))));
387+
if let Some(link) = link {
388+
return Ok(link);
412389
}
413-
.into());
414390
}
391+
debug!(
392+
"returning primitive error for {}::{} in {} namespace",
393+
path,
394+
item_name,
395+
ns.descr()
396+
);
397+
return Err(ResolutionFailure::NoPrimitiveAssocItem {
398+
res: prim,
399+
prim_name: path,
400+
assoc_item: item_name,
401+
}
402+
.into());
403+
}
415404

416-
let ty_res = cx
417-
.enter_resolver(|resolver| {
418-
// only types can have associated items
419-
resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
420-
})
421-
.map(|(_, res)| res);
422-
let ty_res = match ty_res {
423-
Err(()) | Ok(Res::Err) => {
424-
return if ns == Namespace::ValueNS {
425-
self.variant_field(path_str, current_item, module_id, extra_fragment)
426-
} else {
427-
// See if it only broke because of the namespace.
428-
let kind = cx.enter_resolver(|resolver| {
429-
// NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it)
430-
for &ns in &[MacroNS, ValueNS] {
431-
match resolver
432-
.resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id)
433-
{
434-
Ok((_, Res::Err)) | Err(()) => {}
435-
Ok((_, res)) => {
436-
let res = res.map_id(|_| panic!("unexpected node_id"));
437-
return ResolutionFailure::CannotHaveAssociatedItems(
438-
res, ns,
439-
);
440-
}
405+
let ty_res = cx
406+
.enter_resolver(|resolver| {
407+
// only types can have associated items
408+
resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
409+
})
410+
.map(|(_, res)| res);
411+
let ty_res = match ty_res {
412+
Err(()) | Ok(Res::Err) => {
413+
return if ns == Namespace::ValueNS {
414+
self.variant_field(path_str, current_item, module_id, extra_fragment)
415+
} else {
416+
// See if it only broke because of the namespace.
417+
let kind = cx.enter_resolver(|resolver| {
418+
// NOTE: this doesn't use `check_full_res` because we explicitly want to ignore `TypeNS` (we already checked it)
419+
for &ns in &[MacroNS, ValueNS] {
420+
match resolver
421+
.resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id)
422+
{
423+
Ok((_, Res::Err)) | Err(()) => {}
424+
Ok((_, res)) => {
425+
let res = res.map_id(|_| panic!("unexpected node_id"));
426+
return ResolutionFailure::CannotHaveAssociatedItems(res, ns);
441427
}
442428
}
443-
ResolutionFailure::NotInScope { module_id, name: path_root.into() }
444-
});
445-
Err(kind.into())
446-
};
447-
}
448-
Ok(res) => res,
449-
};
450-
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
451-
let res = match ty_res {
452-
Res::Def(
453-
DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias,
454-
did,
455-
) => {
456-
debug!("looking for associated item named {} for item {:?}", item_name, did);
457-
// Checks if item_name belongs to `impl SomeItem`
458-
let assoc_item = cx
459-
.tcx
460-
.inherent_impls(did)
461-
.iter()
462-
.flat_map(|&imp| {
463-
cx.tcx.associated_items(imp).find_by_name_and_namespace(
464-
cx.tcx,
465-
Ident::with_dummy_span(item_name),
466-
ns,
467-
imp,
468-
)
469-
})
470-
.map(|item| (item.kind, item.def_id))
471-
// There should only ever be one associated item that matches from any inherent impl
472-
.next()
473-
// Check if item_name belongs to `impl SomeTrait for SomeItem`
474-
// This gives precedence to `impl SomeItem`:
475-
// Although having both would be ambiguous, use impl version for compat. sake.
476-
// To handle that properly resolve() would have to support
477-
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
478-
.or_else(|| {
479-
let kind = resolve_associated_trait_item(
480-
did, module_id, item_name, ns, &self.cx,
481-
);
482-
debug!("got associated item kind {:?}", kind);
483-
kind
484-
});
485-
486-
if let Some((kind, id)) = assoc_item {
487-
let out = match kind {
488-
ty::AssocKind::Fn => "method",
489-
ty::AssocKind::Const => "associatedconstant",
490-
ty::AssocKind::Type => "associatedtype",
491-
};
492-
Some(if extra_fragment.is_some() {
493-
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
494-
ty_res,
495-
)))
496-
} else {
497-
// HACK(jynelson): `clean` expects the type, not the associated item.
498-
// but the disambiguator logic expects the associated item.
499-
// Store the kind in a side channel so that only the disambiguator logic looks at it.
500-
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
501-
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
502-
})
503-
} else if ns == Namespace::ValueNS {
504-
debug!("looking for variants or fields named {} for {:?}", item_name, did);
505-
match cx.tcx.type_of(did).kind() {
506-
ty::Adt(def, _) => {
507-
let field = if def.is_enum() {
508-
def.all_fields().find(|item| item.ident.name == item_name)
509-
} else {
510-
def.non_enum_variant()
511-
.fields
512-
.iter()
513-
.find(|item| item.ident.name == item_name)
514-
};
515-
field.map(|item| {
516-
if extra_fragment.is_some() {
517-
let res = Res::Def(
518-
if def.is_enum() {
519-
DefKind::Variant
520-
} else {
521-
DefKind::Field
522-
},
523-
item.did,
524-
);
525-
Err(ErrorKind::AnchorFailure(
526-
AnchorFailure::RustdocAnchorConflict(res),
527-
))
528-
} else {
529-
Ok((
530-
ty_res,
531-
Some(format!(
532-
"{}.{}",
533-
if def.is_enum() {
534-
"variant"
535-
} else {
536-
"structfield"
537-
},
538-
item.ident
539-
)),
540-
))
541-
}
542-
})
543-
}
544-
_ => None,
545429
}
546-
} else {
547-
// We already know this isn't in ValueNS, so no need to check variant_field
548-
return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into());
549-
}
550-
}
551-
Res::Def(DefKind::Trait, did) => cx
430+
ResolutionFailure::NotInScope { module_id, name: path_root.into() }
431+
});
432+
Err(kind.into())
433+
};
434+
}
435+
Ok(res) => res,
436+
};
437+
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
438+
let res = match ty_res {
439+
Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum | DefKind::TyAlias, did) => {
440+
debug!("looking for associated item named {} for item {:?}", item_name, did);
441+
// Checks if item_name belongs to `impl SomeItem`
442+
let assoc_item = cx
552443
.tcx
553-
.associated_items(did)
554-
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, did)
555-
.map(|item| {
556-
let kind = match item.kind {
557-
ty::AssocKind::Const => "associatedconstant",
558-
ty::AssocKind::Type => "associatedtype",
559-
ty::AssocKind::Fn => {
560-
if item.defaultness.has_value() {
561-
"method"
444+
.inherent_impls(did)
445+
.iter()
446+
.flat_map(|&imp| {
447+
cx.tcx.associated_items(imp).find_by_name_and_namespace(
448+
cx.tcx,
449+
Ident::with_dummy_span(item_name),
450+
ns,
451+
imp,
452+
)
453+
})
454+
.map(|item| (item.kind, item.def_id))
455+
// There should only ever be one associated item that matches from any inherent impl
456+
.next()
457+
// Check if item_name belongs to `impl SomeTrait for SomeItem`
458+
// This gives precedence to `impl SomeItem`:
459+
// Although having both would be ambiguous, use impl version for compat. sake.
460+
// To handle that properly resolve() would have to support
461+
// something like [`ambi_fn`](<SomeStruct as SomeTrait>::ambi_fn)
462+
.or_else(|| {
463+
let kind =
464+
resolve_associated_trait_item(did, module_id, item_name, ns, &self.cx);
465+
debug!("got associated item kind {:?}", kind);
466+
kind
467+
});
468+
469+
if let Some((kind, id)) = assoc_item {
470+
let out = match kind {
471+
ty::AssocKind::Fn => "method",
472+
ty::AssocKind::Const => "associatedconstant",
473+
ty::AssocKind::Type => "associatedtype",
474+
};
475+
Some(if extra_fragment.is_some() {
476+
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
477+
} else {
478+
// HACK(jynelson): `clean` expects the type, not the associated item.
479+
// but the disambiguator logic expects the associated item.
480+
// Store the kind in a side channel so that only the disambiguator logic looks at it.
481+
self.kind_side_channel.set(Some((kind.as_def_kind(), id)));
482+
Ok((ty_res, Some(format!("{}.{}", out, item_name))))
483+
})
484+
} else if ns == Namespace::ValueNS {
485+
debug!("looking for variants or fields named {} for {:?}", item_name, did);
486+
match cx.tcx.type_of(did).kind() {
487+
ty::Adt(def, _) => {
488+
let field = if def.is_enum() {
489+
def.all_fields().find(|item| item.ident.name == item_name)
490+
} else {
491+
def.non_enum_variant()
492+
.fields
493+
.iter()
494+
.find(|item| item.ident.name == item_name)
495+
};
496+
field.map(|item| {
497+
if extra_fragment.is_some() {
498+
let res = Res::Def(
499+
if def.is_enum() {
500+
DefKind::Variant
501+
} else {
502+
DefKind::Field
503+
},
504+
item.did,
505+
);
506+
Err(ErrorKind::AnchorFailure(
507+
AnchorFailure::RustdocAnchorConflict(res),
508+
))
562509
} else {
563-
"tymethod"
510+
Ok((
511+
ty_res,
512+
Some(format!(
513+
"{}.{}",
514+
if def.is_enum() { "variant" } else { "structfield" },
515+
item.ident
516+
)),
517+
))
564518
}
565-
}
566-
};
567-
568-
if extra_fragment.is_some() {
569-
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(
570-
ty_res,
571-
)))
572-
} else {
573-
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
574-
Ok((res, Some(format!("{}.{}", kind, item_name))))
519+
})
575520
}
576-
}),
577-
_ => None,
578-
};
579-
res.unwrap_or_else(|| {
580-
if ns == Namespace::ValueNS {
581-
self.variant_field(path_str, current_item, module_id, extra_fragment)
521+
_ => None,
522+
}
582523
} else {
583-
Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into())
524+
// We already know this isn't in ValueNS, so no need to check variant_field
525+
return Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into());
584526
}
585-
})
586-
} else {
587-
debug!("attempting to resolve item without parent module: {}", path_str);
588-
Err(ResolutionFailure::NoParentItem.into())
589-
}
527+
}
528+
Res::Def(DefKind::Trait, did) => cx
529+
.tcx
530+
.associated_items(did)
531+
.find_by_name_and_namespace(cx.tcx, Ident::with_dummy_span(item_name), ns, did)
532+
.map(|item| {
533+
let kind = match item.kind {
534+
ty::AssocKind::Const => "associatedconstant",
535+
ty::AssocKind::Type => "associatedtype",
536+
ty::AssocKind::Fn => {
537+
if item.defaultness.has_value() {
538+
"method"
539+
} else {
540+
"tymethod"
541+
}
542+
}
543+
};
544+
545+
if extra_fragment.is_some() {
546+
Err(ErrorKind::AnchorFailure(AnchorFailure::RustdocAnchorConflict(ty_res)))
547+
} else {
548+
let res = Res::Def(item.kind.as_def_kind(), item.def_id);
549+
Ok((res, Some(format!("{}.{}", kind, item_name))))
550+
}
551+
}),
552+
_ => None,
553+
};
554+
res.unwrap_or_else(|| {
555+
if ns == Namespace::ValueNS {
556+
self.variant_field(path_str, current_item, module_id, extra_fragment)
557+
} else {
558+
Err(ResolutionFailure::NoAssocItem(ty_res, item_name).into())
559+
}
560+
})
590561
}
591562

592563
/// Used for reporting better errors.
@@ -599,7 +570,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
599570
&self,
600571
ns: Namespace,
601572
path_str: &str,
602-
base_node: Option<DefId>,
573+
module_id: DefId,
603574
current_item: &Option<String>,
604575
extra_fragment: &Option<String>,
605576
) -> Option<Res> {
@@ -616,11 +587,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
616587
};
617588
// cannot be used for macro namespace
618589
let check_full_res = |this: &Self, ns| {
619-
let result = this.resolve(path_str, ns, current_item, base_node, extra_fragment);
590+
let result = this.resolve(path_str, ns, current_item, module_id, extra_fragment);
620591
check_full_res_inner(this, result.map(|(res, _)| res))
621592
};
622593
let check_full_res_macro = |this: &Self| {
623-
let result = this.macro_resolve(path_str, base_node);
594+
let result = this.macro_resolve(path_str, module_id);
624595
check_full_res_inner(this, result.map_err(ErrorKind::from))
625596
};
626597
match ns {
@@ -843,7 +814,6 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
843814
self.mod_ids.push(item.def_id);
844815
}
845816

846-
let cx = self.cx;
847817
let dox = item.attrs.collapsed_doc_value().unwrap_or_else(String::new);
848818
trace!("got documentation '{}'", dox);
849819

@@ -885,377 +855,439 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
885855
});
886856

887857
for (ori_link, link_range) in markdown_links(&dox) {
888-
trace!("considering link '{}'", ori_link);
858+
self.resolve_link(
859+
&mut item,
860+
&dox,
861+
&current_item,
862+
parent_node,
863+
&parent_name,
864+
ori_link,
865+
link_range,
866+
);
867+
}
868+
869+
if item.is_mod() && !item.attrs.inner_docs {
870+
self.mod_ids.push(item.def_id);
871+
}
872+
873+
if item.is_mod() {
874+
let ret = self.fold_item_recur(item);
875+
876+
self.mod_ids.pop();
889877

890-
// Bail early for real links.
891-
if ori_link.contains('/') {
892-
continue;
878+
ret
879+
} else {
880+
self.fold_item_recur(item)
881+
}
882+
}
883+
}
884+
885+
impl LinkCollector<'_, '_> {
886+
fn resolve_link(
887+
&self,
888+
item: &mut Item,
889+
dox: &str,
890+
current_item: &Option<String>,
891+
parent_node: Option<DefId>,
892+
parent_name: &Option<String>,
893+
ori_link: String,
894+
link_range: Option<Range<usize>>,
895+
) {
896+
trace!("considering link '{}'", ori_link);
897+
898+
// Bail early for real links.
899+
if ori_link.contains('/') {
900+
return;
901+
}
902+
903+
// [] is mostly likely not supposed to be a link
904+
if ori_link.is_empty() {
905+
return;
906+
}
907+
908+
let cx = self.cx;
909+
let link = ori_link.replace("`", "");
910+
let parts = link.split('#').collect::<Vec<_>>();
911+
let (link, extra_fragment) = if parts.len() > 2 {
912+
anchor_failure(cx, &item, &link, dox, link_range, AnchorFailure::MultipleAnchors);
913+
return;
914+
} else if parts.len() == 2 {
915+
if parts[0].trim().is_empty() {
916+
// This is an anchor to an element of the current page, nothing to do in here!
917+
return;
893918
}
919+
(parts[0], Some(parts[1].to_owned()))
920+
} else {
921+
(parts[0], None)
922+
};
923+
let resolved_self;
924+
let link_text;
925+
let mut path_str;
926+
let disambiguator;
927+
let (mut res, mut fragment) = {
928+
path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
929+
disambiguator = Some(d);
930+
path
931+
} else {
932+
disambiguator = None;
933+
&link
934+
}
935+
.trim();
894936

895-
// [] is mostly likely not supposed to be a link
896-
if ori_link.is_empty() {
897-
continue;
937+
if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
938+
return;
898939
}
899940

900-
let link = ori_link.replace("`", "");
901-
let parts = link.split('#').collect::<Vec<_>>();
902-
let (link, extra_fragment) = if parts.len() > 2 {
903-
anchor_failure(cx, &item, &link, &dox, link_range, AnchorFailure::MultipleAnchors);
904-
continue;
905-
} else if parts.len() == 2 {
906-
if parts[0].trim().is_empty() {
907-
// This is an anchor to an element of the current page, nothing to do in here!
908-
continue;
909-
}
910-
(parts[0], Some(parts[1].to_owned()))
941+
// We stripped `()` and `!` when parsing the disambiguator.
942+
// Add them back to be displayed, but not prefix disambiguators.
943+
link_text = disambiguator
944+
.map(|d| d.display_for(path_str))
945+
.unwrap_or_else(|| path_str.to_owned());
946+
947+
// In order to correctly resolve intra-doc-links we need to
948+
// pick a base AST node to work from. If the documentation for
949+
// this module came from an inner comment (//!) then we anchor
950+
// our name resolution *inside* the module. If, on the other
951+
// hand it was an outer comment (///) then we anchor the name
952+
// resolution in the parent module on the basis that the names
953+
// used are more likely to be intended to be parent names. For
954+
// this, we set base_node to None for inner comments since
955+
// we've already pushed this node onto the resolution stack but
956+
// for outer comments we explicitly try and resolve against the
957+
// parent_node first.
958+
let base_node = if item.is_mod() && item.attrs.inner_docs {
959+
self.mod_ids.last().copied()
911960
} else {
912-
(parts[0], None)
961+
parent_node
913962
};
914-
let resolved_self;
915-
let link_text;
916-
let mut path_str;
917-
let disambiguator;
918-
let (mut res, mut fragment) = {
919-
path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
920-
disambiguator = Some(d);
921-
path
922-
} else {
923-
disambiguator = None;
924-
&link
925-
}
926-
.trim();
927963

928-
if path_str.contains(|ch: char| !(ch.is_alphanumeric() || ch == ':' || ch == '_')) {
929-
continue;
964+
let module_id = if let Some(id) = base_node {
965+
id
966+
} else {
967+
debug!("attempting to resolve item without parent module: {}", path_str);
968+
let err_kind = ResolutionFailure::NoParentItem.into();
969+
resolution_failure(
970+
self,
971+
&item,
972+
path_str,
973+
disambiguator,
974+
dox,
975+
link_range,
976+
smallvec![err_kind],
977+
);
978+
return;
979+
};
980+
981+
// replace `Self` with suitable item's parent name
982+
if path_str.starts_with("Self::") {
983+
if let Some(ref name) = parent_name {
984+
resolved_self = format!("{}::{}", name, &path_str[6..]);
985+
path_str = &resolved_self;
930986
}
987+
}
931988

932-
// We stripped `()` and `!` when parsing the disambiguator.
933-
// Add them back to be displayed, but not prefix disambiguators.
934-
link_text = disambiguator
935-
.map(|d| d.display_for(path_str))
936-
.unwrap_or_else(|| path_str.to_owned());
937-
938-
// In order to correctly resolve intra-doc-links we need to
939-
// pick a base AST node to work from. If the documentation for
940-
// this module came from an inner comment (//!) then we anchor
941-
// our name resolution *inside* the module. If, on the other
942-
// hand it was an outer comment (///) then we anchor the name
943-
// resolution in the parent module on the basis that the names
944-
// used are more likely to be intended to be parent names. For
945-
// this, we set base_node to None for inner comments since
946-
// we've already pushed this node onto the resolution stack but
947-
// for outer comments we explicitly try and resolve against the
948-
// parent_node first.
949-
let base_node = if item.is_mod() && item.attrs.inner_docs {
950-
self.mod_ids.last().copied()
951-
} else {
952-
parent_node
953-
};
989+
match self.resolve_with_disambiguator(
990+
disambiguator,
991+
item,
992+
dox,
993+
path_str,
994+
current_item,
995+
module_id,
996+
extra_fragment,
997+
&ori_link,
998+
link_range.clone(),
999+
) {
1000+
Some(x) => x,
1001+
None => return,
1002+
}
1003+
};
9541004

955-
// replace `Self` with suitable item's parent name
956-
if path_str.starts_with("Self::") {
957-
if let Some(ref name) = parent_name {
958-
resolved_self = format!("{}::{}", name, &path_str[6..]);
959-
path_str = &resolved_self;
1005+
// Check for a primitive which might conflict with a module
1006+
// Report the ambiguity and require that the user specify which one they meant.
1007+
// FIXME: could there ever be a primitive not in the type namespace?
1008+
if matches!(
1009+
disambiguator,
1010+
None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
1011+
) && !matches!(res, Res::PrimTy(_))
1012+
{
1013+
if let Some((path, prim)) = is_primitive(path_str, TypeNS) {
1014+
// `prim@char`
1015+
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1016+
if fragment.is_some() {
1017+
anchor_failure(
1018+
cx,
1019+
&item,
1020+
path_str,
1021+
dox,
1022+
link_range,
1023+
AnchorFailure::RustdocAnchorConflict(prim),
1024+
);
1025+
return;
9601026
}
1027+
res = prim;
1028+
fragment = Some(path.to_owned());
1029+
} else {
1030+
// `[char]` when a `char` module is in scope
1031+
let candidates = vec![res, prim];
1032+
ambiguity_error(cx, &item, path_str, dox, link_range, candidates);
1033+
return;
9611034
}
1035+
}
1036+
}
9621037

963-
match disambiguator.map(Disambiguator::ns) {
964-
Some(ns @ (ValueNS | TypeNS)) => {
965-
match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment)
966-
{
967-
Ok(res) => res,
968-
Err(ErrorKind::Resolve(box mut kind)) => {
969-
// We only looked in one namespace. Try to give a better error if possible.
970-
if kind.full_res().is_none() {
971-
let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
972-
for &new_ns in &[other_ns, MacroNS] {
973-
if let Some(res) = self.check_full_res(
974-
new_ns,
975-
path_str,
976-
base_node,
977-
&current_item,
978-
&extra_fragment,
979-
) {
980-
kind = ResolutionFailure::WrongNamespace(res, ns);
981-
break;
982-
}
983-
}
984-
}
985-
resolution_failure(
986-
self,
987-
&item,
988-
path_str,
989-
disambiguator,
990-
&dox,
991-
link_range,
992-
smallvec![kind],
993-
);
994-
// This could just be a normal link or a broken link
995-
// we could potentially check if something is
996-
// "intra-doc-link-like" and warn in that case.
997-
continue;
998-
}
999-
Err(ErrorKind::AnchorFailure(msg)) => {
1000-
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
1001-
continue;
1002-
}
1003-
}
1038+
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
1039+
// The resolved item did not match the disambiguator; give a better error than 'not found'
1040+
let msg = format!("incompatible link kind for `{}`", path_str);
1041+
report_diagnostic(cx, &msg, &item, dox, &link_range, |diag, sp| {
1042+
let note = format!(
1043+
"this link resolved to {} {}, which is not {} {}",
1044+
resolved.article(),
1045+
resolved.descr(),
1046+
specified.article(),
1047+
specified.descr()
1048+
);
1049+
diag.note(&note);
1050+
suggest_disambiguator(resolved, diag, path_str, dox, sp, &link_range);
1051+
});
1052+
};
1053+
if let Res::PrimTy(_) = res {
1054+
match disambiguator {
1055+
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
1056+
item.attrs.links.push(ItemLink {
1057+
link: ori_link,
1058+
link_text: path_str.to_owned(),
1059+
did: None,
1060+
fragment,
1061+
});
1062+
}
1063+
Some(other) => {
1064+
report_mismatch(other, Disambiguator::Primitive);
1065+
return;
1066+
}
1067+
}
1068+
} else {
1069+
debug!("intra-doc link to {} resolved to {:?}", path_str, res);
1070+
1071+
// Disallow e.g. linking to enums with `struct@`
1072+
if let Res::Def(kind, _) = res {
1073+
debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
1074+
match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
1075+
| (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
1076+
// NOTE: this allows 'method' to mean both normal functions and associated functions
1077+
// This can't cause ambiguity because both are in the same namespace.
1078+
| (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
1079+
// These are namespaces; allow anything in the namespace to match
1080+
| (_, Some(Disambiguator::Namespace(_)))
1081+
// If no disambiguator given, allow anything
1082+
| (_, None)
1083+
// All of these are valid, so do nothing
1084+
=> {}
1085+
(actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
1086+
(_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
1087+
report_mismatch(specified, Disambiguator::Kind(kind));
1088+
return;
10041089
}
1005-
None => {
1006-
// Try everything!
1007-
let mut candidates = PerNS {
1008-
macro_ns: self
1009-
.macro_resolve(path_str, base_node)
1010-
.map(|res| (res, extra_fragment.clone())),
1011-
type_ns: match self.resolve(
1012-
path_str,
1013-
TypeNS,
1014-
&current_item,
1015-
base_node,
1016-
&extra_fragment,
1017-
) {
1018-
Ok(res) => {
1019-
debug!("got res in TypeNS: {:?}", res);
1020-
Ok(res)
1021-
}
1022-
Err(ErrorKind::AnchorFailure(msg)) => {
1023-
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
1024-
continue;
1025-
}
1026-
Err(ErrorKind::Resolve(box kind)) => Err(kind),
1027-
},
1028-
value_ns: match self.resolve(
1029-
path_str,
1030-
ValueNS,
1031-
&current_item,
1032-
base_node,
1033-
&extra_fragment,
1034-
) {
1035-
Ok(res) => Ok(res),
1036-
Err(ErrorKind::AnchorFailure(msg)) => {
1037-
anchor_failure(cx, &item, &ori_link, &dox, link_range, msg);
1038-
continue;
1039-
}
1040-
Err(ErrorKind::Resolve(box kind)) => Err(kind),
1041-
}
1042-
.and_then(|(res, fragment)| {
1043-
// Constructors are picked up in the type namespace.
1044-
match res {
1045-
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
1046-
Err(ResolutionFailure::WrongNamespace(res, TypeNS))
1047-
}
1048-
_ => match (fragment, extra_fragment) {
1049-
(Some(fragment), Some(_)) => {
1050-
// Shouldn't happen but who knows?
1051-
Ok((res, Some(fragment)))
1052-
}
1053-
(fragment, None) | (None, fragment) => Ok((res, fragment)),
1054-
},
1055-
}
1056-
}),
1057-
};
1090+
}
1091+
}
10581092

1059-
let len = candidates.iter().filter(|res| res.is_ok()).count();
1093+
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
1094+
if let Some((src_id, dst_id)) = res
1095+
.opt_def_id()
1096+
.and_then(|def_id| def_id.as_local())
1097+
.and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
1098+
{
1099+
use rustc_hir::def_id::LOCAL_CRATE;
10601100

1061-
if len == 0 {
1062-
resolution_failure(
1063-
self,
1064-
&item,
1065-
path_str,
1066-
disambiguator,
1067-
&dox,
1068-
link_range,
1069-
candidates.into_iter().filter_map(|res| res.err()).collect(),
1070-
);
1071-
// this could just be a normal link
1072-
continue;
1073-
}
1101+
let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
1102+
let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
10741103

1075-
if len == 1 {
1076-
candidates.into_iter().filter_map(|res| res.ok()).next().unwrap()
1077-
} else if len == 2 && is_derive_trait_collision(&candidates) {
1078-
candidates.type_ns.unwrap()
1079-
} else {
1080-
if is_derive_trait_collision(&candidates) {
1081-
candidates.macro_ns = Err(ResolutionFailure::Dummy);
1082-
}
1083-
// If we're reporting an ambiguity, don't mention the namespaces that failed
1084-
let candidates =
1085-
candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
1086-
ambiguity_error(
1087-
cx,
1088-
&item,
1089-
path_str,
1090-
&dox,
1091-
link_range,
1092-
candidates.present_items().collect(),
1093-
);
1094-
continue;
1095-
}
1096-
}
1097-
Some(MacroNS) => {
1098-
match self.macro_resolve(path_str, base_node) {
1099-
Ok(res) => (res, extra_fragment),
1100-
Err(mut kind) => {
1101-
// `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
1102-
for &ns in &[TypeNS, ValueNS] {
1103-
if let Some(res) = self.check_full_res(
1104-
ns,
1105-
path_str,
1106-
base_node,
1107-
&current_item,
1108-
&extra_fragment,
1109-
) {
1110-
kind = ResolutionFailure::WrongNamespace(res, MacroNS);
1111-
break;
1112-
}
1113-
}
1114-
resolution_failure(
1115-
self,
1116-
&item,
1104+
if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
1105+
&& !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
1106+
{
1107+
privacy_error(cx, &item, &path_str, dox, link_range);
1108+
return;
1109+
}
1110+
}
1111+
let id = register_res(cx, res);
1112+
item.attrs.links.push(ItemLink { link: ori_link, link_text, did: Some(id), fragment });
1113+
}
1114+
}
1115+
1116+
fn resolve_with_disambiguator(
1117+
&self,
1118+
disambiguator: Option<Disambiguator>,
1119+
item: &mut Item,
1120+
dox: &str,
1121+
path_str: &str,
1122+
current_item: &Option<String>,
1123+
base_node: DefId,
1124+
extra_fragment: Option<String>,
1125+
ori_link: &str,
1126+
link_range: Option<Range<usize>>,
1127+
) -> Option<(Res, Option<String>)> {
1128+
match disambiguator.map(Disambiguator::ns) {
1129+
Some(ns @ (ValueNS | TypeNS)) => {
1130+
match self.resolve(path_str, ns, &current_item, base_node, &extra_fragment) {
1131+
Ok(res) => Some(res),
1132+
Err(ErrorKind::Resolve(box mut kind)) => {
1133+
// We only looked in one namespace. Try to give a better error if possible.
1134+
if kind.full_res().is_none() {
1135+
let other_ns = if ns == ValueNS { TypeNS } else { ValueNS };
1136+
for &new_ns in &[other_ns, MacroNS] {
1137+
if let Some(res) = self.check_full_res(
1138+
new_ns,
11171139
path_str,
1118-
disambiguator,
1119-
&dox,
1120-
link_range,
1121-
smallvec![kind],
1122-
);
1123-
continue;
1140+
base_node,
1141+
&current_item,
1142+
&extra_fragment,
1143+
) {
1144+
kind = ResolutionFailure::WrongNamespace(res, ns);
1145+
break;
1146+
}
11241147
}
11251148
}
1149+
resolution_failure(
1150+
self,
1151+
&item,
1152+
path_str,
1153+
disambiguator,
1154+
dox,
1155+
link_range,
1156+
smallvec![kind],
1157+
);
1158+
// This could just be a normal link or a broken link
1159+
// we could potentially check if something is
1160+
// "intra-doc-link-like" and warn in that case.
1161+
return None;
11261162
}
1127-
}
1128-
};
1129-
1130-
// Check for a primitive which might conflict with a module
1131-
// Report the ambiguity and require that the user specify which one they meant.
1132-
// FIXME: could there ever be a primitive not in the type namespace?
1133-
if matches!(
1134-
disambiguator,
1135-
None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
1136-
) && !matches!(res, Res::PrimTy(_))
1137-
{
1138-
if let Some((path, prim)) = is_primitive(path_str, TypeNS) {
1139-
// `prim@char`
1140-
if matches!(disambiguator, Some(Disambiguator::Primitive)) {
1141-
if fragment.is_some() {
1142-
anchor_failure(
1143-
cx,
1144-
&item,
1145-
path_str,
1146-
&dox,
1147-
link_range,
1148-
AnchorFailure::RustdocAnchorConflict(prim),
1149-
);
1150-
continue;
1151-
}
1152-
res = prim;
1153-
fragment = Some(path.to_owned());
1154-
} else {
1155-
// `[char]` when a `char` module is in scope
1156-
let candidates = vec![res, prim];
1157-
ambiguity_error(cx, &item, path_str, &dox, link_range, candidates);
1158-
continue;
1163+
Err(ErrorKind::AnchorFailure(msg)) => {
1164+
anchor_failure(self.cx, &item, &ori_link, dox, link_range, msg);
1165+
return None;
11591166
}
11601167
}
11611168
}
1169+
None => {
1170+
// Try everything!
1171+
let mut candidates = PerNS {
1172+
macro_ns: self
1173+
.macro_resolve(path_str, base_node)
1174+
.map(|res| (res, extra_fragment.clone())),
1175+
type_ns: match self.resolve(
1176+
path_str,
1177+
TypeNS,
1178+
&current_item,
1179+
base_node,
1180+
&extra_fragment,
1181+
) {
1182+
Ok(res) => {
1183+
debug!("got res in TypeNS: {:?}", res);
1184+
Ok(res)
1185+
}
1186+
Err(ErrorKind::AnchorFailure(msg)) => {
1187+
anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1188+
return None;
1189+
}
1190+
Err(ErrorKind::Resolve(box kind)) => Err(kind),
1191+
},
1192+
value_ns: match self.resolve(
1193+
path_str,
1194+
ValueNS,
1195+
&current_item,
1196+
base_node,
1197+
&extra_fragment,
1198+
) {
1199+
Ok(res) => Ok(res),
1200+
Err(ErrorKind::AnchorFailure(msg)) => {
1201+
anchor_failure(self.cx, &item, ori_link, dox, link_range, msg);
1202+
return None;
1203+
}
1204+
Err(ErrorKind::Resolve(box kind)) => Err(kind),
1205+
}
1206+
.and_then(|(res, fragment)| {
1207+
// Constructors are picked up in the type namespace.
1208+
match res {
1209+
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) => {
1210+
Err(ResolutionFailure::WrongNamespace(res, TypeNS))
1211+
}
1212+
_ => match (fragment, extra_fragment) {
1213+
(Some(fragment), Some(_)) => {
1214+
// Shouldn't happen but who knows?
1215+
Ok((res, Some(fragment)))
1216+
}
1217+
(fragment, None) | (None, fragment) => Ok((res, fragment)),
1218+
},
1219+
}
1220+
}),
1221+
};
11621222

1163-
let report_mismatch = |specified: Disambiguator, resolved: Disambiguator| {
1164-
// The resolved item did not match the disambiguator; give a better error than 'not found'
1165-
let msg = format!("incompatible link kind for `{}`", path_str);
1166-
report_diagnostic(cx, &msg, &item, &dox, &link_range, |diag, sp| {
1167-
let note = format!(
1168-
"this link resolved to {} {}, which is not {} {}",
1169-
resolved.article(),
1170-
resolved.descr(),
1171-
specified.article(),
1172-
specified.descr()
1223+
let len = candidates.iter().filter(|res| res.is_ok()).count();
1224+
1225+
if len == 0 {
1226+
resolution_failure(
1227+
self,
1228+
&item,
1229+
path_str,
1230+
disambiguator,
1231+
dox,
1232+
link_range,
1233+
candidates.into_iter().filter_map(|res| res.err()).collect(),
11731234
);
1174-
diag.note(&note);
1175-
suggest_disambiguator(resolved, diag, path_str, &dox, sp, &link_range);
1176-
});
1177-
};
1178-
if let Res::PrimTy(_) = res {
1179-
match disambiguator {
1180-
Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {
1181-
item.attrs.links.push(ItemLink {
1182-
link: ori_link,
1183-
link_text: path_str.to_owned(),
1184-
did: None,
1185-
fragment,
1186-
});
1187-
}
1188-
Some(other) => {
1189-
report_mismatch(other, Disambiguator::Primitive);
1190-
continue;
1191-
}
1235+
// this could just be a normal link
1236+
return None;
11921237
}
1193-
} else {
1194-
debug!("intra-doc link to {} resolved to {:?}", path_str, res);
1195-
1196-
// Disallow e.g. linking to enums with `struct@`
1197-
if let Res::Def(kind, _) = res {
1198-
debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
1199-
match (self.kind_side_channel.take().map(|(kind, _)| kind).unwrap_or(kind), disambiguator) {
1200-
| (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
1201-
// NOTE: this allows 'method' to mean both normal functions and associated functions
1202-
// This can't cause ambiguity because both are in the same namespace.
1203-
| (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
1204-
// These are namespaces; allow anything in the namespace to match
1205-
| (_, Some(Disambiguator::Namespace(_)))
1206-
// If no disambiguator given, allow anything
1207-
| (_, None)
1208-
// All of these are valid, so do nothing
1209-
=> {}
1210-
(actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
1211-
(_, Some(specified @ Disambiguator::Kind(_) | specified @ Disambiguator::Primitive)) => {
1212-
report_mismatch(specified, Disambiguator::Kind(kind));
1213-
continue;
1214-
}
1238+
1239+
if len == 1 {
1240+
Some(candidates.into_iter().filter_map(|res| res.ok()).next().unwrap())
1241+
} else if len == 2 && is_derive_trait_collision(&candidates) {
1242+
Some(candidates.type_ns.unwrap())
1243+
} else {
1244+
if is_derive_trait_collision(&candidates) {
1245+
candidates.macro_ns = Err(ResolutionFailure::Dummy);
12151246
}
1247+
// If we're reporting an ambiguity, don't mention the namespaces that failed
1248+
let candidates = candidates.map(|candidate| candidate.ok().map(|(res, _)| res));
1249+
ambiguity_error(
1250+
self.cx,
1251+
&item,
1252+
path_str,
1253+
dox,
1254+
link_range,
1255+
candidates.present_items().collect(),
1256+
);
1257+
return None;
12161258
}
1217-
1218-
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
1219-
if let Some((src_id, dst_id)) = res
1220-
.opt_def_id()
1221-
.and_then(|def_id| def_id.as_local())
1222-
.and_then(|dst_id| item.def_id.as_local().map(|src_id| (src_id, dst_id)))
1223-
{
1224-
use rustc_hir::def_id::LOCAL_CRATE;
1225-
1226-
let hir_src = self.cx.tcx.hir().local_def_id_to_hir_id(src_id);
1227-
let hir_dst = self.cx.tcx.hir().local_def_id_to_hir_id(dst_id);
1228-
1229-
if self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_src)
1230-
&& !self.cx.tcx.privacy_access_levels(LOCAL_CRATE).is_exported(hir_dst)
1231-
{
1232-
privacy_error(cx, &item, &path_str, &dox, link_range);
1233-
continue;
1259+
}
1260+
Some(MacroNS) => {
1261+
match self.macro_resolve(path_str, base_node) {
1262+
Ok(res) => Some((res, extra_fragment)),
1263+
Err(mut kind) => {
1264+
// `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
1265+
for &ns in &[TypeNS, ValueNS] {
1266+
if let Some(res) = self.check_full_res(
1267+
ns,
1268+
path_str,
1269+
base_node,
1270+
&current_item,
1271+
&extra_fragment,
1272+
) {
1273+
kind = ResolutionFailure::WrongNamespace(res, MacroNS);
1274+
break;
1275+
}
1276+
}
1277+
resolution_failure(
1278+
self,
1279+
&item,
1280+
path_str,
1281+
disambiguator,
1282+
dox,
1283+
link_range,
1284+
smallvec![kind],
1285+
);
1286+
return None;
12341287
}
12351288
}
1236-
let id = register_res(cx, res);
1237-
item.attrs.links.push(ItemLink {
1238-
link: ori_link,
1239-
link_text,
1240-
did: Some(id),
1241-
fragment,
1242-
});
12431289
}
12441290
}
1245-
1246-
if item.is_mod() && !item.attrs.inner_docs {
1247-
self.mod_ids.push(item.def_id);
1248-
}
1249-
1250-
if item.is_mod() {
1251-
let ret = self.fold_item_recur(item);
1252-
1253-
self.mod_ids.pop();
1254-
1255-
ret
1256-
} else {
1257-
self.fold_item_recur(item)
1258-
}
12591291
}
12601292
}
12611293

@@ -1536,13 +1568,9 @@ fn resolution_failure(
15361568
break;
15371569
}
15381570
};
1539-
if let Some(res) = collector.check_full_res(
1540-
TypeNS,
1541-
&current,
1542-
Some(*module_id),
1543-
&None,
1544-
&None,
1545-
) {
1571+
if let Some(res) =
1572+
collector.check_full_res(TypeNS, &current, *module_id, &None, &None)
1573+
{
15461574
failure = ResolutionFailure::NoAssocItem(res, Symbol::intern(current));
15471575
break;
15481576
}

0 commit comments

Comments
 (0)
Please sign in to comment.