Skip to content

Commit d3846aa

Browse files
Fix invalid suggestions on ambiguous intra doc links
1 parent 8c0f83d commit d3846aa

File tree

1 file changed

+68
-4
lines changed

1 file changed

+68
-4
lines changed

src/librustdoc/passes/collect_intra_doc_links.rs

+68-4
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ impl LinkCollector<'_, '_> {
10181018
} else {
10191019
// `[char]` when a `char` module is in scope
10201020
let candidates = vec![res, prim];
1021-
ambiguity_error(self.cx, diag_info, path_str, candidates);
1021+
ambiguity_error(self.cx, diag_info, path_str, candidates, module_id);
10221022
return None;
10231023
}
10241024
}
@@ -1304,7 +1304,13 @@ impl LinkCollector<'_, '_> {
13041304
if ignore_macro {
13051305
candidates.macro_ns = None;
13061306
}
1307-
ambiguity_error(self.cx, diag, path_str, candidates.present_items().collect());
1307+
ambiguity_error(
1308+
self.cx,
1309+
diag,
1310+
path_str,
1311+
candidates.present_items().collect(),
1312+
base_node,
1313+
);
13081314
None
13091315
}
13101316
}
@@ -1888,15 +1894,73 @@ fn report_malformed_generics(
18881894
);
18891895
}
18901896

1897+
fn get_matching_items(
1898+
cx: &DocContext<'_>,
1899+
item_name: &str,
1900+
candidates: &mut Vec<Res>,
1901+
def_id: DefId,
1902+
) {
1903+
let assoc_items = cx.tcx.associated_items(def_id);
1904+
for assoc_item in assoc_items.in_definition_order() {
1905+
if assoc_item.name.as_str() == item_name {
1906+
candidates.push(Res::Def(assoc_item.kind.as_def_kind(), assoc_item.def_id));
1907+
}
1908+
}
1909+
}
1910+
18911911
/// Report an ambiguity error, where there were multiple possible resolutions.
18921912
fn ambiguity_error(
1893-
cx: &DocContext<'_>,
1913+
cx: &mut DocContext<'_>,
18941914
diag_info: DiagnosticInfo<'_>,
18951915
path_str: &str,
1896-
candidates: Vec<Res>,
1916+
mut candidates: Vec<Res>,
1917+
module_id: DefId,
18971918
) {
18981919
let mut msg = format!("`{}` is ", path_str);
1920+
let mut def_id_counts = FxHashMap::default();
1921+
let mut need_dedup = false;
1922+
1923+
// If the item is an impl block or a trait, we need to disambiguate even further, otherwise
1924+
// `Res` will always tell it's an impl/trait.
1925+
for candidate in &candidates {
1926+
if let Some(def_id) = candidate.def_id(cx.tcx) {
1927+
def_id_counts
1928+
.entry(def_id)
1929+
.and_modify(|(count, _)| {
1930+
*count += 1;
1931+
need_dedup = true;
1932+
})
1933+
.or_insert((1, *candidate));
1934+
}
1935+
}
1936+
if need_dedup && let Some(item_name) = path_str.split("::").last() {
1937+
candidates.clear();
1938+
for (def_id, (count, candidate)) in def_id_counts.into_iter() {
1939+
if count > 1 {
1940+
if matches!(cx.tcx.def_kind(def_id), DefKind::Trait | DefKind::Impl { .. }) {
1941+
// If it's a trait or an impl block, we can directly get the items from it.
1942+
get_matching_items(cx, item_name, &mut candidates, def_id);
1943+
} else {
1944+
// We go through the type's impl blocks.
1945+
for impl_ in cx.tcx.inherent_impls(def_id) {
1946+
get_matching_items(cx, item_name, &mut candidates, *impl_);
1947+
}
18991948

1949+
// We now go through trait impls.
1950+
let trait_impls = trait_impls_for(
1951+
cx,
1952+
cx.tcx.type_of(def_id).subst_identity(),
1953+
module_id,
1954+
);
1955+
for (impl_, _) in trait_impls {
1956+
get_matching_items(cx, item_name, &mut candidates, impl_);
1957+
}
1958+
}
1959+
} else {
1960+
candidates.push(candidate);
1961+
}
1962+
}
1963+
}
19001964
match candidates.as_slice() {
19011965
[first_def, second_def] => {
19021966
msg += &format!(

0 commit comments

Comments
 (0)