Skip to content

Commit 002d3a9

Browse files
committed
Don't give misleading errors for f::A, where f is in the value namespace
1 parent f4e6ebd commit 002d3a9

File tree

3 files changed

+54
-20
lines changed

3 files changed

+54
-20
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ enum ResolutionFailure<'a> {
5757
/// This resolved, but with the wrong namespace.
5858
/// `Namespace` is the expected namespace (as opposed to the actual).
5959
WrongNamespace(Res, Namespace),
60+
/// This has a partial resolution, but is not in the TypeNS and so cannot
61+
/// have associated items or fields.
62+
CannotHaveAssociatedItems(Res, Namespace),
6063
/// `String` is the base name of the path (not necessarily the whole link)
6164
NotInScope(Cow<'a, str>),
6265
/// this is a primitive type without an impls (no associated methods)
@@ -90,7 +93,8 @@ impl ResolutionFailure<'a> {
9093
| NoPrimitiveImpl(res, _)
9194
| NotAnEnum(res)
9295
| NotAVariant(res, _)
93-
| WrongNamespace(res, _) => Some(*res),
96+
| WrongNamespace(res, _)
97+
| CannotHaveAssociatedItems(res, _) => Some(*res),
9498
NotInScope(_) | NoParentItem | Dummy => None,
9599
}
96100
}
@@ -360,21 +364,39 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
360364
}));
361365
}
362366

363-
let (_, ty_res) = cx
367+
let ty_res = cx
364368
.enter_resolver(|resolver| {
365369
// only types can have associated items
366370
resolver.resolve_str_path_error(DUMMY_SP, &path_root, TypeNS, module_id)
367371
})
368-
.map_err(|_| {
369-
ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.clone().into()))
370-
})?;
371-
if let Res::Err = ty_res {
372-
return if ns == Namespace::ValueNS {
373-
self.variant_field(path_str, current_item, module_id)
374-
} else {
375-
Err(ErrorKind::Resolve(ResolutionFailure::NotInScope(path_root.into())))
376-
};
377-
}
372+
.map(|(_, res)| res);
373+
let ty_res = match ty_res {
374+
Err(()) | Ok(Res::Err) => {
375+
return if ns == Namespace::ValueNS {
376+
self.variant_field(path_str, current_item, module_id)
377+
} else {
378+
// See if it only broke because of the namespace.
379+
let kind = cx.enter_resolver(|resolver| {
380+
for &ns in &[MacroNS, ValueNS] {
381+
match resolver
382+
.resolve_str_path_error(DUMMY_SP, &path_root, ns, module_id)
383+
{
384+
Ok((_, Res::Err)) | Err(()) => {}
385+
Ok((_, res)) => {
386+
let res = res.map_id(|_| panic!("unexpected node_id"));
387+
return ResolutionFailure::CannotHaveAssociatedItems(
388+
res, ns,
389+
);
390+
}
391+
}
392+
}
393+
ResolutionFailure::NotInScope(path_root.into())
394+
});
395+
Err(ErrorKind::Resolve(kind))
396+
};
397+
}
398+
Ok(res) => res,
399+
};
378400
let ty_res = ty_res.map_id(|_| panic!("unexpected node_id"));
379401
let res = match ty_res {
380402
Res::Def(
@@ -1006,14 +1028,12 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
10061028
Ok(res) => (res, extra_fragment),
10071029
Err(mut kind) => {
10081030
// `macro_resolve` only looks in the macro namespace. Try to give a better error if possible.
1009-
//if kind.res().is_none() {
10101031
for &ns in &[TypeNS, ValueNS] {
10111032
if let Some(res) = check_full_res(self, ns) {
10121033
kind = ResolutionFailure::WrongNamespace(res, MacroNS);
10131034
break;
10141035
}
10151036
}
1016-
//}
10171037
resolution_failure(
10181038
cx,
10191039
&item,
@@ -1456,6 +1476,20 @@ fn resolution_failure(
14561476
diag.note(&note);
14571477
}
14581478
}
1479+
ResolutionFailure::CannotHaveAssociatedItems(res, _) => {
1480+
let (item, _) = item(res);
1481+
diag.note(&format!("this link partially resolves to {}", item));
1482+
if let Res::Def(kind, def_id) = res {
1483+
let name = cx.tcx.item_name(def_id);
1484+
let note = format!(
1485+
"`{}` is {} {}, not a module or type, and cannot have associated items",
1486+
name,
1487+
kind.article(),
1488+
kind.descr(def_id)
1489+
);
1490+
diag.note(&note);
1491+
}
1492+
}
14591493
// TODO: is there ever a case where this happens?
14601494
ResolutionFailure::NotAnEnum(res) => {
14611495
let (item, comma) = item(res);

src/test/rustdoc-ui/intra-link-errors.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
// FIXME: this could say which path was the first to not be found (in this case, `path`)
88
/// [path::to::nonexistent::module]
99
//~^ ERROR unresolved link
10-
//~| NOTE no item named `path::to::nonexistent` is in scope
10+
//~| NOTE no item named `path::to` is in scope
1111
//~| HELP to escape
1212

1313
// TODO: why does this say `f` and not `f::A`??
1414
/// [f::A]
1515
//~^ ERROR unresolved link
16-
//~| NOTE no item named `f` is in scope
17-
//~| HELP to escape
16+
//~| NOTE this link partially resolves
17+
//~| NOTE `f` is a function, not a module
1818

1919
/// [S::A]
2020
//~^ ERROR unresolved link

src/test/rustdoc-ui/intra-link-errors.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ note: the lint level is defined here
99
|
1010
LL | #![deny(broken_intra_doc_links)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^
12-
= note: no item named `path::to::nonexistent` is in scope
12+
= note: no item named `path::to` is in scope
1313
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
1414

1515
error: unresolved link to `f::A`
@@ -18,8 +18,8 @@ error: unresolved link to `f::A`
1818
LL | /// [f::A]
1919
| ^^^^
2020
|
21-
= note: no item named `f` is in scope
22-
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
21+
= note: this link partially resolves to the function `f`
22+
= note: `f` is a function, not a module or type, and cannot have associated items
2323

2424
error: unresolved link to `S::A`
2525
--> $DIR/intra-link-errors.rs:19:6

0 commit comments

Comments
 (0)