Auto merge of #145898 - lolbinarycat:rustdoc-search-trait-parent, r=GuillaumeGomez,notriddle

If a trait item appears in rustdoc search, hide the corrosponding impl items

fixes rust-lang/rust#138251

cc `@notriddle`
This commit is contained in:
bors
2025-10-03 08:43:43 +00:00
14 changed files with 275 additions and 96 deletions
@@ -32,7 +32,7 @@ ENV SCRIPT \
python3 ../x.py clippy ci --stage 2 && \
python3 ../x.py test --stage 1 core alloc std test proc_macro && \
python3 ../x.py test --stage 1 src/tools/compiletest && \
python3 ../x.py doc bootstrap && \
python3 ../x.py doc bootstrap --stage 1 && \
# Build both public and internal documentation.
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc compiler --stage 1 && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc library --stage 1 && \
+20 -11
View File
@@ -146,6 +146,14 @@ pub(crate) fn new(document_private: bool, document_hidden: bool) -> Self {
Cache { document_private, document_hidden, ..Cache::default() }
}
fn parent_stack_last_impl_and_trait_id(&self) -> (Option<DefId>, Option<DefId>) {
if let Some(ParentStackItem::Impl { item_id, trait_, .. }) = self.parent_stack.last() {
(item_id.as_def_id(), trait_.as_ref().map(|tr| tr.def_id()))
} else {
(None, None)
}
}
/// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was
/// in `krate` due to the data being moved into the `Cache`.
pub(crate) fn populate(cx: &mut DocContext<'_>, mut krate: clean::Crate) -> clean::Crate {
@@ -572,11 +580,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id),
_ => item_def_id,
};
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
item_id.as_def_id()
} else {
None
};
let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id();
let search_type = get_function_type_for_search(
item,
tcx,
@@ -594,12 +598,15 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
desc,
parent: parent_did,
parent_idx: None,
trait_parent,
trait_parent_idx: None,
exact_module_path: None,
impl_id,
search_type,
aliases,
deprecation,
};
cache.search_index.push(index_item);
}
@@ -608,19 +615,21 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
/// See [`Cache::orphan_impl_items`].
fn handle_orphan_impl_child(cache: &mut Cache, item: &clean::Item, parent_did: DefId) {
let impl_generics = clean_impl_generics(cache.parent_stack.last());
let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
item_id.as_def_id()
} else {
None
let (impl_id, trait_parent) = cache.parent_stack_last_impl_and_trait_id();
let orphan_item = OrphanImplItem {
parent: parent_did,
trait_parent,
item: item.clone(),
impl_generics,
impl_id,
};
let orphan_item =
OrphanImplItem { parent: parent_did, item: item.clone(), impl_generics, impl_id };
cache.orphan_impl_items.push(orphan_item);
}
pub(crate) struct OrphanImplItem {
pub(crate) parent: DefId,
pub(crate) impl_id: Option<DefId>,
pub(crate) trait_parent: Option<DefId>,
pub(crate) item: clean::Item,
pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>,
}
+2
View File
@@ -134,6 +134,8 @@ pub(crate) struct IndexItem {
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
pub(crate) parent_idx: Option<usize>,
pub(crate) trait_parent: Option<DefId>,
pub(crate) trait_parent_idx: Option<usize>,
pub(crate) exact_module_path: Option<Vec<Symbol>>,
pub(crate) impl_id: Option<DefId>,
pub(crate) search_type: Option<IndexItemFunctionType>,
+49 -29
View File
@@ -610,6 +610,7 @@ pub(crate) fn sort(self) -> SerializedSearchIndex {
module_path,
exact_module_path,
parent,
trait_parent,
deprecated,
associated_item_disambiguator,
}| EntryData {
@@ -619,6 +620,7 @@ pub(crate) fn sort(self) -> SerializedSearchIndex {
exact_module_path: exact_module_path
.and_then(|path_id| map.get(&path_id).copied()),
parent: parent.and_then(|path_id| map.get(&path_id).copied()),
trait_parent: trait_parent.and_then(|path_id| map.get(&path_id).copied()),
deprecated: *deprecated,
associated_item_disambiguator: associated_item_disambiguator.clone(),
},
@@ -900,6 +902,7 @@ struct EntryData {
module_path: Option<usize>,
exact_module_path: Option<usize>,
parent: Option<usize>,
trait_parent: Option<usize>,
deprecated: bool,
associated_item_disambiguator: Option<String>,
}
@@ -915,6 +918,7 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
seq.serialize_element(&self.module_path.map(|id| id + 1).unwrap_or(0))?;
seq.serialize_element(&self.exact_module_path.map(|id| id + 1).unwrap_or(0))?;
seq.serialize_element(&self.parent.map(|id| id + 1).unwrap_or(0))?;
seq.serialize_element(&self.trait_parent.map(|id| id + 1).unwrap_or(0))?;
seq.serialize_element(&if self.deprecated { 1 } else { 0 })?;
if let Some(disambig) = &self.associated_item_disambiguator {
seq.serialize_element(&disambig)?;
@@ -946,6 +950,9 @@ fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<EntryData, A::Erro
.ok_or_else(|| A::Error::missing_field("exact_module_path"))?;
let parent: SerializedOptional32 =
v.next_element()?.ok_or_else(|| A::Error::missing_field("parent"))?;
let trait_parent: SerializedOptional32 =
v.next_element()?.ok_or_else(|| A::Error::missing_field("trait_parent"))?;
let deprecated: u32 = v.next_element()?.unwrap_or(0);
let associated_item_disambiguator: Option<String> = v.next_element()?;
Ok(EntryData {
@@ -955,6 +962,7 @@ fn visit_seq<A: de::SeqAccess<'de>>(self, mut v: A) -> Result<EntryData, A::Erro
exact_module_path: Option::<i32>::from(exact_module_path)
.map(|path| path as usize),
parent: Option::<i32>::from(parent).map(|path| path as usize),
trait_parent: Option::<i32>::from(trait_parent).map(|path| path as usize),
deprecated: deprecated != 0,
associated_item_disambiguator,
})
@@ -1305,7 +1313,8 @@ pub(crate) fn build_index(
// Attach all orphan items to the type's definition if the type
// has since been learned.
for &OrphanImplItem { impl_id, parent, ref item, ref impl_generics } in &cache.orphan_impl_items
for &OrphanImplItem { impl_id, parent, trait_parent, ref item, ref impl_generics } in
&cache.orphan_impl_items
{
if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = short_markdown_summary(&item.doc_value(), &item.link_names(cache));
@@ -1317,6 +1326,8 @@ pub(crate) fn build_index(
desc,
parent: Some(parent),
parent_idx: None,
trait_parent,
trait_parent_idx: None,
exact_module_path: None,
impl_id,
search_type: get_function_type_for_search(
@@ -1421,6 +1432,7 @@ pub(crate) fn build_index(
module_path: None,
exact_module_path: None,
parent: None,
trait_parent: None,
deprecated: false,
associated_item_disambiguator: None,
}),
@@ -1434,39 +1446,46 @@ pub(crate) fn build_index(
}
};
// First, populate associated item parents
// First, populate associated item parents and trait parents
let crate_items: Vec<&mut IndexItem> = search_index
.iter_mut()
.map(|item| {
item.parent_idx = item.parent.and_then(|defid| {
cache.paths.get(&defid).map(|&(ref fqp, ty)| {
let pathid = serialized_index.names.len();
match serialized_index.crate_paths_index.entry((ty, fqp.clone())) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
entry.insert(pathid);
let (name, path) = fqp.split_last().unwrap();
serialized_index.push_path(
name.as_str().to_string(),
PathData {
ty,
module_path: path.to_vec(),
exact_module_path: if let Some(exact_path) =
cache.exact_paths.get(&defid)
&& let Some((name2, exact_path)) = exact_path.split_last()
&& name == name2
{
Some(exact_path.to_vec())
} else {
None
let mut defid_to_rowid = |defid, check_external: bool| {
cache
.paths
.get(&defid)
.or_else(|| check_external.then(|| cache.external_paths.get(&defid)).flatten())
.map(|&(ref fqp, ty)| {
let pathid = serialized_index.names.len();
match serialized_index.crate_paths_index.entry((ty, fqp.clone())) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
entry.insert(pathid);
let (name, path) = fqp.split_last().unwrap();
serialized_index.push_path(
name.as_str().to_string(),
PathData {
ty,
module_path: path.to_vec(),
exact_module_path: if let Some(exact_path) =
cache.exact_paths.get(&defid)
&& let Some((name2, exact_path)) =
exact_path.split_last()
&& name == name2
{
Some(exact_path.to_vec())
} else {
None
},
},
},
);
usize::try_from(pathid).unwrap()
);
usize::try_from(pathid).unwrap()
}
}
}
})
});
})
};
item.parent_idx = item.parent.and_then(|p| defid_to_rowid(p, false));
item.trait_parent_idx = item.trait_parent.and_then(|p| defid_to_rowid(p, true));
if let Some(defid) = item.defid
&& item.parent_idx.is_none()
@@ -1549,6 +1568,7 @@ pub(crate) fn build_index(
EntryData {
ty: item.ty,
parent: item.parent_idx,
trait_parent: item.trait_parent_idx,
module_path,
exact_module_path,
deprecated: item.deprecation.is_some(),
+21 -1
View File
@@ -241,6 +241,7 @@ declare namespace rustdoc {
modulePath: number?,
exactModulePath: number?,
parent: number?,
traitParent: number?,
deprecated: boolean,
associatedItemDisambiguator: string?,
}
@@ -291,9 +292,12 @@ declare namespace rustdoc {
path: PathData?,
functionData: FunctionData?,
deprecated: boolean,
parent: { path: PathData, name: string}?,
parent: RowParent,
traitParent: RowParent,
}
type RowParent = { path: PathData, name: string } | null;
type ItemType = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26;
@@ -316,7 +320,23 @@ declare namespace rustdoc {
interface ResultObject {
desc: Promise<string|null>,
displayPath: string,
/**
* path to where the item was defined (not inlined),
* then `|`, then the `ItemType` of the item.
*
* This is often a private path, so it should not be displayed,
* but this allows us to use it to reliably deduplicate reexported and inlined items
*/
fullPath: string,
/**
* The `fullPath` of the corresponding item within a trait.
* For example, for `File::read`, this would be `std::io::Read::read|12`
*
* This is used to hide items from trait impls when the trait itself is in the search results.
*
* `null` if the item is not from a trait impl block.
*/
traitPath: string | null,
href: string,
id: number,
dist: number,
+102 -19
View File
@@ -1076,6 +1076,34 @@ function isPathSeparator(c) {
return c === ":" || c === " ";
}
/**
* Given an array and an ascending list of indices,
* efficiently removes each index in the array.
*
* @template T
* @param {Array<T>} a
* @param {Array<number>} idxList
*/
function removeIdxListAsc(a, idxList) {
if (idxList.length === 0) {
return;
}
let removed = 0;
let i = idxList[0];
let nextToRemove = idxList[0];
while (i < a.length - idxList.length) {
while (i === nextToRemove && removed < idxList.length) {
removed++;
i++;
nextToRemove = idxList[removed];
}
a[i] = a[i + removed];
i++;
}
// truncate array
a.length -= idxList.length;
}
/**
* @template T
*/
@@ -1598,6 +1626,7 @@ class DocSearch {
* module_path,
* exact_module_path,
* parent,
* trait_parent,
* deprecated,
* associated_item_disambiguator
* @type {rustdoc.ArrayWithOptionals<[
@@ -1607,6 +1636,7 @@ class DocSearch {
* number,
* number,
* number,
* number,
* ], [string]>}
*/
const raw = JSON.parse(encoded);
@@ -1616,8 +1646,9 @@ class DocSearch {
modulePath: raw[2] === 0 ? null : raw[2] - 1,
exactModulePath: raw[3] === 0 ? null : raw[3] - 1,
parent: raw[4] === 0 ? null : raw[4] - 1,
deprecated: raw[5] === 1 ? true : false,
associatedItemDisambiguator: raw.length === 6 ? null : raw[6],
traitParent: raw[5] === 0 ? null : raw[5] - 1,
deprecated: raw[6] === 1 ? true : false,
associatedItemDisambiguator: raw.length === 7 ? null : raw[7],
};
}
@@ -1853,14 +1884,25 @@ class DocSearch {
if (!entry && !path) {
return null;
}
/** @type {function("parent" | "traitParent"): Promise<rustdoc.RowParent>} */
const buildParentLike = async field => {
const [name, path] = entry !== null && entry[field] !== null ?
await Promise.all([this.getName(entry[field]), this.getPathData(entry[field])]) :
[null, null];
if (name !== null && path !== null) {
return { name, path };
}
return null;
};
const [
moduleName,
modulePathData,
exactModuleName,
exactModulePathData,
parentName,
parentPath,
crate,
parent,
traitParent,
crateOrNull,
] = await Promise.all([
entry && entry.modulePath !== null ? this.getName(entry.modulePath) : null,
entry && entry.modulePath !== null ? this.getPathData(entry.modulePath) : null,
@@ -1870,14 +1912,11 @@ class DocSearch {
entry && entry.exactModulePath !== null ?
this.getPathData(entry.exactModulePath) :
null,
entry && entry.parent !== null ?
this.getName(entry.parent) :
null,
entry && entry.parent !== null ?
this.getPathData(entry.parent) :
null,
entry ? nonnull(await this.getName(entry.krate)) : "",
buildParentLike("parent"),
buildParentLike("traitParent"),
entry ? this.getName(entry.krate) : "",
]);
const crate = crateOrNull === null ? "" : crateOrNull;
const name = name_ === null ? "" : name_;
const normalizedName = (name.indexOf("_") === -1 ?
name :
@@ -1886,6 +1925,7 @@ class DocSearch {
(modulePathData.modulePath === "" ?
moduleName :
`${modulePathData.modulePath}::${moduleName}`);
return {
id,
crate,
@@ -1901,9 +1941,8 @@ class DocSearch {
path,
functionData,
deprecated: entry ? entry.deprecated : false,
parent: parentName !== null && parentPath !== null ?
{ name: parentName, path: parentPath } :
null,
parent,
traitParent,
};
}
@@ -2101,11 +2140,12 @@ class DocSearch {
/**
* @param {rustdoc.Row} item
* @returns {[string, string, string]}
* @returns {[string, string, string, string|null]}
*/
const buildHrefAndPath = item => {
let displayPath;
let href;
let traitPath = null;
const type = itemTypes[item.ty];
const name = item.name;
let path = item.modulePath;
@@ -2163,7 +2203,11 @@ class DocSearch {
href = this.rootPath + item.modulePath.replace(/::/g, "/") +
"/" + type + "." + name + ".html";
}
return [displayPath, href, `${exactPath}::${name}`];
if (item.traitParent) {
const tparent = item.traitParent;
traitPath = `${tparent.path.exactModulePath}::${tparent.name}::${name}`;
}
return [displayPath, href, `${exactPath}::${name}`, traitPath];
};
/**
@@ -2598,8 +2642,14 @@ class DocSearch {
* @returns {rustdoc.ResultObject[]}
*/
const transformResults = (results, typeInfo, duplicates) => {
/** @type {rustdoc.ResultObject[]} */
const out = [];
// if we match a trait-associated item, we want to go back and
// remove all the items that are their equivalent but in an impl block.
/** @type {Map<string, number[]>} */
const traitImplIdxMap = new Map();
for (const result of results) {
const item = result.item;
if (item.id !== -1) {
@@ -2630,17 +2680,35 @@ class DocSearch {
item,
displayPath: pathSplitter(res[0]),
fullPath: "",
traitPath: null,
href: "",
displayTypeSignature: null,
}, result);
// unlike other items, methods have a different ty when they are
// in an impl block vs a trait. want to normalize this away.
let ty = obj.item.ty;
if (ty === TY_TYMETHOD) {
ty = TY_METHOD;
}
// To be sure than it some items aren't considered as duplicate.
obj.fullPath = res[2] + "|" + obj.item.ty;
obj.fullPath = res[2] + "|" + ty;
if (res[3]) {
// "tymethod" is never used on impl blocks
// (this is the reason we need to normalize tymethod away).
obj.traitPath = res[3] + "|" + obj.item.ty;
}
if (duplicates.has(obj.fullPath)) {
continue;
}
// If we're showing something like `Iterator::next`,
// we don't want to also show a bunch of `<SomeType as Iterator>::next`
if (obj.traitPath && duplicates.has(obj.traitPath)) {
continue;
}
// Exports are specifically not shown if the items they point at
// are already in the results.
if (obj.item.ty === TY_IMPORT && duplicates.has(res[2])) {
@@ -2661,14 +2729,29 @@ class DocSearch {
);
}
// FIXME: if the trait item matches but is cut off due to MAX_RESULTS,
// this deduplication will not happen.
obj.href = res[1];
if (obj.traitPath) {
let list = traitImplIdxMap.get(obj.traitPath);
if (list === undefined) {
list = [];
}
list.push(out.length);
traitImplIdxMap.set(obj.traitPath, list);
} else {
const toRemoveList = traitImplIdxMap.get(obj.fullPath);
if (toRemoveList) {
removeIdxListAsc(out, toRemoveList);
}
traitImplIdxMap.delete(obj.fullPath);
}
out.push(obj);
if (out.length >= MAX_RESULTS) {
break;
}
}
}
return out;
};
+26 -19
View File
@@ -5,7 +5,7 @@ include: "utils.goml"
define-function: (
"check-search-color",
[
theme, count_color, desc_color, path_color, bottom_border_color, keyword_color,
theme, count_color, path_color, bottom_border_color, keyword_color,
struct_color, associatedtype_color, tymethod_color, method_color, structfield_color,
structfield_hover_color, macro_color, fn_color, hover_path_color, hover_background,
attribute_color, grey
@@ -21,10 +21,6 @@ define-function: (
{"color": |count_color|},
ALL,
)
assert-css: (
"//*[@class='desc'][normalize-space()='Just a normal struct.']",
{"color": |desc_color|},
)
assert-css: (
"//*[@class='result-name']//*[normalize-space()='test_docs::']",
{"color": |path_color|},
@@ -97,16 +93,6 @@ define-function: (
ALL,
)
// Checking color and background on hover.
move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal struct.']"
assert-css: (
"//*[@class='result-name']//*[normalize-space()='test_docs::']",
{"color": |hover_path_color|},
)
assert-css: (
"//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a",
{"color": |hover_path_color|, "background-color": |hover_background|},
)
}
)
@@ -157,7 +143,6 @@ show-text: true
call-function: ("check-search-color", {
"theme": "ayu",
"count_color": "#888",
"desc_color": "#c5c5c5",
"path_color": "#0096cf",
"bottom_border_color": "#aaa3",
"keyword_color": "#39afd7",
@@ -179,7 +164,6 @@ call-function: ("check-search-color", {
call-function: ("check-search-color", {
"theme": "dark",
"count_color": "#888",
"desc_color": "#ddd",
"path_color": "#ddd",
"bottom_border_color": "#aaa3",
"keyword_color": "#d2991d",
@@ -201,7 +185,6 @@ call-function: ("check-search-color", {
call-function: ("check-search-color", {
"theme": "light",
"count_color": "#888",
"desc_color": "#000",
"path_color": "#000",
"bottom_border_color": "#aaa3",
"keyword_color": "#3873ad",
@@ -226,12 +209,27 @@ call-function: ("perform-search", {"query": "thisisanalias"})
define-function: (
"check-alias",
[theme, alias, grey],
[theme, alias, grey, desc_color, hover_path_color, hover_background],
block {
call-function: ("switch-theme", {"theme": |theme|})
// Checking that the colors for the alias element are the ones expected.
assert-css: (".result-name .path .alias", {"color": |alias|})
assert-css: (".result-name .path .alias > .grey", {"color": |grey|})
assert-css: (
"//*[@class='desc'][normalize-space()='Just a normal enum.']",
{"color": |desc_color|},
)
// Checking color and background on hover.
move-cursor-to: "//*[@class='desc'][normalize-space()='Just a normal enum.']"
assert-css: (
"//*[@class='result-name']//*[normalize-space()='test_docs::']",
{"color": |hover_path_color|},
)
assert-css: (
"//*[@class='result-name']//*[normalize-space()='test_docs::']/ancestor::a",
{"color": |hover_path_color|, "background-color": |hover_background|},
)
},
)
@@ -239,14 +237,23 @@ call-function: ("check-alias", {
"theme": "ayu",
"alias": "#c5c5c5",
"grey": "#999",
"desc_color": "#c5c5c5",
"hover_path_color": "#fff",
"hover_background": "#3c3c3c",
})
call-function: ("check-alias", {
"theme": "dark",
"alias": "#fff",
"grey": "#ccc",
"desc_color": "#ddd",
"hover_path_color": "#ddd",
"hover_background": "#616161",
})
call-function: ("check-alias", {
"theme": "light",
"alias": "#000",
"grey": "#999",
"desc_color": "#000",
"hover_path_color": "#000",
"hover_background": "#ccc",
})
+1 -1
View File
@@ -79,7 +79,7 @@ call-function: ("check-colors", {
set-window-size: (851, 600)
// Check the size and count in tabs
assert-text: ("#search-tabs > button:nth-child(1) > .count", "(27)")
assert-text: ("#search-tabs > button:nth-child(1) > .count", "(25)")
assert-text: ("#search-tabs > button:nth-child(2) > .count", "(7)")
assert-text: ("#search-tabs > button:nth-child(3) > .count", "(0)")
store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
+2
View File
@@ -1,5 +1,7 @@
// Checks multiple things on the sidebar display (width of its elements, colors, etc).
include: "utils.goml"
// Disable animations so they don't mess up color assertions later.
emulate-media-features: { "prefers-reduced-motion": "reduce" }
go-to: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-property: (".sidebar", {"clientWidth": "199"})
show-text: true
+1 -3
View File
@@ -1,12 +1,10 @@
// ignore-order
const EXPECTED = {
'query': 'RawFd::as_raw_fd',
'query': 'method:RawFd::as_raw_fd',
'others': [
// Reproduction test for https://github.com/rust-lang/rust/issues/78724
// Validate that type alias methods get the correct path.
{ 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' },
{ 'path': 'std::os::fd::AsRawFd', 'name': 'as_raw_fd' },
{ 'path': 'std::os::fd::RawFd', 'name': 'as_raw_fd' },
],
};
+9 -9
View File
@@ -1,21 +1,21 @@
// make sure quoted search works both for items and and without generics
// ignore-order
const FILTER_CRATE = 'std';
const EXPECTED = {
'query': '"error"',
'query': '"result"',
'others': [
{ 'path': 'std', 'name': 'error' },
{ 'path': 'std::fmt', 'name': 'Error' },
{ 'path': 'std::io', 'name': 'Error' },
{ 'path': 'std', 'name': 'result' },
{ 'path': 'std::result', 'name': 'Result' },
{ 'path': 'std::fmt', 'name': 'Result' },
],
'in_args': [
{ 'path': 'std::fmt::Error', 'name': 'eq' },
{ 'path': 'std::fmt::Error', 'name': 'cmp' },
{ 'path': 'std::fmt::Error', 'name': 'partial_cmp' },
{ 'path': 'std::result::Result', 'name': 'branch' },
{ 'path': 'std::result::Result', 'name': 'ok' },
{ 'path': 'std::result::Result', 'name': 'unwrap' },
],
'returned': [
{ 'path': 'std::fmt::LowerExp', 'name': 'fmt' },
{ 'path': 'std::bool', 'name': 'try_into' },
],
};
@@ -1,10 +1,10 @@
// ignore-order
// make sure type-based searches with traits get unboxed too
const EXPECTED = [
{
'query': 'bufread -> result<[u8]>',
'query': 'any -> result<box>',
'others': [
{ 'path': 'std::boxed::Box', 'name': 'fill_buf' },
{ 'path': 'std::boxed::Box', 'name': 'downcast' },
],
},
{
+20
View File
@@ -9,4 +9,24 @@ const EXPECTED = [
{ 'path': 'trait_methods::MyTrait', 'name': 'next' },
],
},
// the traitParent deduplication pass should remove
// Empty::next, as it would be redundant
{
'query': 'next',
'correction': null,
'in_args': [],
'others': [
{ 'path': 'trait_methods::MyTrait', 'name': 'next' },
],
},
// if the trait does not match, no deduplication happens
{
'query': '-> option<()>',
'correction': null,
'in_args': [],
'others': [
{ 'path': 'trait_methods::Empty', 'name': 'next' },
{ 'path': 'trait_methods::Void', 'name': 'next' },
],
},
];
+18
View File
@@ -2,3 +2,21 @@ pub trait MyTrait {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
pub struct Empty;
impl MyTrait for Empty {
type Item = ();
fn next(&mut self) -> Option<()> {
None
}
}
pub struct Void;
impl MyTrait for Void {
type Item = ();
fn next(&mut self) -> Option<()> {
Some(())
}
}